diff --git a/documentation/site/content/quickstart/quickstart.md b/documentation/site/content/quickstart/quickstart.md index 94086fb21..4501f03da 100644 --- a/documentation/site/content/quickstart/quickstart.md +++ b/documentation/site/content/quickstart/quickstart.md @@ -24,35 +24,46 @@ The high level steps for creating an image are: and save them in a directory of your choice, for example, `/home/acmeuser/wls-installers`: `fmw_12.2.1.3.0_wls_Disk1_1of1.zip`\ - `jdk-8u202-linux-x64.tar.gz` + `jdk-8u231-linux-x64.tar.gz` 2. Use the [Cache Tool]({{< relref "/userguide/tools/cache.md" >}}) to add the installers: ```bash - $ imagetool cache addInstaller --type jdk --version 8u202 --path /home/acmeuser/wls-installers/jdk-8u202-linux-x64.tar.gz + $ imagetool cache addInstaller --type jdk --version 8u231 --path /home/acmeuser/wls-installers/jdk-8u231-linux-x64.tar.gz --architecture AMD64 ``` ```bash - $ imagetool cache addInstaller --type wls --version 12.2.1.3.0 --path /home/acmeuser/wls-installers/fmw_12.2.1.3.0_wls_Disk1_1of1.zip + $ imagetool cache addInstaller --type wls --version 12.2.1.3.0 --path /home/acmeuser/wls-installers/fmw_12.2.1.3.0_wls_Disk1_1of1.zip --architecture AMD64 ``` You can verify the cache entries by: ```bash - $ imagetool cache listItems + $ imagetool cache listInstallers ``` ```bash - Cache contents - jdk_8u202=/home/acmeuser/wls-installers/jdk-8u202-linux-x64.tar.gz - wls_12.2.1.3.0=/home/acmeuser/wls-installers/fmw_12.2.1.3.0_wls_Disk1_1of1.zip + JDK: + 8u231: + - location: /home/acmeuser/wls-installers/jdk-8u231-linux-x64.tar.gz + platform: linux/amd64 + digest: A011584A2C9378BF70C6903EF5FBF101B30B08937441DC2EC67932FB3620B2CF + dateAdded: 2024-12-30 + version: 8u231 + WLS: + 12.2.1.3.0: + - location: /home/acmeuser/wls-installers/fmw_12.2.1.3.0_wls_Disk1_1of1.zip + platform: linux/amd64 + digest: CBFD847E7F8993E199C30003BCB226BA2451911747099ADC33EA5CEA2C35E0BC + dateAdded: 2024-12-30 + version: 12.2.1.3.0 ``` 3. Run the [`imagetool create`]({{< relref "/userguide/tools/create-image.md" >}}) command: ```bash - $ imagetool create --tag wls:12.2.1.3.0 + $ imagetool create --tag wls:12.2.1.3.0 --version 12.2.1.3.0 --platform linux/amd64 ``` The final image will have the following structure: @@ -60,8 +71,8 @@ The final image will have the following structure: ```bash [oracle@c3fe8ee0167d oracle]$ ls -arlt /u01/ total 20 -drwxr-xr-x 7 oracle oracle 4096 May 28 23:40 jdk -drwxr-xr-x 11 oracle oracle 4096 May 28 23:40 oracle -drwxr-xr-x 5 oracle oracle 4096 May 28 23:40 . -drwxr-xr-x 18 root root 4096 May 29 01:31 .. +drwxr-xr-x 7 oracle oracle 4096 Jan 28 23:40 jdk +drwxr-xr-x 11 oracle oracle 4096 Jan 28 23:40 oracle +drwxr-xr-x 5 oracle oracle 4096 Jan 28 23:40 . +drwxr-xr-x 18 root root 4096 Jan 29 01:31 .. ``` diff --git a/documentation/site/content/userguide/tools/cache.md b/documentation/site/content/userguide/tools/cache.md index 62fd87170..ba31e70a3 100644 --- a/documentation/site/content/userguide/tools/cache.md +++ b/documentation/site/content/userguide/tools/cache.md @@ -1,6 +1,6 @@ --- title: "Cache" -date: 2019-02-23 +date: 2024-12-23 draft: false weight: 20 description: "The Image Tool maintains metadata on the local file system for patches and installers. You can use the cache command to manipulate the local metadata." @@ -8,9 +8,23 @@ description: "The Image Tool maintains metadata on the local file system for pat The Image Tool maintains a local file cache store. This store is used to look up where the Java, WebLogic Server installers, and WebLogic Server patches reside in the local file system. -By default, the cache store is located in the user's ```$HOME/cache``` directory. Under this directory, the lookup information is stored in the ```.metadata``` file. All automatically downloaded patches also reside in this directory. +By default, the cache store is located in the user's ```$HOME/.imagetool``` directory. Under this directory, the lookup information is stored in the ```.metadata``` file. All automatically downloaded patches also reside in this directory. -You can change the default cache store location by setting the environment variable `WLSIMG_CACHEDIR`: +In this release, there is a `settings.yaml` in the cache directory. This file controls the default settings of image tool. For example: + +```bash +defaultBuildPlatform: linux/arm64 +installerSettings: + JDK: {defaultVersion: 8u401} + WDT: {defaultVersion: latest} + WLS: {defaultVersion: 12.2.1.4.0} +patchDirectory: /home/acmeuser/.imagetool/oraclePatches +``` + +The installers and patches details are stored separately into two different files. `installers.yaml` and `patches.yaml`. By default these two files are stored in the same +directory of `settings.yaml`. + +You can change the default cache store location by setting the environment variable `WLSIMG_CACHEDIR`: ?? TODO ```bash $ export WLSIMG_CACHEDIR="/path/to/cachedir" @@ -25,36 +39,158 @@ List and set cache options | Option | Description | | --- | --- | -|`listItems`| List cache contents. | +|`listInstallers`| List cache installers. | +|`listPatches`| List cache patches. | |`addInstaller` | Add an installer to the cache. | | `addPatch` | Add a patch to the cache. | -| `addEntry` | Add a cache entry. Use with caution. | +| `deletePatch` | Delete a patch from the cache. | +| `deleteInstaller` | Delete an installer to the cache. | | `help` | Display help information for the specified command.| ### Usage scenarios -- `listItems`: Display the contents of the cache. Displays key value pairs of the installers and patches. - ``` - imagetool cache listItems - - Cache contents - jdk_8u202=/home/acmeuser/Downloads/cache/server-jre-8u202-linux-x64.tar.gz - wls_12.2.1.3.0=/home/acmeuser/Downloads/cache/fmw_12.2.1.3.0_wls_Disk1_1of1.zip - 28186730_opatch=/home/acmeuser/cache/p28186730_139400_Generic.zip - 29135930_12.2.1.3.190416=/home/acmeuser/cache/p29135930_12213190416_Generic.zip - 29135930_12.2.1.3.0=/home/acmeuser/cache/p29135930_122130_Generic.zip - cache.dir=/home/acemeuser/cache - ``` +- `listInstallers`: Display the contents of the cache. Displays key value pairs of the installers and patches. +```bash +Usage: imagetool cache listInstallers [--commonName=] + [--type=] [--version=] +List installers + --commonName= + Filter installer by common name. + --type= Filter installer type. e.g. wls, jdk, wdt + --version= Filter installer by version. + --details Show the details about the installers +e.g. + +imagetool.sh cache listInstallers + +JDK: + 17u11: + linux/arm64: + version: 17.0.11 + location: /Users/acme/Downloads/jdk-17_linux-aarch64_11bin.tar.gz + 8u401: + linux/amd64: + version: 8.0.401 + location: /Users/acme/Downloads/jdk-8u401-linux-x64.tar.gz + linux/arm64: + version: 8.0.401 + location: /Users/acme/Downloads/jdk-8u401-fcs-bin-b10-linux-aarch64-19_dec_2023.tar.gz + 8u231: + linux/amd64: + version: 8u231 + location: /Users/acme/Downloads/jdk-8u231-linux-x64.tar.gz + 11u22: + linux/amd64: + version: 11.0.22 + location: /Users/acme/Downloads/jdk-11.0.22_linux-x64_bin.tar.gz + linux/arm64: + version: 11.0.22 + location: /Users/acme/Downloads/jdk-11.0.22_linux-aarch64_bin.tar.gz +WLS: + 12.2.1.4.0: + linux/amd64: + version: 12.2.1.4.0 + location: /Users/acme/Downloads/fmw_12.2.1.4.0_wls_lite_Disk1_1of1.zip + linux/arm64: + version: 12.2.1.4.0 + location: /Users/acme/Downloads/fmw_12.2.1.4.0_wls_generic_ARM_OCI.zip + 14.1.1.0.0: + linux/amd64: + version: 14.1.1.0.0 + location: /Users/acme/Downloads/fmw_14.1.1.0.0_wls_lite_Disk1_1of1.zip + linux/arm64: + version: 14.1.1.0.0 + location: /Users/acme/Downloads/fmw14110.zip + 12.2.1.3.0: + linux/amd64: + version: 12.2.1.3.0 + location: /Volumes/home/files/witsystest/fmw_12.2.1.3.0_wls_Disk1_1of1.zip + 14.1.2.0.0: + linux/arm64: + version: 14.1.2.0.0 + location: /Users/acme/Downloads/fmw_14.1.2.0.0_wls_generic-091024.jar +``` + +- `listPatches`: List all the patches. + +```bash + +Usage: imagetool cache listPatches [--patchId=] [--version=] +List patches + --patchId= Patch id + --version= Patch version + --details Show details about the patches + + +$ imagetool.sh cache listPatches + +36805124: + linux/amd64: + - version: 12.2.1.4.0 + location: /Users/acme/oraclePatches/p36805124_122140_Generic.zip +28186730: + Generic: + - version: 13.9.4.2.16 + location: /Users/acme/cache/p28186730_1394216_Generic.zip + - version: 13.9.4.2.17 + location: /Users/acme/oraclePatches/p28186730_1394217_Generic.zip + + +``` + - `addInstaller`: Add an installer to the cache, for example, JDK. ```bash - $ imagetool cache addInstaller --type jdk --version 8u202 --path /path/to/local/jdk.tar.gz + + Usage: imagetool cache addInstaller [--force] -a= + [-c=] -p= [-t=] + -v= + Add cache entry for wls, fmw, jdk or wdt installer + --force Overwrite existing entry, if it exists + -p, --path= Location of the file on disk. For ex: + /path/to/patch-or-installer.zip + -t, --type= Type of installer. Valid values: wlsdev, wlsslim, + wls, fmw, soa, osb, b2b, mft, idm, oam, ohs, + db19, oud, oid, wcc, wcp, wcs, jdk, wdt, odi + -v, --version= Installer version. Ex: For WLS|FMW use 12.2.1.3.0 + For jdk, use 8u201. The version for WLS, FMW etc. + will be used to obtain patches. + -a, --architecture= + Installer architecture. Valid values: arm64, amd64, + Generic + -c, --commonName= + (Optional) common name. Valid values: Alphanumeric + values with no special characters. If not + specified, default to the version value. Use + this if you want to use a special name for the + particular version of the installer. + + + + $ imagetool cache addInstaller --type jdk --version 8u202 --path /path/to/local/jdk.tar.gz --architecture AMD64 ``` - `addPatch`: Add a patch to the cache. ```bash - $ imagetool cache addPatch --patchId 12345678_12.2.1.3.0 --path /path/to/patch.zip + + Usage: imagetool cache addPatch [--force] -a= [-d=] + -p= --patchId= -v= + Add cache entry for wls|fmw patch or psu + --force Overwrite existing entry, if it exists + -p, --path= Location of the file on disk. For ex: + /path/to/patch-or-installer.zip + --patchId= Patch number. Ex: 28186730 + -v, --version= Patch version. + -a, --architecture= + Patch architecture. Valid values: arm64, amd64, + Generic + -d, --description= + Patch description. + + + + $ imagetool cache addPatch --patchId 12345678_12.2.1.3.0 --path /path/to/patch.zip --architecture AMD64 ``` **Note**: When adding a patch to the cache store, the `patchId` should be in the following format: `99999999_9.9.9.9.99999` The first 8 digits is the patch ID, followed by an underscore, and then the release number to identify the patch between different patch versions. @@ -68,12 +204,41 @@ List and set cache options If you downloaded the release version ```12.2.1.3.190416``` of the patch, then you should use the argument ```--patchId 29135930_12.2.1.3.190416```. -- `addEntry`: Consider this an expert mode where you can add key value pairs to the cache without any validation. + +- `deleteInstaller`: Delete an installer from the cache for a given key. **Note**: This command does not delete files from the disk. ```bash - $ imagetool cache addEntry --key xyz_123 --value /path/to/file + + Usage: imagetool cache deleteInstaller [--architecture=] + [--cn=] [--type=] + [--version=] + Delete a installer + --architecture= + Specific architecture to delete + --cn= Filter by common name. + --type= Filter installer type. e.g. wls, jdk, wdt + --version= Specific version to delete + + + $ imagetool cache deleteInstaller --version 14.1.1.0.0 --architecture ARM64 ``` -- `deleteEntry`: Delete an entry from the cache for a given key. **Note**: This command does not delete files from the disk. +- `deletePatch`: Delete a patch from the cache for a given key. **Note**: This command does not delete files from the disk. ```bash - $ imagetool cache deleteEntry --key xyz_123 + Usage: imagetool cache deletePatch [--architecture=] + [--patchId=] [--version=] + Delete a patch + --architecture= + Specific architecture to delete + --patchId= Bug num + --version= Specific version to delete + + + $ imagetool cache deletePatch --patchId 12345678 --version 14.1.1.0.0 --architecture ARM64 ``` + +- `convert`: Convert Imagetool version 1.x to 2.0 format. + +```bash +Usage: imagetool cache convert +Convert cache settings from v1 to v2 +``` \ No newline at end of file diff --git a/documentation/site/content/userguide/tools/create-aux-image.md b/documentation/site/content/userguide/tools/create-aux-image.md index abe6c8b29..4456e7027 100644 --- a/documentation/site/content/userguide/tools/create-aux-image.md +++ b/documentation/site/content/userguide/tools/create-aux-image.md @@ -31,9 +31,11 @@ Usage: imagetool createAuxImage [OPTIONS] | `--fromImage` | Container image to use as a base image when creating a new image. | `busybox` | | `--httpProxyUrl` | Proxy for the HTTP protocol. Example: `http://myproxy:80` or `http:user:passwd@myproxy:8080` | | | `--httpsProxyUrl` | Proxy for the HTTPS protocol. Example: `https://myproxy:80` or `https:user:passwd@myproxy:8080` | | +| `--load` | Load into local repository - only valid for docker building multi platforms images, user is assumed logged in to the repo registry already. All others are ignored.| | `--packageManager` | Override the default package manager for the base image's operating system. Supported values: `APK`, `APTGET`, `NONE`, `YUM`, `ZYPPER` | | -| `--platform` | Set the target platform to build. Supported values: `linux/amd64` or `linux/arm64`. | | +| `--platform` | Comma separated list (no space between) of target platforms for the image: linux/amd64 or linux/arm64| | `--pull` | Always attempt to pull a newer version of base images during the build. | | +| `--push` | Push to remote repository - only valid for docker building multi platforms images, user is assumed logged in to the repo registry already. All others are ignored. | | `--skipcleanup` | Do not delete the build context folder, intermediate images, and failed build containers. For debugging purposes. | | | `--target` | Select the target environment in which the created image will be used. Supported values: `Default` (Docker/Kubernetes), `OpenShift`. See [Additional information](#--target). | `Default` | | `--wdtArchive` | A WDT archive ZIP file or comma-separated list of files. | | diff --git a/documentation/site/content/userguide/tools/create-image.md b/documentation/site/content/userguide/tools/create-image.md index 68a1f5ae2..b4de9def9 100644 --- a/documentation/site/content/userguide/tools/create-image.md +++ b/documentation/site/content/userguide/tools/create-image.md @@ -34,14 +34,16 @@ Usage: imagetool create [OPTIONS] | `--inventoryPointerInstallLoc` | Target location for the inventory pointer file. | | | `--jdkVersion` | Version of the server JDK to install. | `8u202` | | `--latestPSU` | Find and apply the latest PatchSet Update. | | +| `--load` | Load into local repository - only valid for docker building multi platforms images, user is assumed logged in to the repo registry already. All others are ignored.| | `--opatchBugNumber` | The patch number for OPatch (patching OPatch). | `28186730` | | `--packageManager` | Override the default package manager for the base image's operating system. Supported values: `APK`, `APTGET`, `NONE`, `OS_DEFAULT`, `YUM`, `ZYPPER` | `OS_DEFAULT` | | `--password` | Request password for the Oracle Support `--user` on STDIN, see `--user`. | | | `--passwordEnv` | Environment variable containing the Oracle Support password, see `--user`. | | | `--passwordFile` | Path to a file containing just the Oracle Support password, see `--user`. | | | `--patches` | Comma separated list of patch IDs. Example: `12345678,87654321` | | -| `--platform` | Set the target platform to build. Supported values: `linux/amd64` or `linux/arm64`. | | +| `--platform` | Comma separated list (no space between) of target platforms for the image: linux/amd64 or linux/arm64| | `--pull` | Always attempt to pull a newer version of base images during the build. | | +| `--push` | Push to remote repository - only valid for docker building multi platforms images, user is assumed logged in to the repo registry already. All others are ignored. | | `--recommendedPatches` | Find and apply the latest PatchSet Update and recommended patches. This takes precedence over `--latestPSU`. | | | `--resourceTemplates` | One or more files containing placeholders that need to be resolved by the Image Tool. See [Resource Template Files](#resource-template-files). | | | `--skipcleanup` | Do not delete the build context folder, intermediate images, and failed build containers. For debugging purposes. | | diff --git a/documentation/site/content/userguide/tools/multiplatform-build.md b/documentation/site/content/userguide/tools/multiplatform-build.md new file mode 100644 index 000000000..f4f4a91b5 --- /dev/null +++ b/documentation/site/content/userguide/tools/multiplatform-build.md @@ -0,0 +1,166 @@ +--- +title: "Cross-Platform and Multi-Platforms Build" +date: 2025-01-11 +draft: false +weight: 1 +description: "Experimental Support for building cross-platform and multi-platforms images." +--- + +This version supports experimental building multi-platform images using `Docker` or `Podman` in a single `create` command. +We have encountered any cross-platform or multi-platforms build may hang during build. You can always create separate images and manually create manifest to build a multi-platform images. + +## Prerequisite: + +When building cross or multi-platform images, the `docker` or `podman` command depends on QEMU emulation setup in the build environment. In +a Linux environment, you can add QEMU support depending on your distribution and releases. You will also need to setup +the `bin-fmt` support in the container environment. Note: If you reboot, reset or purge the build system, you will need +to setup again before building cross platform images. For `podman` the equivalent command is `sudo podman` instead of +`docker`. + +```bash +$ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes +``` + +In ARM64, you can use + +```bash +$ docker run --privileged --rm tonistiigi/binfmt --install all +``` + +This will install all the emulated platform binaries. You need to verify the binaries are actually installed in + +```bash +$ ls -l /proc/sys/fs/binfmt_misc/ +-rw-r--r--. 1 root root 0 Jan 1 19:55 qemu-aarch64 +-rw-r--r--. 1 root root 0 Jan 1 19:55 qemu-arm +-rw-r--r--. 1 root root 0 Jan 1 19:55 qemu-mips64 +-rw-r--r--. 1 root root 0 Jan 1 19:55 qemu-mips64el +-rw-r--r--. 1 root root 0 Jan 1 19:55 qemu-ppc64le +-rw-r--r--. 1 root root 0 Jan 1 19:55 qemu-riscv64 +-rw-r--r--. 1 root root 0 Jan 1 19:55 qemu-s390x +--w-------. 1 root root 0 Jan 1 19:54 register +-rw-r--r--. 1 root root 0 Jan 1 19:54 status + +``` + +You can verify if you can run a pod using the emulator for a different platform. For example, if you are in a `amd64` +environment and want to run `arm64` images, then you can: + +```bash +$ docker run -it --platform linux/arm64 --rm ghcr.io/oracle/oraclelinux:8-slim sh +``` +In the terminal session, run `microdnf update` + +If the commands are successful, then the emulation is working. If it fails, then you can +check `/var/log/messages` or `/var/log/audit/audit.log` for errors. Most of the issues are related to `SELinux` settings, +a quick work around is set to `permissive` using: + +```bash +$ sudo setenforce 0 +``` + +You may need administrator and security expert to help to solve any SELinux related issue in your environment. + +If you want to unregister an particular emulator, you can + +```bash +$ sudo echo '-1' > /proc/sys/fs/binfmt_misc/qemu-aarch64 +``` + +For unregistering all emulators + +```bash +$ for f in /proc/sys/fs/binfmt_misc/qemu*; do + sudo echo '-1' > "$f" + done +``` + +## Creating image using a single command: + +1. Make sure the installers are added to the cache, for example: + +```bash +$ imagetool.sh cache addInstaller --type wls --architecture AMD64 --version 14.1.1.0.0 --path /path/to/wls12214-amd-install.zip +$ imagetool.sh cache addInstaller --type wls --architecture ARM64 --version 14.1.1.0.0 --path /path/to/wls12214-arm-install.zip +$ imagetool.sh cache addInstaller --type jdk --architecture AMD64 --version 11u22 --path /path/to/jdk-11u22-amd.tar.gz +$ imagetool.sh cache addInstaller --type jdk --architecture ARM64 --version 11u22 --path /path/to/jdk-11u22-arm.tar.gz +``` + +2. Create the image: + +```bash +$ imagetool.sh create --tag myrepo/wls14110:20250111 --platform linux/arm64,linux/amd64 --version 14.1.1.0.0 --jdkVersion 11u22 --push +``` + +This command will build the multi-platform image containing both `linux/arm64` and `linux/amd64` in the manifest and push to the repository `myrepo`. + +## Creating image using multiple commands: + +In case the emulation failed when building cross platform image, you can always do it manually with multiple commands. + +1. Build the `linux/amd64` image in a `amd64` machine and push it to the repository. + +```bash +$ imagetool.sh create --tag myrepo/wls14110:20250111-amd64 --platform linux/amd64 --version 14.1.1.0.0 --jdkVersion 11u22 +$ docker push myrepo/wls14110:20250111-amd64 +``` + +2. Build the `linux/arm64` image in a `arm64` machine and push it to the repository. + +```bash +$ imagetool.sh create --tag myrepo/wls14110:20250111-arm64 --platform linux/arm64 --version 14.1.1.0.0 --jdkVersion 11u22 +$ docker push myrepo/wls14110:20250111-arm64 +``` + +3. Create the manifest. + +```bash +$ docker manifest create myrepo/wls14110:20250111 +``` + +4. Add the images to the manifest. + +```bash +$ docker manifest add myrepo/wls14110:20250111 myrepo/wls14110:20250111-amd64 +$ docker manifest add myrepo/wls14110:20250111 myrepo/wls14110:20250111-arm64 +``` + +5. Push the manifest to the repository. + +```bash +$ docker manifest push myrepo/wls14110:20250111 myrepo/wls14110:20250111 +``` + + +## Known Problems. + +1. During building cross platform image, for example if you are on a `AMD644` platform building a `ARM64` platform, you will +see it stuck in `Copying Files` + +``` +#31 14.93 Setting ORACLE_HOME... +#31 20.06 Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. +#31 20.07 Reading response file.. +#31 20.17 Skipping Software Updates +#31 20.18 Validations are disabled for this session. +#31 20.18 Verifying data +#31 30.47 Copying Files +#31 52.05 Percent Complete : 10 +#31 56.19 Percent Complete : 20 + +``` + +If you run the command `ps axww | grep qemu` on the build machine, you will find duplicate java processes similar to this + +``` +1807143 ? Sl 2:01 /usr/bin/qemu-aarch64-static /u01/jdk/bin/java -cp /tmp/OraInstall2025-02-07_01-38-49PM/oui/mw/common/framework/jlib/engine-nextgen.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oui/mw/common/framework/jlib/message.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oui/mw/common/framework/jlib/oneclick.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.dms/dms.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.odl/ojdl.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.odl/ojdl2.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.odl/ojdl-log4j.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.bali.jewt/jewt4.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.bali.jewt/olaf2.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/jlib/wizardCommonResources.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.bali.share/share.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.help/oracle_ice.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.help/ohj.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.help/help-share.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oui/modules/installer-launch.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oui/modules/xml.jar -mx512m -Doracle.installer.appendjre=true -Djava.io.tmpdir=/tmp -Doracle.installer.jre_loc=/u01/jdk -Doracle.installer.custom_inventory=/u01/oracle/oraInventory -Doracle.cie.logging.useodl=true -Doracle.installer.startup_location=/tmp/orcl4209283845595543233.tmp/Disk1/install/linux64 -Doracle.installer.nlsEnabled=true -Doracle.installer.bootstrap=true -Doracle.installer.paramFile=/tmp/orcl4209283845595543233.tmp/Disk1/install/linux64/oraparam.ini -Doracle.installer.oui_loc=/tmp/OraInstall2025-02-07_01-38-49PM/oui -Doracle.installer.launch_loc=/ -Doracle.installer.unixVersion=6.8.0-39-generic -Doracle.installer.scratchPath=/tmp/OraInstall2025-02-07_01-38-49PM -Doracle.installer.extjre=true -Doracle.installer.timestamp=2025-02-07_01-38-49PM -Doracle.installer.operation=install -DisNextGen=true -Doracle.installer.prereqConfigLoc=/tmp/OraInstall2025-02-07_01-38-49PM/oui/mw/wls/prereq -Doracle.installer.library_loc=/tmp/OraInstall2025-02-07_01-38-49PM/oui/lib/linux64 -Doracle.installer.logPath=/tmp/OraInstall2025-02-07_01-38-49PM oracle.sysman.oio.oioc.OiocOneClickInstaller -scratchPath /tmp/OraInstall2025-02-07_01-38-49PM -sourceType network -timestamp 2025-02-07_01-38-49PM -paramFile /tmp/orcl4209283845595543233.tmp/Disk1/install/linux64/oraparam.ini -silent ORACLE_HOME=/u01/oracle -responseFile /tmp/imagetool/wls/linux/amd64/wls.rsp -invPtrLoc /u01/oracle/oraInst.loc -ignoreSysPrereqs -force -novalidation -nocleanUpOnExit +1809542 ? Sl 0:00 /usr/bin/qemu-aarch64-static /u01/jdk/bin/java -cp /tmp/OraInstall2025-02-07_01-38-49PM/oui/mw/common/framework/jlib/engine-nextgen.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oui/mw/common/framework/jlib/message.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oui/mw/common/framework/jlib/oneclick.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.dms/dms.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.odl/ojdl.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.odl/ojdl2.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.odl/ojdl-log4j.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.bali.jewt/jewt4.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.bali.jewt/olaf2.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/jlib/wizardCommonResources.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.bali.share/share.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.help/oracle_ice.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.help/ohj.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oracle_common/modules/oracle.help/help-share.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oui/modules/installer-launch.jar:/tmp/OraInstall2025-02-07_01-38-49PM/oui/modules/xml.jar -mx512m -Doracle.installer.appendjre=true -Djava.io.tmpdir=/tmp -Doracle.installer.jre_loc=/u01/jdk -Doracle.installer.custom_inventory=/u01/oracle/oraInventory -Doracle.cie.logging.useodl=true -Doracle.installer.startup_location=/tmp/orcl4209283845595543233.tmp/Disk1/install/linux64 -Doracle.installer.nlsEnabled=true -Doracle.installer.bootstrap=true -Doracle.installer.paramFile=/tmp/orcl4209283845595543233.tmp/Disk1/install/linux64/oraparam.ini -Doracle.installer.oui_loc=/tmp/OraInstall2025-02-07_01-38-49PM/oui -Doracle.installer.launch_loc=/ -Doracle.installer.unixVersion=6.8.0-39-generic -Doracle.installer.scratchPath=/tmp/OraInstall2025-02-07_01-38-49PM -Doracle.installer.extjre=true -Doracle.installer.timestamp=2025-02-07_01-38-49PM -Doracle.installer.operation=install -DisNextGen=true -Doracle.installer.prereqConfigLoc=/tmp/OraInstall2025-02-07_01-38-49PM/oui/mw/wls/prereq -Doracle.installer.library_loc=/tmp/OraInstall2025-02-07_01-38-49PM/oui/lib/linux64 -Doracle.installer.logPath=/tmp/OraInstall2025-02-07_01-38-49PM oracle.sysman.oio.oioc.OiocOneClickInstaller -scratchPath /tmp/OraInstall2025-02-07_01-38-49PM -sourceType network -timestamp 2025-02-07_01-38-49PM -paramFile /tmp/orcl4209283845595543233.tmp/Disk1/install/linux64/oraparam.ini -silent ORACLE_HOME=/u01/oracle -responseFile /tmp/imagetool/wls/linux/amd64/wls.rsp -invPtrLoc /u01/oracle/oraInst.loc -ignoreSysPrereqs -force -novalidation -nocleanUpOnExit +``` + +It appears to be a `QEMU` process not handling sub process correctly, you can terminate the last process by `sudo kill -9 `. For example +`sudo kill -9 1809542` then the build will continue and complete. The best way to solve this is use manual build in each respective platform as mentioned in last section. + + +2. You may encounter `SELinux` security issue, usually it appears some build commands failed. You can check for errors in `/var/log/messages` and search for +for `QEMU`. In this case, you need to work with your administrator either relax the security policy or setup customized policy. This is +environment specific. diff --git a/documentation/site/content/userguide/tools/rebase-image.md b/documentation/site/content/userguide/tools/rebase-image.md deleted file mode 100644 index 25f7fd549..000000000 --- a/documentation/site/content/userguide/tools/rebase-image.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: "Rebase Image" -date: 2024-04-11 -draft: false -weight: 3 -description: "The rebase command creates a new container image using an existing WebLogic domain from an existing image." ---- - - -The `rebase` command creates a new container image and copies an existing WebLogic domain to that new image. -The new container image can be based on an existing image in the repository or created as part of the rebase operation -similar to the `create` command. - -``` -Usage: imagetool rebase [OPTIONS] -``` - -| Parameter | Definition | Default | -| --- | --- | --- | -| `--sourceImage` | (Required) Source Image containing the WebLogic domain. | | -| `--tag` | (Required) Tag for the final build image. Example: `store/oracle/weblogic:12.2.1.3.0` | | -| `--additionalBuildCommands` | Path to a file with additional build commands. For more details, see [Additional information](#--additionalbuildcommands). | -| `--additionalBuildFiles` | Additional files that are required by your `additionalBuildCommands`. A comma separated list of files that should be copied to the build context. See [Additional information](#--additionalbuildfiles). | -| `--builder`, `-b` | Executable to process the Dockerfile. Use the full path of the executable if not on your path. | Defaults to `docker`, or, when set, to the value in environment variable `WLSIMG_BUILDER`. | -| `--buildNetwork` | Networking mode for the RUN instructions during the image build. See `--network` for Docker `build`. | | -| `--chown` | `userid:groupid` to be used for creating files within the image, such as the JDK, the FMW/WLS installs, etc. If the user or group does not exist in the image, they will be added with useradd/groupadd. | `oracle:oracle` | -| `--dryRun` | Skip Docker build execution and print the Dockerfile to stdout. | | -| `--fromImage` | Container image to use as a base image when creating a new image. | `ghcr.io/oracle/oraclelinux:8-slim` | -| `--httpProxyUrl` | Proxy for the HTTP protocol. Example: `http://myproxy:80` or `http:user:passwd@myproxy:8080` | | -| `--httpsProxyUrl` | Proxy for the HTTPS protocol. Example: `https://myproxy:80` or `https:user:passwd@myproxy:8080` | | -| `--installerResponseFile` | One or more custom response files. A comma separated list of paths to installer response files. Overrides the default responses for the Oracle silent installer. | | -| `--inventoryPointerFile` | Path to custom inventory pointer file. | | -| `--inventoryPointerInstallLoc` | Target location for the inventory pointer file. | | -| `--jdkVersion` | Version of the server JDK to install. | `8u202` | -| `--latestPSU` | Find and apply the latest PatchSet Update. | | -| `--opatchBugNumber` | The patch number for OPatch (patching OPatch). | `28186730` | -| `--packageManager` | Override the default package manager for the base image's operating system. Supported values: `APK`, `APTGET`, `NONE`, `OS_DEFAULT`, `YUM`, `ZYPPER` | `OS_DEFAULT` | -| `--password` | Request password for the Oracle Support `--user` on STDIN, see `--user`. | | -| `--passwordEnv` | Environment variable containing the Oracle Support password, see `--user`. | | -| `--passwordFile` | Path to a file containing just the Oracle Support password, see `--user`. | | -| `--patches` | Comma separated list of patch IDs. Example: `12345678,87654321` | | -| `--platform` | Set the target platform to build. Supported values: `linux/amd64` or `linux/arm64`. | | -| `--pull` | Always attempt to pull a newer version of base images during the build. | | -| `--recommendedPatches` | Find and apply the latest PatchSet Update and recommended patches. This takes precedence over `--latestPSU`. | | -| `--skipcleanup` | Do not delete the build context folder, intermediate images, and failed build containers. For debugging purposes. | | -| `--strictPatchOrdering` | Instruct OPatch to apply patches one at a time (uses `apply` instead of `napply`). | | -| `--target` | Select the target environment in which the created image will be used. Supported values: `Default` (Docker/Kubernetes), `OpenShift`. See [Additional information](#--target). | `Default` | -| `--targetImage` | Container image to extend for the domain's new image. | | -| `--type` | Installer type. Supported values: `WLS`, `WLSDEV`, `WLSSLIM`, `FMW`, `IDM`, `MFT`, `OAM`, `ODI`, `OHS`, `OIG`, `OUD`, `OUD_WLS`, `OID`, `OSB`, `SOA`, `SOA_OSB`, `SOA_OSB_B2B`, `WCC`, `WCP`, `WCS` | `WLS` | -| `--user` | Your Oracle support email ID. When supplying `user`, you must supply the password either as an environment variable using `--passwordEnv`, or as a file using `--passwordFile`, or interactively, on the command line with `--password`. | | -| `--version` | Installer version. | `12.2.1.3.0` | - -### Additional information - -#### `--additionalBuildCommands` - -This is an advanced option that let's you provide additional commands to the Docker build step. -The input for this parameter is a simple text file that contains one or more of the valid sections. Valid sections for rebase: - -| Section | Available Variables | Build Stage | Timing | -| --- | --- | --- | --- | -| `initial-build-commands` | None | All | As root, and before any Image Tool actions. | -| `before-jdk-install` | `JAVA_HOME` `DOMAIN_HOME`| Intermediate (JDK_BUILD) | Before the JDK is installed. | -| `after-jdk-install` | `JAVA_HOME` `DOMAIN_HOME` | Intermediate (JDK_BUILD) | After the JDK is installed. | -| `before-fmw-install` | `JAVA_HOME` `ORACLE_HOME` `DOMAIN_HOME` | Intermediate (WLS_BUILD) | Before the Oracle Home is created. | -| `after-fmw-install` | `JAVA_HOME` `ORACLE_HOME` `DOMAIN_HOME` | Intermediate (WLS_BUILD) | After all of the Oracle middleware installers are finished. | -| `final-build-commands` | `JAVA_HOME` `ORACLE_HOME` `DOMAIN_HOME` | Final image | After all Image Tool actions are complete, and just before the container image is finalized. | - -**NOTE**: Changes made in intermediate stages may not be carried forward to the final image unless copied manually. -The Image Tool will copy the Java Home and the Oracle Home directories to the final image. -Changes fully contained within these directories do not need an additional `COPY` command in the `final-build-commands` section. -Each section can contain one or more valid Dockerfile commands and would look like the following: - -```dockerfile -[after-fmw-install] -RUN rm /some/dir/unnecessary-file -COPY --chown=oracle:oracle files/my_additional_file.txt /u01 - -[final-build-commands] -LABEL owner="middleware team" -``` - -#### `--additionalBuildFiles` - -This option provides a way to supply additional files to the image build command. -All provided files and directories are copied directly under the `files` subfolder of the build context. -To get those files into the image, additional build commands must be provided using the `additionalBuildCommands` options. -Access to these files using a build command, such as `COPY` or `ADD`, should use the original filename -with the folder prefix, `files/`. For example, if the -original file was provided as `--additionalBuildFiles /scratch/test1/convenience.sh`, the Docker build command `COPY` -provided in `--additionalBuildCommands` should look like -`COPY --chown=oracle:oracle files/convenience.sh /my/internal/image/location`. -Because Image Tool uses multi-stage -builds, it is important to place the build command (like `COPY`) in the appropriate section of the `Dockerfile` based -on when the build needs access to the file. For example, if the file is needed in the final image and not for -installation or domain creation steps, use the `final-build-commands` section so that the `COPY` command occurs in the -final stage of the image build. Or, if the file needs to change the Oracle Home prior to domain creation, use -the `after-fmw-install` or `before-wdt-command` sections. - -#### `--target` - -By default, the generated WLS domain in your image will use the best practices defined by Oracle WebLogic Server. -The `target` option allows you to toggle the defaults so that the generated domain is easier to use in the target -environment. For example, the `--target OpenShift` option will change the file permissions in the domain directory -so that the group permissions match the user permissions. - -| Target | Default File Permissions | Default File Ownership | -| --- | --- | --- | -| `Default` | `rwxr-x---` | `oracle:oracle` | -| `OpenShift` | `rwxrwx---` | `oracle:root` | - -#### Use an argument file - -You can save all arguments passed for the Image Tool in a file, then use the file as a parameter. - -For example, create a file called `build_args`: - -```bash -rebase ---tag wls:122140 ---sourceImage wls:122130 ---version 12.2.1.4.0 ---jdkVersion 8u221 -``` - -Use it on the command line, as follows: - -```bash -$ imagetool @/path/to/build_args -``` - - -### Usage scenarios - -**Note**: Use `--passwordEnv` or `--passwordFile` instead of `--password`. - -The commands below assume that all the required JDK, WLS, or FMW (WebLogic infrastructure) installers have been downloaded - to the cache directory. Use the [cache]({{< relref "/userguide/tools/cache.md" >}}) command to set it up. - - -- Update the JDK for an existing domain. Copy the existing domain from `sample:v1` where the JDK was 8u202 to a new -image called `sample:v2` and install the newer JDK 8u221 with WebLogic Server 12.2.1.3.0. - ```bash - $ imagetool rebase --tag sample:v2 --sourceImage sample:v1 --version 12.2.1.3.0 --jdkVersion 8u221 - ``` - -- Update the Oracle Home for an existing domain with a newer WebLogic version. Copy a domain from an existing image to -a new image with a new install of WebLogic Server 12.2.1.4.0. Copy the domain -from `sample:v1` and select the desired WebLogic installer using the `--version` argument. - ```bash - $ imagetool rebase --tag sample:v2 --sourceImage sample:v1 --version 12.2.1.4.0 --jdkVersion 8u221 - ``` - -- Update the JDK and/or Oracle Home for an existing domain using another image with pre-installed binaries. -Copy the domain from the source image named `sample:v1` to a new image called `sample:v2` based on a target image -named `fmw:12214`. **Note**: The Oracle Home and JDK must be installed in the same same folders on each image. - ```bash - $ imagetool rebase --tag sample:v2 --sourceImage sample:v1 --targetImage fmw:12214 - ``` diff --git a/imagetool/pom.xml b/imagetool/pom.xml index 826b68de7..f355ce785 100644 --- a/imagetool/pom.xml +++ b/imagetool/pom.xml @@ -47,6 +47,10 @@ uk.org.webcompere system-stubs-jupiter + + org.yaml + snakeyaml + diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/api/model/CachedFile.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/api/model/CachedFile.java index 2d461e548..4e69de3e8 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/api/model/CachedFile.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/api/model/CachedFile.java @@ -9,17 +9,16 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import com.oracle.weblogic.imagetool.cachestore.CacheStore; +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; import com.oracle.weblogic.imagetool.installer.InstallerType; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import com.oracle.weblogic.imagetool.util.Architecture; import com.oracle.weblogic.imagetool.util.Utils; + /** * Base class to represent either an installer or a patch file. */ @@ -27,85 +26,65 @@ public class CachedFile { private static final LoggingFacade logger = LoggingFactory.getLogger(CachedFile.class); - private final String id; private final String version; + private String commonName = null; private final Architecture architecture; + private final InstallerType installerType; /** * Represents a locally cached file. * - * @param id cache ID (like installer type or patchId) - * @param version version number for the patch or installer. - * @param target the system architecture that this file/installer is applicable + * @param id cache ID (like installer type) + * @param version version number for the patch or installer. + * @param architecture the system architecture that this file/installer is applicable */ - public CachedFile(String id, String version, Architecture target) { - Objects.requireNonNull(id, "key for the cached file cannot be null"); - logger.entering(id, version, target); - this.id = id; - this.version = version; - this.architecture = target; - logger.exiting(); + public CachedFile(InstallerType id, String version, Architecture architecture) { + this(id, version, architecture, version); } /** * Represents a locally cached file. * - * @param id cache ID (like installer type) - * @param version version number for the patch or installer. - * @param target the system architecture that this file/installer is applicable + * @param id cache ID (like installer type) + * @param version version number for the patch or installer. + * @param architecture the system architecture that this file/installer is applicable + * @param commonName common name of the installer */ - public CachedFile(InstallerType id, String version, Architecture target) { - this(id.toString(), version, target); + public CachedFile(InstallerType id, String version, Architecture architecture, String commonName) { + if (commonName == null) { + commonName = version; + } + this.installerType = id; + this.version = version; + this.architecture = architecture; + this.commonName = commonName; } /** * Represents a locally cached file. * - * @param id cache ID (like installer type) - * @param version version number for the patch or installer. + * @param id cache ID (like installer type) + * @param version version number for the patch or installer. */ public CachedFile(InstallerType id, String version) { - this(id.toString(), version, null); - } - - public static boolean isFileOnDisk(String filePath) { - return filePath != null && Files.isRegularFile(Paths.get(filePath)); + this(id, version, null); } /** - * Get the key for this cache entry. - * If the ID that was used to create this CachedFile object contains the separator (underscore), - * then the key is the same as the ID. Otherwise, the key is the ID plus version, like ID + "_" + version. - * @return the key to use for this cache entry, like xxxx_yyyy. + * constructor. + * @param isPatch is it a patch + * @param patchId patch id + * @param version version + * @param architecture architecture */ - public String getKey() { - if (architecture == null) { - return getCacheKey(null); - } else { - return getCacheKey(architecture.toString()); - } - } - - private String getCacheKey(String arch) { - if (id.contains(CacheStore.CACHE_KEY_SEPARATOR)) { - return id; - } - - StringBuilder key = new StringBuilder(32); - key.append(id); - key.append(CacheStore.CACHE_KEY_SEPARATOR); - key.append(version); - if (arch != null) { - key.append(CacheStore.CACHE_KEY_SEPARATOR); - key.append(arch); - } - return key.toString(); + public CachedFile(boolean isPatch, String patchId, String version, Architecture architecture) { + this.version = version; + this.architecture = architecture; + this.installerType = null; } - private List getPossibleKeys(Architecture architecture) { - ArrayList result = new ArrayList<>(); - architecture.getAcceptableNames().forEach(name -> result.add(getCacheKey(name))); - return result; + public static boolean isFileOnDisk(String filePath) { + return filePath != null && Files.isRegularFile(Paths.get(filePath)); } /** @@ -124,6 +103,14 @@ public Architecture getArchitecture() { return architecture; } + /** + * Get the common name of the installer. + * @return common name + */ + public String getCommonName() { + return commonName; + } + /** * Get the path of the file stored locally in the cache. * Searching the cache starts with the specified key. If the key is not found in the cache, @@ -132,35 +119,34 @@ public Architecture getArchitecture() { * for an entry listing without the architecture in the key (generic architecture entry). If the user * did not specify an architecture, check the cache for an entry listing using the local architecture * in case the user added the cache entry with the architecture. - * @param cacheStore the cache store to search * @return the Path of the file, if found * @throws IOException throws FileNotFoundException, if this cached file (key) could not be located in the cache */ - public String resolve(CacheStore cacheStore) throws IOException { + public String resolve() throws IOException { // check entry exists in cache logger.entering(); String filePath = null; - List keySearchOrder = new ArrayList<>(); - if (getArchitecture() == null) { - // architecture was not specified, search for cache key with no arch first, then look for local arch - keySearchOrder.add(getCacheKey(null)); - keySearchOrder.addAll(getPossibleKeys(Architecture.getLocalArchitecture())); - } else { - // architecture was specified, search for cache key with arch first, then look for no arch key - keySearchOrder.addAll(getPossibleKeys(getArchitecture())); - keySearchOrder.add(getCacheKey(null)); - } - for (String key: keySearchOrder) { - logger.finer("Trying cache key {0}", key); - filePath = cacheStore.getValueFromCache(key); - if (filePath != null) { - logger.finer("Found cache key {0}", key); - break; + + InstallerMetaData metaData = ConfigManager.getInstance() + .getInstallerForPlatform(installerType, getArchitecture(), getVersion(), getCommonName()); + if (metaData == null) { + metaData = ConfigManager.getInstance() + .getInstallerForPlatform(installerType, Architecture.getLocalArchitecture(), getVersion(), + getCommonName()); + + if (metaData == null) { + metaData = ConfigManager.getInstance() + .getInstallerForPlatform(installerType, Architecture.GENERIC, getVersion(), getCommonName()); } } - if (!isFileOnDisk(filePath)) { - throw new FileNotFoundException(Utils.getMessage("IMG-0011", getKey())); + if (metaData != null) { + filePath = metaData.getLocation(); + } + + if (metaData == null || !isFileOnDisk(filePath)) { + throw new FileNotFoundException(Utils.getMessage("IMG-0011", installerType, + getVersion(), getArchitecture(), filePath)); } logger.exiting(filePath); @@ -169,14 +155,13 @@ public String resolve(CacheStore cacheStore) throws IOException { /** * Copy file from cacheStore to Docker build context directory. - * @param cacheStore cache to copy file from * @param buildContextDir directory to copy file to * @return the path of the file copied to the Docker build context directory */ - public Path copyFile(CacheStore cacheStore, String buildContextDir) throws IOException { - logger.entering(id, version, architecture, buildContextDir); + public Path copyFile(String buildContextDir) throws IOException { + logger.entering(installerType, version, architecture, buildContextDir); Path result; - String sourceFile = resolve(cacheStore); + String sourceFile = resolve(); logger.info("IMG-0043", sourceFile); String targetFilename = new File(sourceFile).getName(); try { diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruPatch.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruPatch.java index 98e8f4aa3..be25d3d9a 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruPatch.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruPatch.java @@ -3,6 +3,7 @@ package com.oracle.weblogic.imagetool.aru; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -17,6 +18,8 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import static com.oracle.weblogic.imagetool.aru.AruUtil.getAruPlatformName; + /** * Metadata for a patch, as defined by ARU. * Simple bean for holding metadata obtained from ARU for a given patch ID and version. @@ -36,6 +39,9 @@ public class AruPatch { private String downloadPath; private String fileName; private String access; + private String platformName = "Generic"; + private String sha256Hash; + private String releasedDate; public String patchId() { return patchId; @@ -63,6 +69,7 @@ public String description() { return description; } + public AruPatch description(String value) { description = value; return this; @@ -77,6 +84,15 @@ public AruPatch product(String value) { return this; } + public String platformName() { + return platformName; + } + + public AruPatch platformName(String platform) { + this.platformName = platform; + return this; + } + public String release() { return release; } @@ -86,6 +102,14 @@ public AruPatch release(String value) { return this; } + public AruPatch sha256Hash(String value) { + sha256Hash = value; + return this; + } + + public String sha256Hash() { + return sha256Hash; + } public String releaseName() { return releaseName; @@ -100,8 +124,14 @@ public Integer platform() { return platform; } + /** + * Setting platform value. + * @param value value of the platform + * @return this + */ public AruPatch platform(String value) { platform = Integer.parseInt(value); + platformName = getAruPlatformName(value); return this; } @@ -136,6 +166,15 @@ public AruPatch downloadPath(String value) { return this; } + public AruPatch releasedDate(String value) { + releasedDate = value; + return this; + } + + public String releasedDate() { + return Utils.getReleaseDate(releasedDate); + } + public String downloadUrl() { return downloadHost + downloadPath; } @@ -166,6 +205,22 @@ public boolean isStackPatchBundle() { return description != null && description.contains("STACK PATCH BUNDLE"); } + /** + * Returns true if this patch is known irregular patch (not an actual patch). + *
    + *
  1. Stack Patch Bundle is a zip of patches, but is not a patch itself.
  2. + *
  3. DB Client 19c Upgrade (34761383) is an installer, and not a patch.
  4. + *
+ * @return true if this patch is a StackPatchBundle or known installer, false otherwise. + */ + public boolean isIrregularPatch() { + boolean result = "34761383".equals(patchId) || isStackPatchBundle(); + if (result) { + logger.fine("Detected irregular patch {0}: {1}", patchId, description); + } + return result; + } + public boolean isCoherenceFeaturePack() { return description != null && description.contains("Coherence 14.1.1 Feature Pack"); } @@ -192,8 +247,12 @@ public static Stream getPatches(Document patchList) throws XPathExpres .product(XPathUtil.string(nodeList.item(i), "./product/@id")) .psuBundle(XPathUtil.string(nodeList.item(i), "./psu_bundle")) .access(XPathUtil.string(nodeList.item(i), "./access")) + .sha256Hash(XPathUtil.string(nodeList.item(i), + "./files/file/digest[@type='SHA-256']/text()")) + .platformName(getAruPlatformName(XPathUtil.string(nodeList.item(i), "./platform/@id"))) .downloadHost(XPathUtil.string(nodeList.item(i), "./files/file/download_url/@host")) .downloadPath(XPathUtil.string(nodeList.item(i), "./files/file/download_url/text()")) + .releasedDate(XPathUtil.string(nodeList.item(i), "./released_date/text()")) .platform(XPathUtil.string(nodeList.item(i), "./platform/@id")); int index = patch.downloadPath().indexOf("patch_file="); @@ -259,10 +318,13 @@ public static AruPatch selectPatch(List patches, String providedVersio } else if (patchMap.containsKey(installerVersion)) { selected = patchMap.get(installerVersion); } - logger.exiting(selected); if (selected == null) { - throw logger.throwing(new PatchVersionException(patches.get(0).patchId(), patches)); + List versionStrings = new ArrayList<>(); + for (AruPatch aruPatch : patches) { + versionStrings.add(patches.get(0).patchId() + "_" + aruPatch.version()); + } + throw logger.throwing(new PatchVersionException(patches.get(0).patchId(), versionStrings)); } else { logger.info("IMG-0099", selected.patchId(), selected.version(), selected.description()); return selected; @@ -283,6 +345,21 @@ public boolean isApplicableToTarget(int aruPlatform) { @Override public String toString() { - return patchId + " - " + description; + return "AruPatch{" + + "patchId='" + patchId + '\'' + + ", version='" + version + '\'' + + ", description='" + description + '\'' + + ", product='" + product + '\'' + + ", release='" + release + '\'' + + ", releaseName='" + releaseName + '\'' + + ", psuBundle='" + psuBundle + '\'' + + ", downloadHost='" + downloadHost + '\'' + + ", downloadPath='" + downloadPath + '\'' + + ", fileName='" + fileName + '\'' + + ", access='" + access + '\'' + + ", platform='" + platform + '\'' + + ", platformName='" + platformName + '\'' + + ", sha256Hash='" + sha256Hash + '\'' + + '}'; } } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruProduct.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruProduct.java index 759136b86..4ab970a3a 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruProduct.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruProduct.java @@ -32,6 +32,7 @@ public enum AruProduct { ODI("13724", "Oracle Data Integrator"), OSS("16609", "Oracle Security Service"), OAM_WG("18388", "Oracle Access Manager Web Gates"), + FMW_GLCM("31939", "Oracle Global Lifecycle Management FMW Installer") ; private final String productId; diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruUtil.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruUtil.java index 64c8f26ac..88a73cc3c 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruUtil.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/AruUtil.java @@ -12,18 +12,17 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.xml.xpath.XPathExpressionException; import com.github.mustachejava.DefaultMustacheFactory; -import com.oracle.weblogic.imagetool.cachestore.CacheStoreException; -import com.oracle.weblogic.imagetool.cachestore.CacheStoreFactory; import com.oracle.weblogic.imagetool.installer.FmwInstallerType; +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; +import com.oracle.weblogic.imagetool.installer.InstallerType; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import com.oracle.weblogic.imagetool.util.Architecture; import com.oracle.weblogic.imagetool.util.HttpUtil; import com.oracle.weblogic.imagetool.util.Utils; @@ -44,6 +43,9 @@ public class AruUtil { private static final LoggingFacade logger = LoggingFactory.getLogger(AruUtil.class); + private static final String GENERIC_NAME = "Generic"; + private static final String AMD_NAME = "linux/amd64"; + private static final String ARM_NAME = "linux/arm64"; private static AruUtil instance; @@ -106,11 +108,22 @@ protected AruUtil() { * @throws AruException when an error occurs trying to access ARU metadata */ public List getLatestPsu(FmwInstallerType type, String version, Architecture architecture, - String userId, String password) + String commonName, String userId, String password) throws AruException { List result = new ArrayList<>(); for (AruProduct product : type.products()) { - List psuList = getLatestPsu(product, version, architecture, userId, password); + + String baseVersion = version; + + if (type == FmwInstallerType.IDM && product == AruProduct.JRF) { + InstallerMetaData metaData = ConfigManager.getInstance().getInstallerForPlatform(InstallerType.IDM, + architecture, version, commonName); + if (metaData != null && metaData.getBaseFMWVersion() != null) { + baseVersion = metaData.getBaseFMWVersion(); + } + } + + List psuList = getLatestPsu(product, baseVersion, architecture, userId, password); if (!psuList.isEmpty()) { for (AruPatch psu: psuList) { String patchAndVersion = psu.patchId() + "_" + psu.version(); @@ -155,7 +168,7 @@ List getLatestPsu(AruProduct product, String version, Architecture arc return AruPatch.getPatches(aruRecommendations) .filter(p -> p.isApplicableToTarget(architecture.getAruPlatform())) .filter(AruPatch::isPsu) - .filter(not(AruPatch::isStackPatchBundle)) + .filter(not(AruPatch::isIrregularPatch)) .collect(Collectors.toList()); } catch (NoPatchesFoundException ex) { logger.exiting(); @@ -175,10 +188,23 @@ List getLatestPsu(AruProduct product, String version, Architecture arc * @return Document listing of all patches (full details) */ public List getRecommendedPatches(FmwInstallerType type, String version, Architecture architecture, - String userId, String password) throws AruException { + String commonName, String userId, String password) throws AruException { List result = new ArrayList<>(); + for (AruProduct product : type.products()) { - List patches = getRecommendedPatches(type, product, version, architecture, userId, password); + + String baseVersion = version; + + if (type == FmwInstallerType.IDM && product == AruProduct.JRF) { + + InstallerMetaData metaData = ConfigManager.getInstance().getInstallerForPlatform(InstallerType.IDM, + architecture, version, commonName); + if (metaData != null && metaData.getBaseFMWVersion() != null) { + baseVersion = metaData.getBaseFMWVersion(); + } + } + + List patches = getRecommendedPatches(type, product, baseVersion, architecture, userId, password); if (!patches.isEmpty()) { patches.forEach(p -> logger.info("IMG-0068", product.description(), p.patchId(), p.description())); @@ -202,7 +228,7 @@ public List getRecommendedPatches(FmwInstallerType type, String versio * @throws AruException when response from ARU has an error or fails */ List getRecommendedPatches(FmwInstallerType type, AruProduct product, String version, - Architecture architecture, String userId, String password) throws AruException { + Architecture architecture, String userId, String password) throws AruException { logger.entering(product, version); List patches = Collections.emptyList(); try { @@ -265,7 +291,7 @@ private String getPsuVersion(String productName, Collection patches) { } List getReleaseRecommendations(AruProduct product, String releaseNumber, Architecture architecture, - String userId, String password) + String userId, String password) throws AruException, XPathExpressionException, RetryFailedException { Document patchesDocument = retry( @@ -273,7 +299,7 @@ List getReleaseRecommendations(AruProduct product, String releaseNumbe return AruPatch.getPatches(patchesDocument) .filter(p -> p.isApplicableToTarget(architecture.getAruPlatform())) - .filter(not(AruPatch::isStackPatchBundle)) // remove the Stack Patch Bundle patch, if returned + .filter(not(AruPatch::isIrregularPatch)) // remove the Stack Patch Bundle patch, if returned // TODO: Need an option for the user to request the Coherence additional feature pack. .filter(not(AruPatch::isCoherenceFeaturePack)) // remove the Coherence feature pack, if returned .filter(p -> p.release().equals(releaseNumber)) @@ -487,7 +513,7 @@ public Stream getPatches(String bugNumber, String userId, String passw throws AruException, IOException, XPathExpressionException { if (userId == null || password == null) { - return getPatchesOffline(bugNumber).stream(); + return ConfigManager.getInstance().getAruPatchForBugNumber(bugNumber).stream(); } String url = String.format(BUG_SEARCH_URL, bugNumber); @@ -502,31 +528,6 @@ public Stream getPatches(String bugNumber, String userId, String passw } } - private List getPatchesOffline(String bugNumber) throws CacheStoreException { - List patchesInCache = new ArrayList<>(); - // Cache keys are in the form {bug number}_{version} or {bug number}_{version}_{architecture} - Pattern pattern = Pattern.compile("^([^_]+)_([^_]+)(?:_(.+))?$"); - // Get a list of patches in the cache for the given bug number - for (String patchId: CacheStoreFactory.cache().getKeysForType(bugNumber)) { - AruPatch patch = new AruPatch(); - Matcher matcher = pattern.matcher(patchId); - if (matcher.find() && matcher.groupCount() < 2) { - logger.fine("While getting patches for {0}, discarded bad cache key {1}", bugNumber, patchId); - } - patch.patchId(matcher.group(1)); - patch.version(matcher.group(2)); - if (matcher.groupCount() == 3 && matcher.group(3) != null) { - int aruPlatform = Architecture.fromString(matcher.group(3)).getAruPlatform(); - patch.platform(Integer.toString(aruPlatform)); - } else { - // architecture was not specified in the cache key, assume generic platform - patch.platform("2000"); - } - patch.description("description unavailable while working offline"); - patchesInCache.add(patch); - } - return patchesInCache; - } /** * Download a patch file from ARU. @@ -623,5 +624,44 @@ private static T retry(MethodToRetry call) throws AruException, RetryFail // When all retries are exhausted, raise an ARU exception to exit the process (give up) throw logger.throwing(new RetryFailedException()); } + + /** + * Get the translated aru platform. + * @param id from aru id + * @return text string fromm id number + */ + public static String getAruPlatformName(String id) { + if ("2000".equals(id)) { + return GENERIC_NAME; + } + if ("541".equals(id)) { + return ARM_NAME; + } + if ("226".equals(id)) { + return AMD_NAME; + } + + return GENERIC_NAME; + } + + /** + * Get the translated aru platform. + * @param id from aru platform string + * @return platform number + */ + public static String getAruPlatformId(String id) { + if (GENERIC_NAME.equalsIgnoreCase(id)) { + return "2000"; + } + if (ARM_NAME.equals(id)) { + return "541"; + } + if (AMD_NAME.equals(id)) { + return "226"; + } + + return "2000"; + } + } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/PatchVersionException.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/PatchVersionException.java index 339fc5eac..a0f4195c9 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/PatchVersionException.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/aru/PatchVersionException.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.util.List; -import java.util.stream.Collectors; import com.oracle.weblogic.imagetool.util.Utils; @@ -17,11 +16,7 @@ public class PatchVersionException extends IOException { * @param bugNumber the bug number that was searched * @param versionsAvailable the list of versions for patches of that bug */ - public PatchVersionException(String bugNumber, List versionsAvailable) { - super(Utils.getMessage("IMG-0034", bugNumber, - versionsAvailable.stream() - .map(s -> bugNumber + "_" + s.version()) - .collect(Collectors.joining(", "))) - ); + public PatchVersionException(String bugNumber, List versionsAvailable) { + super(Utils.getMessage("IMG-0034", bugNumber, versionsAvailable)); } } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/AbstractCommand.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/AbstractCommand.java new file mode 100644 index 000000000..b1428f63e --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/AbstractCommand.java @@ -0,0 +1,139 @@ +// 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 com.oracle.weblogic.imagetool.builder; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.util.CloseableList; +import com.oracle.weblogic.imagetool.util.Utils; + +public abstract class AbstractCommand { + private static final LoggingFacade logger = LoggingFactory.getLogger(AbstractCommand.class); + + public abstract List getCommand(boolean showPasswords); + + /** + * Executes the given docker command and writes the process stdout to log. + * + * @param dockerLog log file to write to + * @throws IOException if an error occurs reading from the process inputstream. + * @throws InterruptedException when the process wait is interrupted. + */ + public void run(Path dockerLog) + throws IOException, InterruptedException { + // process builder + logger.entering(getCommand(false), dockerLog); + Path dockerLogPath = createFile(dockerLog); + logger.finer("Docker log: {0}", dockerLogPath); + + if (dockerLogPath != null) { + logger.info("dockerLog: " + dockerLog); + } + + ProcessBuilder processBuilder = new ProcessBuilder(getCommand(true)); + processBuilder.redirectErrorStream(true); + logger.finer("Starting docker process..."); + final Process process = processBuilder.start(); + logger.finer("Docker process started"); + Thread readerThread = writeFromInputToOutputStreams(process.getInputStream(), dockerLogPath); + logger.finer("Waiting for Docker to finish"); + if (process.waitFor() != 0) { + Utils.processError(process); + } + readerThread.join(); + } + + /** + * Create a file with the given path. + * + * @param filePath the path of the file to create + * @return file path or null in case of error + */ + private Path createFile(Path filePath) { + Path logFilePath = filePath; + if (logFilePath != null) { + try { + if (!Files.exists(logFilePath)) { + Files.createDirectories(logFilePath.getParent()); + Files.createFile(logFilePath); + } else { + if (Files.isDirectory(logFilePath)) { + logFilePath = Paths.get(logFilePath.toAbsolutePath().toString(), "dockerbuild.log"); + if (Files.exists(logFilePath)) { + Files.delete(logFilePath); + } + Files.createFile(logFilePath); + } + } + } catch (IOException e) { + logger.fine("Failed to create log file for the build command", e); + logFilePath = null; + } + } + return logFilePath; + } + + private Thread writeFromInputToOutputStreams(InputStream inputStream, Path dockerLogPath) { + Thread readerThread = new Thread(() -> { + BufferedReader processReader = new BufferedReader(new InputStreamReader(inputStream)); + PrintWriter stdoutWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)), + true); + PrintWriter logWriter = null; + OutputStream fileOutputStream = null; + + try { + if (dockerLogPath != null) { + fileOutputStream = Files.newOutputStream(dockerLogPath); + logWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(fileOutputStream)), + true); + } + String line; + while ((line = processReader.readLine()) != null) { + if (logWriter != null) { + logWriter.println(line); + } + stdoutWriter.println(line); + } + } catch (IOException e) { + logger.severe(e.getMessage()); + } finally { + try { + processReader.close(); + if (logWriter != null) { + logWriter.close(); + } + if (fileOutputStream != null) { + fileOutputStream.close(); + } + } catch (IOException ioe) { + logger.finest(ioe.getMessage()); + } + } + }); + readerThread.start(); + return readerThread; + } + + private CloseableList createPrintWriters(List outputStreams) { + CloseableList retVal = new CloseableList<>(); + for (OutputStream outputStream : outputStreams) { + retVal.add(new PrintWriter(new OutputStreamWriter(outputStream), true)); + } + return retVal; + } + +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/BuildCommand.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/BuildCommand.java index fa55d133e..32e72810c 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/BuildCommand.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/BuildCommand.java @@ -3,16 +3,6 @@ package com.oracle.weblogic.imagetool.builder; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -20,10 +10,9 @@ import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; -import com.oracle.weblogic.imagetool.util.CloseableList; import com.oracle.weblogic.imagetool.util.Utils; -public class BuildCommand { +public class BuildCommand extends AbstractCommand { private static final LoggingFacade logger = LoggingFactory.getLogger(BuildCommand.class); private final String executable; @@ -32,6 +21,7 @@ public class BuildCommand { private List additionalOptions; private final String context; private boolean useBuildx = false; + private List buildPlatforms = new ArrayList<>(); /** * Create a build command for creating an image. At some point, it might @@ -60,19 +50,35 @@ public BuildCommand tag(String value) { return this; } + /** + * Toggle the use of the BuildKit. + * If true, the build command will start "buildx build". + * If false, the build command will start "build". + * @param value true to enable buildx + * @return this + */ + public BuildCommand useBuildx(boolean value) { + useBuildx = value; + return this; + } + /** * Add container build platform. Pass the desired * build architecture to the build process. * @param value a single platform name. * @return this */ - public BuildCommand platform(String value) { - if (Utils.isEmptyString(value)) { + public BuildCommand platform(List value) { + buildPlatforms = value; + if (value == null || value.isEmpty()) { return this; } command.add("--platform"); - command.add(value); - useBuildx = true; + command.add(String.join(",", value)); + // Only use buildx for multi platform build + if (value.size() > 1) { + useBuildx = true; + } return this; } @@ -83,7 +89,7 @@ public BuildCommand platform(String value) { * @return this */ public BuildCommand forceRm(boolean value) { - if (value) { + if (value && !useBuildx) { command.add("--force-rm"); } return this; @@ -162,98 +168,30 @@ public BuildCommand pull(boolean value) { } /** - * Executes the given docker command and writes the process stdout to log. - * - * @param dockerLog log file to write to - * @throws IOException if an error occurs reading from the process inputstream. - * @throws InterruptedException when the process wait is interrupted. + * Add a --push to the Docker build command. If value is false, return without adding the --push. + * @param value true to add the pull */ - public void run(Path dockerLog) - throws IOException, InterruptedException { - // process builder - logger.entering(getCommand(false), dockerLog); - Path dockerLogPath = createFile(dockerLog); - logger.finer("Docker log: {0}", dockerLogPath); - List outputStreams = new ArrayList<>(); - - outputStreams.add(System.out); - - if (dockerLogPath != null) { - logger.info("dockerLog: " + dockerLog); - outputStreams.add(Files.newOutputStream(dockerLogPath)); - } - - ProcessBuilder processBuilder = new ProcessBuilder(getCommand(true)); - logger.finer("Starting docker process..."); - final Process process = processBuilder.start(); - logger.finer("Docker process started"); - writeFromInputToOutputStreams(process.getInputStream(), outputStreams); - logger.finer("Waiting for Docker to finish"); - if (process.waitFor() != 0) { - Utils.processError(process); + public BuildCommand push(boolean value) { + if (value) { + command.add("--push"); } + return this; } /** - * Create a file with the given path. - * - * @param filePath the path of the file to create - * @return file path or null in case of error + * Add a --load to the Docker build command. If value is false, return without adding the --load. + * @param value true to add the pull */ - private Path createFile(Path filePath) { - Path logFilePath = filePath; - if (logFilePath != null) { - try { - if (!Files.exists(logFilePath)) { - Files.createDirectories(logFilePath.getParent()); - Files.createFile(logFilePath); - } else { - if (Files.isDirectory(logFilePath)) { - logFilePath = Paths.get(logFilePath.toAbsolutePath().toString(), "dockerbuild.log"); - if (Files.exists(logFilePath)) { - Files.delete(logFilePath); - } - Files.createFile(logFilePath); - } - } - } catch (IOException e) { - logger.fine("Failed to create log file for the build command", e); - logFilePath = null; - } + public BuildCommand load(boolean value) { + if (value) { + command.add("--load"); } - return logFilePath; - } - - private void writeFromInputToOutputStreams(InputStream inputStream, List outputStreams) { - Thread readerThread = new Thread(() -> { - try ( - BufferedReader processReader = new BufferedReader(new InputStreamReader(inputStream)); - CloseableList printWriters = createPrintWriters(outputStreams) - ) { - if (!printWriters.isEmpty()) { - String line; - while ((line = processReader.readLine()) != null) { - String finalLine = line; - printWriters.forEach(x -> x.println(finalLine)); - } - } - } catch (IOException e) { - logger.severe(e.getMessage()); - } - }); - readerThread.setDaemon(true); - readerThread.start(); + return this; } - private CloseableList createPrintWriters(List outputStreams) { - CloseableList retVal = new CloseableList<>(); - for (OutputStream outputStream : outputStreams) { - retVal.add(new PrintWriter(new OutputStreamWriter(outputStream), true)); - } - return retVal; - } - private List getCommand(boolean showPasswords) { + @Override + public List getCommand(boolean showPasswords) { List result = new ArrayList<>(); result.add(executable); if (useBuildx) { @@ -268,9 +206,75 @@ private List getCommand(boolean showPasswords) { result.addAll(arg.toList(showPasswords)); } result.add(context); + result.add("--progress=plain"); + return result; } + /** + * Return the build engine used. + * @return build engine passed or used + */ + public String getExecutable() { + return executable; + } + + /** + * Return the build platforms passed in. + * @return build platforms + */ + + public List getBuildPlatforms() { + return buildPlatforms; + } + + /** + * Substitute the platform value. + * @param platform platform value used for substitution + */ + public void substitutePlatform(String platform) { + for (int i = 0; i < command.size() - 1; i++) { + if (command.get(i).equals("--platform")) { + command.set(i + 1, platform); + } + } + } + + /** + * Substitute the tagname value. + * @param tagName platform value used for substitution + */ + public String substituteTagName(String tagName) { + for (int i = 0; i < command.size() - 1; i++) { + if (command.get(i).equals("--tag")) { + command.set(i + 1, tagName); + return command.get(i + 1); + } + } + return null; + } + + /** + * Return the tag name value. + * @return tag name + */ + public String getTagName() { + for (int i = 0; i < command.size() - 1; i++) { + if (command.get(i).equals("--tag")) { + return command.get(i + 1); + } + } + return null; + } + + /** + * Return context folder. + * @return context folder + */ + public String getContext() { + return context; + } + @Override public String toString() { return String.join(" ", getCommand(false)); diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/ManifestCommand.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/ManifestCommand.java new file mode 100644 index 000000000..f2c68d2ec --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/ManifestCommand.java @@ -0,0 +1,97 @@ +// 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 com.oracle.weblogic.imagetool.builder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.util.Utils; + +public class ManifestCommand extends AbstractCommand { + private static final LoggingFacade logger = LoggingFactory.getLogger(ManifestCommand.class); + + private final String executable; + private final List command; + + /** + * Create a manifest command for manipulating manifest. + */ + public ManifestCommand(String buildEngine, String contextFolder) { + Objects.requireNonNull(contextFolder); + executable = buildEngine; + command = new ArrayList<>(); + } + + /** + * Add manifest name for this command. + * @param value name to be used as the manifest name. + * @return this + */ + public ManifestCommand name(String value) { + if (Utils.isEmptyString(value)) { + return this; + } + command.add(value); + return this; + } + + /** + * Add manifest create command. + * @return this + */ + public ManifestCommand create() { + command.add("create"); + return this; + } + + /** + * Add manifest add command. + * @return this + */ + public ManifestCommand add() { + command.add("add"); + return this; + } + + /** + * Add Docker image tag name for this build command. + * @param value name to be used as the image tag. + * @return this + */ + public ManifestCommand tag(String value) { + if (Utils.isEmptyString(value)) { + return this; + } + command.add(value); + return this; + } + + + /** + * Add a push to the manifest command. + */ + public ManifestCommand push() { + command.add("push"); + return this; + } + + + @Override + public List getCommand(boolean showPasswords) { + List result = new ArrayList<>(); + result.add(executable); + result.add("manifest"); + result.addAll(command); + return result; + } + + @Override + public String toString() { + return String.join(" ", getCommand(false)); + } + +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/PushCommand.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/PushCommand.java new file mode 100644 index 000000000..b9651b433 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/builder/PushCommand.java @@ -0,0 +1,58 @@ +// 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 com.oracle.weblogic.imagetool.builder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.util.Utils; + +public class PushCommand extends AbstractCommand { + private static final LoggingFacade logger = LoggingFactory.getLogger(PushCommand.class); + + private final String executable; + private final List command; + + /** + * Create a manifest command for manipulating manifest. + */ + public PushCommand(String buildEngine, String contextFolder) { + Objects.requireNonNull(contextFolder); + executable = buildEngine; + command = new ArrayList<>(); + } + + + /** + * Add Docker image tag name for this build command. + * @param value name to be used as the image tag. + * @return this + */ + public PushCommand tag(String value) { + if (Utils.isEmptyString(value)) { + return this; + } + command.add(value); + return this; + } + + + @Override + public List getCommand(boolean showPasswords) { + List result = new ArrayList<>(); + result.add(executable); + result.add("push"); + result.addAll(command); + return result; + } + + @Override + public String toString() { + return String.join(" ", getCommand(false)); + } + +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/CacheStore.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/CacheStore.java index ba65441db..1cee5a995 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/CacheStore.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/CacheStore.java @@ -3,69 +3,492 @@ package com.oracle.weblogic.imagetool.cachestore; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; + +import com.oracle.weblogic.imagetool.aru.AruPatch; +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; +import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.settings.ConfigManager; +import com.oracle.weblogic.imagetool.settings.SettingsFile; +import com.oracle.weblogic.imagetool.settings.UserSettingsFile; +import com.oracle.weblogic.imagetool.util.Architecture; +import com.oracle.weblogic.imagetool.util.Utils; +import org.jetbrains.annotations.Nullable; + +import static com.oracle.weblogic.imagetool.aru.AruUtil.getAruPlatformId; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.ARCHITECTURE; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.BASE_FMW_VERSION; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.DATE_ADDED; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.DESCRIPTION; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.DIGEST; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.LOCATION; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.PATCH_VERSION; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.PRODUCT_VERSION; +import static com.oracle.weblogic.imagetool.util.Utils.getTodayDate; /** - * This is the interface that helps keep track of application metadata like + * This is the helper class that helps keep track of application metadata like * which patches have been downloaded and their location on disk. */ -public interface CacheStore { +public class CacheStore { + + public static final String CACHE_KEY_SEPARATOR = "_"; + public static final String CACHE_DIR_ENV = "WLSIMG_CACHEDIR"; + private static final LoggingFacade logger = LoggingFactory.getLogger(UserSettingsFile.class); - String CACHE_KEY_SEPARATOR = "_"; /** - * Cache dir used by this application. cache dir is where the application downloads - * artifacts to. - * - * @return cache dir location on disk + * Return all the installers based on the configured directory for the yaml file. + * @return map of installers */ - String getCacheDir(); + public Map>> getInstallers() { + + + Map allInstallers = new SettingsFile( + Paths.get(ConfigManager.getInstance().getInstallerDetailsFile())).load(); + + if (allInstallers == null) { + return new HashMap<>(); + } + + Map>> installerDetails = new HashMap<>(); + + allInstallers.forEach((key, value) -> { + if (key != null && !key.isEmpty()) { + String upperKey = key.toUpperCase(); // Convert key to uppercase (e.g., jdk, wls, fmw) + try { + @SuppressWarnings("unchecked") + Map installerValues = (Map) value; + + Map> installerMetaData = new HashMap<>(); + + installerValues.forEach((individualKey, individualValue) -> { + List metaDataList = new ArrayList<>(); + + if (individualValue instanceof List) { + @SuppressWarnings("unchecked") + List installerList = (List) individualValue; + installerList.forEach(installerValue -> + metaDataList.add(createInstallerMetaData((Map) installerValue))); + } else { + metaDataList.add(createInstallerMetaData((Map) individualValue)); + } + + installerMetaData.put(individualKey, metaDataList); + }); + + installerDetails.put(upperKey, installerMetaData); + + } catch (IllegalArgumentException e) { + logger.warning("{0} could not be loaded: {1}", upperKey, InstallerType.class.getEnumConstants()); + } + } + }); + + return installerDetails; + + } + /** - * Returns the value if key is present. Since the application tracks downloaded artifact location, - * key is usually patch number or artifact identifier. Value is location on disk. - * - * @param key key to look for. Ex: patch number - * @return value if present in cache or else null. + * Add installer to local file. + * @param installerType installer type + * @param commonName common name used + * @param metaData installer metadata */ - String getValueFromCache(String key); + public void addInstaller(InstallerType installerType, String commonName, InstallerMetaData metaData) + throws IOException { + + Map>> installerDetails = getInstallers(); + Map> installerMetaDataMap; + List installerMetaDataList; + String installerKey = installerType.toString().toUpperCase(); + + if (installerDetails.containsKey(installerKey)) { + installerMetaDataMap = installerDetails.get(installerKey); + } else { + installerDetails.put(installerKey, new HashMap<>()); + installerMetaDataMap = installerDetails.get(installerKey); + } + + if (installerMetaDataMap.containsKey(commonName)) { + installerMetaDataList = installerMetaDataMap.get(commonName); + } else { + installerMetaDataMap.put(commonName, new ArrayList<>()); + installerMetaDataList = installerMetaDataMap.get(commonName); + } + // Before adding see if one same already existed. + if (installerMetaDataList.contains(metaData)) { + logger.info("IMG-0135", metaData.toString()); + } else { + installerMetaDataList.add(metaData); + } + + if (!baseFMWVersionExists(installerType, installerDetails, metaData.getBaseFMWVersion())) { + logger.severe("IMG-0149", metaData.getBaseFMWVersion(), metaData.getProductVersion(), + installerType.toString().toUpperCase()); + System.exit(2); + } + + if (!Utils.isBaseInstallerType(installerType) && metaData.getBaseFMWVersion() == null) { + metaData.setBaseFMWVersion(metaData.getProductVersion()); + } + + // Update the list + installerDetails.put(installerKey, installerMetaDataMap); + saveAllInstallers(installerDetails); + } + + private boolean baseFMWVersionExists(InstallerType type, Map>> installerDetails, String baseFMWVersion) { + + if (!Utils.isBaseInstallerType(type)) { + if (baseFMWVersion == null) { + return true; + } else { + Map> installers = installerDetails.get( + InstallerType.FMW.toString().toUpperCase()); + return installers != null && installers.containsKey(baseFMWVersion); + } + } else { + return true; + } + } /** - * Checks if cache contains the specified key. - * - * @param key artifact identifier - * @return true if found + * Return the metadata for the platformed installer. + * @param installerType installer type + * @param platformName platform name + * @param installerVersion version of the installer + * @return InstallerMetaData meta data for the installer */ - boolean containsKey(String key); + + public InstallerMetaData getInstallerForPlatform(InstallerType installerType, Architecture platformName, + String installerVersion, String commonName) { + + + platformName = (platformName == null) ? Architecture.GENERIC : platformName; + installerType = (installerType == null) ? InstallerType.WLS : installerType; + + String installerKey = installerType.toString().toUpperCase(); + installerVersion = verifyInstallerVersion(installerVersion, installerType); + + Map> installers = getInstallers().get(installerKey); + if (installers != null && !installers.isEmpty()) { + return getInstallerMetaData(installerVersion, installerType, platformName, installers, commonName); + } + return null; + + } /** - * Add an entry to the cache metadata file. - * - * @param key artifact identifier - * @param value a file path + * Return the metadata for the platformed installer. + * @param installerType installer type + * @param commonName common name + * @return InstallerMetaData meta data for the installer */ - void addToCache(String key, String value) throws CacheStoreException; + + public List listInstallerByCommonName(InstallerType installerType, String commonName) { + + + installerType = (installerType == null) ? InstallerType.WLS : installerType; + + String installerKey = installerType.toString().toUpperCase(); + + Map> installers = getInstallers().get(installerKey); + if (installers != null && !installers.isEmpty()) { + for (String key : installers.keySet()) { + if (key.equals(commonName)) { + return installers.get(key); + } + } + } + return null; + } /** - * Delete an entry from the cache. - * - * @param key key corresponding to an entry in the cache - * @return value or null + * Add a patch to the local system. + * @param bugNumber bug number + * @param patchArchitecture architecture of the patch + * @param patchLocation file location of the patch + * @param patchVersion version of the patch + * @throws IOException error */ - String deleteFromCache(String key) throws CacheStoreException; + public void addPatch(String bugNumber, String patchArchitecture, String patchLocation, + String patchVersion, String description) throws IOException { + ConfigManager configManager = ConfigManager.getInstance(); + Map> patches = configManager.getAllPatches(); + List latestPatches = patches.get(bugNumber); + if (latestPatches == null) { + latestPatches = new ArrayList<>(); + } + PatchMetaData newPatch = new PatchMetaData(patchArchitecture, patchLocation, patchVersion, description); + if (latestPatches.contains(newPatch)) { + logger.info("IMG-0136", newPatch); + } else { + latestPatches.add(newPatch); + } + patches.put(bugNumber, latestPatches); + configManager.saveAllPatches(patches); + } /** - * Delete all entries from the cache. + * Add a patch to the local system. + * @param bugNumber bug number + * @param patchArchitecture architecture of the patch + * @param patchLocation file location of the patch + * @param patchVersion version of the patch + * @throws IOException error */ - void clearCache() throws CacheStoreException; + public void addPatch(String bugNumber, String patchArchitecture, String patchLocation, + String patchVersion, String description, String dateAdded) throws IOException { + ConfigManager configManager = ConfigManager.getInstance(); + Map> patches = configManager.getAllPatches(); + List latestPatches = patches.get(bugNumber); + if (latestPatches == null) { + latestPatches = new ArrayList<>(); + } + PatchMetaData newPatch = new PatchMetaData(patchArchitecture, patchLocation, patchVersion, description, + dateAdded); + if (latestPatches.contains(newPatch)) { + logger.info("IMG-0136", newPatch.toString()); + } else { + latestPatches.add(newPatch); + } + patches.put(bugNumber, latestPatches); + configManager.saveAllPatches(patches); + } /** - * Returns a map of current items in the cache. - * - * @return map of current items + * Return the metadata for the platformed installer. + * @param platformName platform name + * @param bugNumber version of the installer + * @return InstallerMetaData meta data for the installer */ - Map getCacheItems(); - List getKeysForType(String type); + public PatchMetaData getPatchForPlatform(String platformName, String bugNumber, String version) { + Map> patches = getAllPatches(); + if (patches != null && !patches.isEmpty()) { + return getPatchMetaData(platformName, bugNumber, version, patches); + } + return null; + } + + private static @Nullable PatchMetaData getPatchMetaData(String platformName, String bugNumber, String version, + Map> patches) { + + List patchMetaDataList = patches.get(bugNumber); + if (patchMetaDataList == null || patchMetaDataList.isEmpty()) { + return null; + } + + for (PatchMetaData patchMetaData : patchMetaDataList) { + boolean isPlatformMatch = (platformName == null || platformName.isEmpty()) + ? "Generic".equalsIgnoreCase(patchMetaData.getArchitecture()) + : platformName.equalsIgnoreCase(patchMetaData.getArchitecture()); + + if (isPlatformMatch && patchMetaData.getPatchVersion().equals(version)) { + return patchMetaData; + } + } + + // Fallback to search for "Generic" for OPatchFile's default bug number + if (OPatchFile.DEFAULT_BUG_NUM.equals(bugNumber)) { + return patchMetaDataList.stream() + .filter(patchMetaData -> "Generic".equalsIgnoreCase(patchMetaData.getArchitecture())) + .findFirst() + .orElse(null); + } + + return null; + } + + /** + * Return the list of AruPatch data from for the patches by bug number from local. + * @param bugNumber version of the installer + * @return list of AruPatch + */ + + public List getAruPatchForBugNumber(String bugNumber) { + Map> patches = getAllPatches(); + List aruPatchList = new ArrayList<>(); + if (patches != null && !patches.isEmpty()) { + List resultPatchMetaDataList = patches.get(bugNumber); + if (resultPatchMetaDataList != null && !resultPatchMetaDataList.isEmpty()) { + for (PatchMetaData patchMetaData: resultPatchMetaDataList) { + AruPatch aruPatch = new AruPatch(); + + aruPatch.platformName(patchMetaData.getArchitecture()) + .platform(getAruPlatformId(patchMetaData.getArchitecture())) + .patchId(bugNumber) + .fileName(patchMetaData.getLocation()) + .version(patchMetaData.getPatchVersion()); + aruPatchList.add(aruPatch); + } + } + } + + return aruPatchList; + } + + /** + * return all the patches. + * @return patch settings + */ + public Map> getAllPatches() { + Map allPatches = new SettingsFile( + Paths.get(ConfigManager.getInstance().getPatchDetailsFile())).load(); + + if (allPatches == null || allPatches.isEmpty()) { + return new HashMap<>(); + } + + Map> patchList = new HashMap<>(); + + allPatches.forEach((key, value) -> { + if (key != null) { + List patchMetaDataList = new ArrayList<>(); + + if (value instanceof List) { + @SuppressWarnings("unchecked") + List valueList = (List) value; + valueList.forEach(item -> + patchMetaDataList.add(createPatchMetaData((Map) item))); + } else { + patchMetaDataList.add(createPatchMetaData((Map) value)); + } + + patchList.put(key, patchMetaDataList); + } + }); + + return patchList; + + } + + /** + * Save all patches in the local metadata file. + * @param allPatches Map of all patch metadata + * @throws IOException when error + */ + public void saveAllPatches(Map> allPatches) throws IOException { + + ConfigManager configManager = ConfigManager.getInstance(); + configManager.getPatchSettingsFile().save(allPatches); + + } + + /** + * Save all installers in the local metadata file. + * @param allInstallers Map of all installers metadata + * @throws IOException when error + */ + public void saveAllInstallers(Map>> allInstallers) throws IOException { + + ConfigManager.getInstance().getInstallerSettingsFile().save(allInstallers); + } + + + private InstallerMetaData createInstallerMetaData(Map objectData) { + String hash = (String) objectData.get(DIGEST); + String dateAdded = (String) objectData.get(DATE_ADDED); + if (dateAdded == null) { + dateAdded = getTodayDate(); + } + String location = (String) objectData.get(LOCATION); + String productVersion = (String) objectData.get(PRODUCT_VERSION); + String platform = (String) objectData.get(ARCHITECTURE); + String baseFMWVersion = (String) objectData.get(BASE_FMW_VERSION); + return new InstallerMetaData(platform, location, hash, dateAdded, productVersion, baseFMWVersion); + } + + private PatchMetaData createPatchMetaData(Map objectData) { + String hash = (String) objectData.get(DIGEST); + String dateAdded = (String) objectData.get(DATE_ADDED); + if (dateAdded == null) { + dateAdded = getTodayDate(); + } + String location = (String) objectData.get(LOCATION); + String productVersion = (String) objectData.get(PATCH_VERSION); + String platform = (String) objectData.get(ARCHITECTURE); + String description = (String) objectData.get(DESCRIPTION); + return new PatchMetaData(platform, location, hash, dateAdded, productVersion, description); + } + + private String verifyInstallerVersion(String installerVersion, InstallerType installerType) { + + if (installerVersion == null) { + switch (installerType) { + case WLS: + installerVersion = ConfigManager.getInstance().getDefaultWLSVersion(); + break; + case JDK: + installerVersion = ConfigManager.getInstance().getDefaultJDKVersion(); + break; + case WDT: + installerVersion = ConfigManager.getInstance().getDefaultWDTVersion(); + break; + default: + break; + } + if (installerVersion == null) { + logger.throwing(new IllegalArgumentException("Cannot determine installer version for installer type " + + installerType.toString())); + } + } + return installerVersion; + } + + + private InstallerMetaData getInstallerMetaData(String installerVersion, InstallerType installerType, + Architecture platformName, Map> installers, String commonName) { + String search = installerVersion; + + if (commonName != null) { + search = commonName; + } + List installerMetaDataList = installers.get(search); + + if (installerMetaDataList != null && !installerMetaDataList.isEmpty()) { + logger.info("IMG-0151", installerType, installerVersion, platformName); + Optional foundInstaller = installerMetaDataList.stream() + .filter(installerMetaData -> platformName.getAcceptableNames() + .contains(installerMetaData.getArchitecture())) + .filter(installerMetaData -> + installerMetaData.getProductVersion().equals(installerVersion)) + .findFirst(); + + if (foundInstaller.isPresent()) { + return foundInstaller.get(); + } + + if (Utils.isGenericInstallerAcceptable(installerType)) { + //If it can't find the specialized platform, try generic. + logger.info("IMG-0152", installerType, installerVersion, platformName); + + foundInstaller = installerMetaDataList.stream() + .filter(installerMetaData -> Architecture.GENERIC.getAcceptableNames() + .contains(installerMetaData.getArchitecture())) + .filter(installerMetaData -> + installerMetaData.getProductVersion().equals(installerVersion)) + .findFirst(); + + if (foundInstaller.isPresent()) { + return foundInstaller.get(); + } + + } + } + return null; + } } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/CacheStoreFactory.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/CacheStoreFactory.java deleted file mode 100644 index 189279962..000000000 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/CacheStoreFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2019, 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 com.oracle.weblogic.imagetool.cachestore; - -/** - * Provides access to a Cache Store. - */ -public class CacheStoreFactory { - - private static CacheStore store; - - private CacheStoreFactory() { - // hidden constructor - } - - /** - * Get the cache store. - * @return the cached instance of the file cache store - */ - public static CacheStore cache() throws CacheStoreException { - if (store == null) { - store = new FileCacheStore(); - } - - return store; - } -} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/FileCacheStore.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/FileCacheStore.java deleted file mode 100644 index e7d65524b..000000000 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/FileCacheStore.java +++ /dev/null @@ -1,184 +0,0 @@ -// 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 com.oracle.weblogic.imagetool.cachestore; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Properties; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.oracle.weblogic.imagetool.logging.LoggingFacade; -import com.oracle.weblogic.imagetool.logging.LoggingFactory; -import com.oracle.weblogic.imagetool.util.Constants; -import com.oracle.weblogic.imagetool.util.Utils; - -public class FileCacheStore implements CacheStore { - - public static final String CACHE_DIR_ENV = "WLSIMG_CACHEDIR"; - private static final LoggingFacade logger = LoggingFactory.getLogger(FileCacheStore.class); - - private final Properties properties = new Properties(); - private final File metadataFile; - private final String cacheDir; - - FileCacheStore() throws CacheStoreException { - try { - cacheDir = initCacheDir(); - metadataFile = Paths.get(cacheDir, Constants.DEFAULT_META_FILE).toFile(); - if (metadataFile.exists() && metadataFile.isFile()) { - loadProperties(metadataFile); - } else { - if (!metadataFile.createNewFile()) { - throw new IOException("Failed to create file cache metadata file " + metadataFile.getName()); - } - } - } catch (IOException e) { - CacheStoreException error = - new CacheStoreException("Failed to establish a cache store on the filesystem", e); - logger.throwing(error); - throw error; - } - } - - @Override - public String getCacheDir() { - return cacheDir; - } - - @Override - public String getValueFromCache(String key) { - Objects.requireNonNull(key, Utils.getMessage("IMG-0066")); - return properties.getProperty(key.toLowerCase()); - } - - @Override - public boolean containsKey(String key) { - if (key == null) { - return false; - } - return properties.containsKey(key.toLowerCase()); - } - - @Override - public void addToCache(String key, String value) throws CacheStoreException { - Objects.requireNonNull(key, Utils.getMessage("IMG-0066")); - Objects.requireNonNull(value, "Cache item value cannot be null"); - properties.put(key.toLowerCase(), value); - persistToDisk(); - } - - @Override - public String deleteFromCache(String key) throws CacheStoreException { - Objects.requireNonNull(key, Utils.getMessage("IMG-0066")); - String oldValue = (String) properties.remove(key.toLowerCase()); - if (oldValue != null) { - persistToDisk(); - } - return oldValue; - } - - @Override - public void clearCache() throws CacheStoreException { - properties.clear(); - persistToDisk(); - } - - @Override - public Map getCacheItems() { - Stream> stream = properties.entrySet().stream(); - return stream.collect(Collectors.toMap( - e -> String.valueOf(e.getKey()), - e -> String.valueOf(e.getValue()))); - } - - /** - * Find the cache keys for entries that match the provided type. - * Cache keys are of the form type_version_architecture. - * @param type The patch type like "wls" or for bugs "12355678" - * @return a list of cache keys that start with the provided string - */ - @Override - public List getKeysForType(String type) { - return properties.keySet().stream() - .map(Object::toString) - .filter(k -> k.startsWith(type)) - .collect(Collectors.toList()); - } - - private void persistToDisk() throws CacheStoreException { - logger.entering(); - synchronized (properties) { - try (FileOutputStream outputStream = new FileOutputStream(metadataFile)) { - properties.store(outputStream, "changed on:" + LocalDateTime.now()); - } catch (IOException e) { - CacheStoreException error = new CacheStoreException("Could not persist cache file", e); - logger.throwing(error); - throw error; - } - } - logger.exiting(); - } - - private void loadProperties(File propsFile) { - logger.entering(); - try (BufferedReader bufferedReader = new BufferedReader(new FileReader(propsFile))) { - if (properties.isEmpty()) { - properties.load(bufferedReader); - } else { - Properties tmpProperties = new Properties(); - tmpProperties.load(bufferedReader); - tmpProperties.forEach((key, value) -> properties.put(((String) key).toLowerCase(), value)); - } - } catch (IOException e) { - // it is okay to fail, the constructor will attempt to create a new one - logger.fine("Failed to load properties file", e); - } - logger.exiting(); - } - - private static String defaultCacheDir() { - return System.getProperty("user.home") + File.separator + "cache"; - } - - String getCacheDirSetting() { - return Utils.getEnvironmentProperty(CACHE_DIR_ENV, FileCacheStore::defaultCacheDir); - } - - /** - * Initialize the cache store directory. - * - * @return cache directory - */ - private String initCacheDir() throws IOException { - String cacheDirStr = getCacheDirSetting(); - Path cacheDirectory = Paths.get(cacheDirStr); - - boolean pathExists = Files.exists(cacheDirectory, LinkOption.NOFOLLOW_LINKS); - - if (!pathExists) { - Files.createDirectory(cacheDirectory); - } else { - if (!Files.isDirectory(cacheDirectory)) { - throw new IOException("Cache Directory specified is not a directory " + cacheDirStr); - } - if (!Files.isWritable(cacheDirectory)) { - throw new IOException("Cache Directory specified is not writable " + cacheDirStr); - } - } - - return cacheDirStr; - } -} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/OPatchFile.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/OPatchFile.java index 71aef3502..eec726857 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/OPatchFile.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/OPatchFile.java @@ -7,7 +7,8 @@ import java.io.IOException; import java.util.Comparator; import java.util.List; -import java.util.Set; +import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import javax.xml.xpath.XPathExpressionException; @@ -18,6 +19,8 @@ import com.oracle.weblogic.imagetool.aru.VersionNotFoundException; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import com.oracle.weblogic.imagetool.util.Utils; public class OPatchFile extends PatchFile { @@ -26,21 +29,20 @@ public class OPatchFile extends PatchFile { public static final String DEFAULT_BUG_NUM = "28186730"; + /** * Create an abstract OPatch file to help resolve the local file or download the remote patch. * * @param patchId bug number and optional version * @param userid the username to use for retrieving the patch * @param password the password to use with the userId to retrieve the patch - * @param cache the local cache used for patch storage * @return an abstract OPatch file */ - public static OPatchFile getInstance(String patchId, String userid, String password, CacheStore cache) + public static OPatchFile getInstance(String patchId, String providedVersion, String userid, String password) throws AruException, XPathExpressionException, IOException { logger.entering(patchId); String patchNumber = patchId; - String providedVersion = null; if (patchId == null) { // if the user did not provide a patch number, assume the default OPatch bug number patchNumber = DEFAULT_BUG_NUM; @@ -54,13 +56,21 @@ public static OPatchFile getInstance(String patchId, String userid, String passw AruPatch selectedPatch; if (isOffline(userid, password)) { - selectedPatch = getAruPatchOffline(patchNumber, providedVersion, cache); + selectedPatch = getAruPatchOffline(patchNumber, providedVersion); } else { try { selectedPatch = getAruPatchOnline(patchNumber, providedVersion, userid, password); } catch (VersionNotFoundException notFound) { // Could not find the user requested OPatch version on ARU, checking local cache before giving up - if (cache.containsKey(patchId)) { + Map> patchSettings = ConfigManager.getInstance().getAllPatches(); + final String searchVersion = providedVersion; + Optional p = patchSettings.entrySet().stream() + .filter(e -> e.getValue().stream() + .anyMatch(m -> m.getPatchVersion().equalsIgnoreCase(searchVersion))) + .map(Map.Entry::getKey) + .findFirst(); + + if (p.isPresent()) { logger.info("OPatch version {0} is not available online, using cached copy.", providedVersion); selectedPatch = new AruPatch().patchId(patchNumber).version(providedVersion); } else { @@ -74,16 +84,29 @@ public static OPatchFile getInstance(String patchId, String userid, String passw return new OPatchFile(selectedPatch, userid, password); } + /** + * Create an abstract OPatch file to help resolve the local file or download the remote patch. + * + * @param patchId bug number and optional version + * @param userid the username to use for retrieving the patch + * @param password the password to use with the userId to retrieve the patch + * @return an abstract OPatch file + */ + public static OPatchFile getInstance(String patchId, String userid, String password) + throws AruException, XPathExpressionException, IOException { + return getInstance(patchId, null, userid, password); + } + private static boolean isOffline(String userid, String password) { return userid == null || password == null; } - private static AruPatch getAruPatchOffline(String patchNumber, String providedVersion, CacheStore cache) { + private static AruPatch getAruPatchOffline(String patchNumber, String providedVersion) { // if working offline, update the placeholder in the list to have the provided version or the latest cached AruPatch offlinePatch = new AruPatch().patchId(patchNumber); if (Utils.isEmptyString(providedVersion)) { // user did not request a specific version, find the latest version of OPatch in the cache - offlinePatch.version(getLatestCachedVersion(cache, patchNumber)); + offlinePatch.version(getLatestCachedVersion(patchNumber)); } else { offlinePatch.version(providedVersion); } @@ -94,7 +117,6 @@ private static AruPatch getAruPatchOffline(String patchNumber, String providedVe private static AruPatch getAruPatchOnline(String patchNumber, String providedVersion, String userid, String password) throws XPathExpressionException, IOException, AruException { - List patches = AruUtil.rest().getPatches(patchNumber, userid, password) .filter(p -> p.isApplicableToTarget(2000)) // OPatch is always platform 2000/generic .filter(AruPatch::isOpenAccess) // filter ARU results based on access flag (discard protected versions) @@ -132,22 +154,17 @@ private OPatchFile(AruPatch patch, String userId, String password) { super(patch, userId, password); } - private static String getLatestCachedVersion(CacheStore cache, String patchId) { + private static String getLatestCachedVersion(String patchId) { String latestVersion = "0.0.0.0.0"; - Set keys = cache.getCacheItems().keySet(); - for (String key : keys) { - if (key.startsWith(patchId)) { - logger.fine("found OPatch entry in cache {0}", key); - int split = key.indexOf('_'); - if (split < 0) { - continue; - } - String cacheVersion = key.substring(split + 1); - if (Utils.compareVersions(latestVersion, cacheVersion) < 0) { - logger.fine("using cache {0} as newer OPatch version instead of {1}", key, latestVersion); - latestVersion = cacheVersion; - } + Map> patchList = ConfigManager.getInstance().getAllPatches(); + List patchMetaDataList = patchList.get(patchId); + for (PatchMetaData patchMetaData : patchMetaDataList) { + String cacheVersion = patchMetaData.getPatchVersion(); + if (Utils.compareVersions(latestVersion, cacheVersion) < 0) { + logger.fine("Found a later version {0}", cacheVersion); + latestVersion = cacheVersion; } + } return latestVersion; } @@ -165,12 +182,20 @@ public static boolean isOPatchPatch(String patchId) { return patchId != null && patchId.startsWith(DEFAULT_BUG_NUM + CacheStore.CACHE_KEY_SEPARATOR); } - @Override - public String resolve(CacheStore cacheStore) throws IOException { + /** + * resolve. + * @return string + * @throws IOException error + */ + public String resolve() throws IOException { try { - return super.resolve(cacheStore); + return super.resolve(); } catch (FileNotFoundException fnfe) { - throw new FileNotFoundException(Utils.getMessage("IMG-0062")); + throw new FileNotFoundException(Utils.getMessage("IMG-0062", this.getVersion())); } } + + public String getVersion() { + return super.getVersion(); + } } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/PatchFile.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/PatchFile.java index 3d40194fa..14980c928 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/PatchFile.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cachestore/PatchFile.java @@ -5,93 +5,132 @@ import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; -import com.oracle.weblogic.imagetool.api.model.CachedFile; import com.oracle.weblogic.imagetool.aru.AruPatch; import com.oracle.weblogic.imagetool.aru.AruUtil; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; -import com.oracle.weblogic.imagetool.util.Architecture; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import com.oracle.weblogic.imagetool.util.Utils; -public class PatchFile extends CachedFile { +import static com.oracle.weblogic.imagetool.util.Utils.getSha256Hash; + +public class PatchFile { private static final LoggingFacade logger = LoggingFactory.getLogger(PatchFile.class); - private final AruPatch aruPatch; private final String userId; private final String password; + private final AruPatch aruPatch; /** * Create an abstract file to hold the metadata for a patch file. * - * @param aruPatch Patch metadata from ARU * @param userId the username to use for retrieving the patch * @param password the password to use with the userId to retrieve the patch */ public PatchFile(AruPatch aruPatch, String userId, String password) { - super(aruPatch.patchId(), aruPatch.version(), Architecture.fromAruPlatform(aruPatch.platform())); this.aruPatch = aruPatch; this.userId = userId; this.password = password; - - if (Utils.isEmptyString(aruPatch.patchId())) { - throw new IllegalArgumentException(Utils.getMessage("IMG-0058", aruPatch.patchId())); - } - } - - @Override - public String getVersion() { - return aruPatch.version(); - } - - void setVersion(String value) { - aruPatch.version(value); } private boolean offlineMode() { return userId == null || password == null; } - @Override - public String resolve(CacheStore cacheStore) throws IOException { - String key = getKey(); - logger.entering(key); - String filePath; - + /** + * download patch. + * @param aruPatch patch + * @param configManager configuration manager + * @return filname path of the patch downloaded + * @throws IOException error + */ + public String downloadPatch(AruPatch aruPatch, ConfigManager configManager) throws IOException { + String filename = AruUtil.rest().downloadAruPatch(aruPatch, configManager.getPatchDirectory(), + userId, password); + String hashString = getSha256Hash(filename); + if (aruPatch.sha256Hash() != null && !aruPatch.sha256Hash().isEmpty() + && !hashString.equals(aruPatch.sha256Hash())) { + throw new IOException(String.format("Patch file hash mismatch local: %s aru: %s", + hashString, aruPatch.sha256Hash())); + } try { - filePath = super.resolve(cacheStore); - logger.info("IMG-0017", key, filePath); - } catch (FileNotFoundException fnfe) { - logger.info("IMG-0061", key, aruPatch.patchId()); - if (offlineMode()) { - throw new FileNotFoundException(Utils.getMessage("IMG-0056", key)); + Map> allPatches = configManager.getAllPatches(); + List patches; + if (allPatches.containsKey(aruPatch.patchId())) { + patches = allPatches.get(aruPatch.patchId()); + patches.add(new PatchMetaData(aruPatch.platformName(), + filename, + aruPatch.sha256Hash(), + aruPatch.releasedDate(), + aruPatch.version(), + aruPatch.description())); + allPatches.remove(aruPatch.patchId()); + allPatches.put(aruPatch.patchId(),patches); + } else { + patches = new ArrayList<>(); + patches.add(new PatchMetaData(aruPatch.platformName(), + filename, + aruPatch.sha256Hash(), + aruPatch.releasedDate(), + aruPatch.version(), + aruPatch.description())); + allPatches.put(aruPatch.patchId(),patches); } - filePath = downloadPatch(cacheStore); - } + configManager.saveAllPatches(allPatches); - logger.exiting(filePath); - return filePath; + } catch (Exception k) { + throw new IOException(k.getMessage(), k); + } + return filename; } - private String downloadPatch(CacheStore cacheStore) throws IOException { - String filename = AruUtil.rest().downloadAruPatch(aruPatch, cacheStore.getCacheDir(), userId, password); + /** + * Resolve the patch location. + * @return path of the file + * @throws IOException when file is not there + */ + public String resolve() throws IOException { + String patchId = aruPatch.patchId(); + logger.entering(patchId); + + String filePath = null; + boolean fileExists = false; + ConfigManager configManager = ConfigManager.getInstance(); + PatchMetaData patchSettings = configManager.getPatchForPlatform(aruPatch.platformName(), + aruPatch.patchId(), aruPatch.version()); + if (patchSettings != null) { + filePath = patchSettings.getLocation(); + fileExists = isFileOnDisk(filePath); + } - // after downloading the file, update the cache metadata - String patchKey = getKey(); - logger.info("IMG-0060", patchKey, filename); - cacheStore.addToCache(patchKey, filename); - String filePath = cacheStore.getValueFromCache(patchKey); + if (fileExists) { + logger.info("IMG-0017", patchId, filePath); + } else { + logger.info("IMG-0061", patchId, aruPatch.patchId()); - if (!isFileOnDisk(filePath)) { - throw new FileNotFoundException(Utils.getMessage("IMG-0037", aruPatch.patchId(), getVersion())); + if (offlineMode()) { + throw new FileNotFoundException(Utils.getMessage("IMG-0056", patchId)); + } + filePath = downloadPatch(aruPatch, configManager); } + logger.exiting(filePath); return filePath; } - @Override - public String toString() { - return getKey(); + public static boolean isFileOnDisk(String filePath) { + return filePath != null && Files.isRegularFile(Paths.get(filePath)); + } + + public String getVersion() { + return aruPatch.version(); } } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/ImageTool.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/ImageTool.java index bbdf084b7..181a1ae9d 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/ImageTool.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/ImageTool.java @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2021, Oracle and/or its affiliates. +// Copyright (c) 2019, 2023, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package com.oracle.weblogic.imagetool.cli; @@ -7,10 +7,10 @@ import com.oracle.weblogic.imagetool.api.model.CommandResponse; import com.oracle.weblogic.imagetool.cli.cache.CacheCLI; +import com.oracle.weblogic.imagetool.cli.config.ConfigCommand; import com.oracle.weblogic.imagetool.cli.menu.CreateAuxImage; import com.oracle.weblogic.imagetool.cli.menu.CreateImage; import com.oracle.weblogic.imagetool.cli.menu.InspectImage; -import com.oracle.weblogic.imagetool.cli.menu.RebaseImage; import com.oracle.weblogic.imagetool.cli.menu.UpdateImage; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; @@ -29,10 +29,10 @@ sortOptions = false, subcommands = { CacheCLI.class, + ConfigCommand.class, CreateImage.class, CreateAuxImage.class, UpdateImage.class, - RebaseImage.class, InspectImage.class }, requiredOptionMarker = '*', diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/AddEntry.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/AddEntry.java deleted file mode 100644 index 810e60877..000000000 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/AddEntry.java +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2019, 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 com.oracle.weblogic.imagetool.cli.cache; - -import com.oracle.weblogic.imagetool.api.model.CommandResponse; -import com.oracle.weblogic.imagetool.cachestore.CacheStoreException; -import com.oracle.weblogic.imagetool.util.Utils; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; - -import static com.oracle.weblogic.imagetool.cachestore.CacheStoreFactory.cache; - -@Command( - name = "addEntry", - description = "Command to add a cache entry. Use caution" -) -public class AddEntry extends CacheOperation { - - @Override - public CommandResponse call() throws CacheStoreException { - if (!Utils.isEmptyString(key) && !Utils.isEmptyString(location)) { - String oldValue = cache().getValueFromCache(key); - String msg; - if (oldValue != null) { - msg = String.format("Replaced old value %s with new value %s for key %s", oldValue, location, key); - } else { - msg = String.format("Added entry %s=%s", key, location); - } - cache().addToCache(key, location); - return CommandResponse.success(msg); - } - return CommandResponse.error("IMG-0044"); - } - - @Option( - names = {"--key"}, - description = "Key for the cache entry", - required = true - ) - private String key; - - @Option( - names = {"--value"}, - description = "Value for the cache entry", - required = true - ) - private String location; -} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/AddInstallerEntry.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/AddInstallerEntry.java index 72a29eacc..7db3eac62 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/AddInstallerEntry.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/AddInstallerEntry.java @@ -3,11 +3,12 @@ package com.oracle.weblogic.imagetool.cli.cache; +import java.io.IOException; + import com.oracle.weblogic.imagetool.api.model.CommandResponse; -import com.oracle.weblogic.imagetool.cachestore.CacheStore; -import com.oracle.weblogic.imagetool.cachestore.CacheStoreException; import com.oracle.weblogic.imagetool.installer.InstallerType; import com.oracle.weblogic.imagetool.util.Architecture; +import com.oracle.weblogic.imagetool.util.Utils; import picocli.CommandLine.Command; import picocli.CommandLine.Option; @@ -19,27 +20,49 @@ public class AddInstallerEntry extends CacheAddOperation { @Override - public CommandResponse call() throws CacheStoreException { + public CommandResponse call() throws IOException { if ("NONE".equalsIgnoreCase(version)) { throw new IllegalArgumentException("IMG-0105"); } - return addToCache(); + return addInstallerToCache(); } @Override public String getKey() { - StringBuilder key = new StringBuilder(25) - .append(type) - .append(CacheStore.CACHE_KEY_SEPARATOR) - .append(version); - - if (architecture != null) { - key.append(CacheStore.CACHE_KEY_SEPARATOR) - .append(architecture); + return type.toString(); + } + + @Override + public String getVersion() { + return version; + } + + @Override + public String getCommonName() { + if (commonName == null) { + return version; } + return commonName; + } - return key.toString(); + @Override + public Architecture getArchitecture() { + return architecture; + } + + @Override + public String getDescription() { + return ""; + } + + @Override + public String getBaseFMWVersion() { + if (!Utils.isBaseInstallerType(type)) { + return baseFMWVersion; + } else { + return null; + } } @Option( @@ -52,15 +75,32 @@ public String getKey() { @Option( names = {"-v", "--version"}, - description = "Installer version. Ex: For WLS|FMW use 12.2.1.3.0 For jdk, use 8u201", + description = "Installer version. Ex: For WLS|FMW use 12.2.1.3.0 For jdk, use 8u201. The version for WLS, " + + "FMW etc. will be used to obtain patches.", required = true ) private String version; @Option( names = {"-a", "--architecture"}, - description = "(Optional) Installer architecture. Valid values: ${COMPLETION-CANDIDATES}" + required = true, + description = "Installer architecture. Valid values: ${COMPLETION-CANDIDATES}" ) private Architecture architecture; + @Option( + names = {"-c", "--commonName"}, + description = "(Optional) common name. Valid values: Alphanumeric values with no special characters. " + + "If not specified, default to the version value. Use this if you want to use a special name for the " + + "particular version of the installer." + ) + private String commonName; + + @Option( + names = {"-bv", "--baseFMWVersion"}, + description = "Base FMW version. Used by upper level product dependent on base WLS version different from the " + + " stack installer.", + required = false + ) + private String baseFMWVersion; } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/AddPatchEntry.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/AddPatchEntry.java index 5445696e7..5b07ce5c5 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/AddPatchEntry.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/AddPatchEntry.java @@ -6,6 +6,7 @@ import java.util.Collections; import com.oracle.weblogic.imagetool.api.model.CommandResponse; +import com.oracle.weblogic.imagetool.util.Architecture; import com.oracle.weblogic.imagetool.util.Utils; import picocli.CommandLine.Command; import picocli.CommandLine.Option; @@ -21,12 +22,37 @@ public String getKey() { return patchId; } + @Override + public String getVersion() { + return version; + } + + @Override + public String getCommonName() { + return null; + } + + @Override + public Architecture getArchitecture() { + return architecture; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getBaseFMWVersion() { + return ""; + } + @Override public CommandResponse call() throws Exception { try { if (patchId != null && !patchId.isEmpty()) { - Utils.validatePatchIds(Collections.singletonList(patchId), false); - return addToCache(); + Utils.validatePatchIds(Collections.singletonList(patchId), true); + return addPatchToCache(); } else { return CommandResponse.error("IMG-0076", "--patchId"); } @@ -41,4 +67,24 @@ public CommandResponse call() throws Exception { required = true ) private String patchId; + + @Option( + names = {"-v", "--version"}, + description = "Patch version. ", + required = true + ) + private String version; + + @Option( + names = {"-a", "--architecture"}, + required = true, + description = "Patch architecture. Valid values: arm64, amd64, Generic" + ) + private Architecture architecture; + + @Option( + names = {"-d", "--description"}, + description = "Patch description." + ) + private String description; } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/CacheAddOperation.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/CacheAddOperation.java index 041de8a0d..7c14d1ada 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/CacheAddOperation.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/CacheAddOperation.java @@ -3,50 +3,116 @@ package com.oracle.weblogic.imagetool.cli.cache; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import com.oracle.weblogic.imagetool.api.model.CommandResponse; -import com.oracle.weblogic.imagetool.cachestore.CacheStoreException; +import com.oracle.weblogic.imagetool.cachestore.CacheStore; +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; +import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.settings.ConfigManager; +import com.oracle.weblogic.imagetool.util.Architecture; import picocli.CommandLine.Option; -import static com.oracle.weblogic.imagetool.cachestore.CacheStoreFactory.cache; public abstract class CacheAddOperation extends CacheOperation { public abstract String getKey(); - CommandResponse addToCache() throws CacheStoreException { - // if file is invalid or does not exist, return an error + public abstract String getVersion(); + + public abstract String getCommonName(); + + public abstract Architecture getArchitecture(); + + public abstract String getDescription(); + + public abstract String getBaseFMWVersion(); + + CommandResponse addInstallerToCache() throws IOException { if (filePath == null || !Files.isRegularFile(filePath)) { return CommandResponse.error("IMG-0049", filePath); } - String key = getKey(); - // if the new value is the same as the existing cache value, do nothing - String existingValue = cache().getValueFromCache(key); - if (absolutePath().toString().equals(existingValue)) { - return CommandResponse.success("IMG-0075"); + String type = getKey(); + String name = getCommonName(); + + Architecture arch = getArchitecture(); + // Force WDT to be generic + if (InstallerType.fromString(type) == InstallerType.WDT) { + arch = Architecture.GENERIC; } - // if there is already a cache entry and the user did not ask to force it, return an error - if (!force && existingValue != null) { - return CommandResponse.error("IMG-0048", key, existingValue); + InstallerMetaData metaData; + + if (getCommonName() == null) { + metaData = ConfigManager.getInstance().getInstallerForPlatform(InstallerType.fromString(type), + arch, getVersion()); + name = getVersion(); + } else { + metaData = ConfigManager.getInstance().getInstallerForPlatform(InstallerType.fromString(type), + arch, getVersion(), getCommonName()); } - // input appears valid, add the entry to the cache and exit - cache().addToCache(key, absolutePath().toString()); - return CommandResponse.success("IMG-0050", key, cache().getValueFromCache(key)); + if (metaData != null) { + return CommandResponse.success("IMG-0075"); + } else { + if (getCommonName() != null) { + // Check the case for commonName, only one version is allowed + List tempList = ConfigManager.getInstance() + .listInstallerByCommonName(InstallerType.fromString(type), + getCommonName()); + if (tempList != null && !tempList.isEmpty()) { + for (InstallerMetaData installerMetaData : tempList) { + if (!installerMetaData.getProductVersion().equals(getVersion())) { + return CommandResponse.error("IMG-0164", installerMetaData.getProductVersion()); + } + } + } + } + } + + metaData = new InstallerMetaData(arch.toString(), filePath.toAbsolutePath().toString(), + getVersion(), getBaseFMWVersion()); + ConfigManager.getInstance().addInstaller(InstallerType.fromString(type), name, metaData); + // if the new value is the same as the existing cache value, do nothing + + return CommandResponse.success("IMG-0050", type, metaData.getProductVersion(), metaData.getLocation()); } - private Path absolutePath() { - if (absolutePath == null) { - absolutePath = filePath.toAbsolutePath(); + CommandResponse addPatchToCache() throws IOException { + // if file is invalid or does not exist, return an error + if (filePath == null || !Files.isRegularFile(filePath)) { + return CommandResponse.error("IMG-0049", filePath); + } + + String bugNumber = getKey(); + String version = getVersion(); + + int separator = bugNumber.indexOf(CacheStore.CACHE_KEY_SEPARATOR); + if (separator > 0) { + version = bugNumber.substring(separator + 1); + bugNumber = bugNumber.substring(0, separator); } - return absolutePath; - } - private Path absolutePath = null; + PatchMetaData metaData = ConfigManager.getInstance().getPatchForPlatform(getArchitecture().toString(), + bugNumber, version); + + if (metaData != null) { + return CommandResponse.success("IMG-0075"); + } + + Architecture arch = getArchitecture(); + + ConfigManager.getInstance().addPatch(bugNumber, arch.toString(), filePath.toAbsolutePath().toString(), + version, getDescription()); + + return CommandResponse.success("IMG-0130", bugNumber, version, + filePath.toAbsolutePath().toString()); + } @Option( names = {"--force"}, diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/CacheCLI.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/CacheCLI.java index aa3aab66a..5c64a5fce 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/CacheCLI.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/CacheCLI.java @@ -4,6 +4,7 @@ package com.oracle.weblogic.imagetool.cli.cache; import com.oracle.weblogic.imagetool.cli.HelpVersionProvider; +import com.oracle.weblogic.imagetool.util.CacheConversion; import picocli.CommandLine.Command; @Command( @@ -12,11 +13,13 @@ versionProvider = HelpVersionProvider.class, commandListHeading = "%nCommands:%n%n", subcommands = { - ListCacheItems.class, + ListPatches.class, + ListInstallers.class, AddInstallerEntry.class, AddPatchEntry.class, - AddEntry.class, - DeleteEntry.class + DeleteInstaller.class, + DeletePatch.class, + CacheConversion.class }, sortOptions = false ) diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/DeleteEntry.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/DeleteEntry.java deleted file mode 100644 index 6f2f1a179..000000000 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/DeleteEntry.java +++ /dev/null @@ -1,44 +0,0 @@ -// 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 com.oracle.weblogic.imagetool.cli.cache; - -import com.oracle.weblogic.imagetool.api.model.CommandResponse; -import com.oracle.weblogic.imagetool.util.Constants; -import com.oracle.weblogic.imagetool.util.Utils; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; - -import static com.oracle.weblogic.imagetool.cachestore.CacheStoreFactory.cache; - -@Command( - name = "deleteEntry", - description = "Command to delete a cache entry" -) -public class DeleteEntry extends CacheOperation { - - @Override - public CommandResponse call() throws Exception { - if (!Utils.isEmptyString(key)) { - if (Constants.DELETE_ALL_FOR_SURE.equalsIgnoreCase(key)) { - cache().clearCache(); - return CommandResponse.success("IMG-0046"); - } else { - String oldValue = cache().deleteFromCache(key); - if (oldValue != null) { - return CommandResponse.success("IMG-0051", key, oldValue); - } else { - return CommandResponse.success("IMG-0052", key); - } - } - } - return CommandResponse.error("IMG-0045"); - } - - @Option( - names = {"--key"}, - description = "Key corresponding to the cache entry to delete", - required = true - ) - private String key; -} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/DeleteInstaller.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/DeleteInstaller.java new file mode 100644 index 000000000..67fad81f9 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/DeleteInstaller.java @@ -0,0 +1,97 @@ +// 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 com.oracle.weblogic.imagetool.cli.cache; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.oracle.weblogic.imagetool.api.model.CommandResponse; +import com.oracle.weblogic.imagetool.cachestore.CacheStoreException; +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; +import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.settings.ConfigManager; +import com.oracle.weblogic.imagetool.util.Architecture; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + + +@Command( + name = "deleteInstaller", + description = "Delete a installer" +) +public class DeleteInstaller extends CacheOperation { + + @Override + public CommandResponse call() throws CacheStoreException { + ConfigManager configManager = ConfigManager.getInstance(); + Map>> data = configManager.getInstallers(); + if (type == null || (version == null && commonName == null) || architecture == null) { + return CommandResponse.success("IMG-0124"); + } + boolean exists = false; + for (String itemType : data.keySet()) { + if (type != null && type != InstallerType.fromString(itemType)) { + continue; + } + Map> items = data.get(itemType); + String search; + if (commonName == null) { + search = version; + } else { + search = commonName; + } + + exists = Optional.ofNullable(items.get(search)) + .map(list -> list.stream().anyMatch(i -> Architecture.fromString(i.getArchitecture()) + .equals(architecture) && i.getProductVersion().equalsIgnoreCase(version))) + .orElse(false); + if (exists) { + Optional.ofNullable(items.get(search)) + .map(list -> list.removeIf(i -> Architecture.fromString(i.getArchitecture()) + .equals(architecture) && i.getProductVersion().equalsIgnoreCase(version))); + + + if (items.get(search).isEmpty()) { + items.remove(search); + } + + try { + configManager.saveAllInstallers(data); + } catch (IOException e) { + throw new CacheStoreException(e.getMessage(), e); + } + + return CommandResponse.success("IMG-0169", search, version, architecture); + } + } + return CommandResponse.error("IMG-0125"); + } + + @Option( + names = {"-t", "--type"}, + description = "Filter installer type. e.g. wls, jdk, wdt." + ) + private InstallerType type; + + @Option( + names = {"-cn", "--commonName"}, + description = "Filter by common name." + ) + private String commonName; + + @Option( + names = {"-v", "--version"}, + description = "Specific version to delete." + ) + private String version; + + @Option( + names = {"-a", "--architecture"}, + description = "Specific architecture to delete." + ) + private Architecture architecture; + +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/DeletePatch.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/DeletePatch.java new file mode 100644 index 000000000..00131539a --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/DeletePatch.java @@ -0,0 +1,94 @@ +// 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 com.oracle.weblogic.imagetool.cli.cache; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.oracle.weblogic.imagetool.api.model.CommandResponse; +import com.oracle.weblogic.imagetool.cachestore.CacheStoreException; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.settings.ConfigManager; +import com.oracle.weblogic.imagetool.util.Architecture; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + + +@Command( + name = "deletePatch", + description = "Delete a patch" +) +public class DeletePatch extends CacheOperation { + + private boolean isPatchVersionMatched(List items) { + return Optional.ofNullable(items) + .map(list -> list.stream().anyMatch(i -> i.getPatchVersion().equals(version) + && Architecture.fromString(i.getArchitecture()).equals(architecture))).orElse(false); + } + + @Override + public CommandResponse call() throws CacheStoreException { + ConfigManager configManager = ConfigManager.getInstance(); + Map> data = configManager.getAllPatches(); + + if (patchId == null || version == null || architecture == null) { + return CommandResponse.success("IMG-0126"); + } + for (String id : data.keySet()) { + if (patchId.equalsIgnoreCase(id)) { + List items = data.get(id); + if (isPatchVersionMatched(items)) { + + Optional d = Optional.of(items).flatMap(list -> list.stream().filter(i -> + i.getPatchVersion().equals(version) + && Architecture.fromString(i.getArchitecture()).equals(architecture)).findAny()); + + if (d.isPresent()) { + Optional.of(items) + .map(list -> list.removeIf(i -> i.getPatchVersion().equals(version) + && Architecture.fromString(i.getArchitecture()).equals(architecture))); + // if all patches are removed for this bug number, remove this bug number from the store. + if (items.isEmpty()) { + data.remove(id); + } + try { + configManager.saveAllPatches(data); + } catch (IOException e) { + throw new CacheStoreException(e.getMessage(), e); + } + + return CommandResponse.success("IMG-0168", patchId, version, architecture); + } + + + } else { + return CommandResponse.error("IMG-0127"); + } + + } + } + return CommandResponse.error("IMG-0127"); + } + + @Option( + names = {"--patchId"}, + description = "Bug num" + ) + private String patchId; + + @Option( + names = {"--version"}, + description = "Specific version to delete" + ) + private String version; + + @Option( + names = {"--architecture"}, + description = "Specific architecture to delete" + ) + private Architecture architecture; + +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/ListCacheItems.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/ListCacheItems.java deleted file mode 100644 index d042a6ce0..000000000 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/ListCacheItems.java +++ /dev/null @@ -1,44 +0,0 @@ -// 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 com.oracle.weblogic.imagetool.cli.cache; - -import java.util.Map; -import java.util.regex.Pattern; - -import com.oracle.weblogic.imagetool.api.model.CommandResponse; -import com.oracle.weblogic.imagetool.cachestore.CacheStoreException; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; - -import static com.oracle.weblogic.imagetool.cachestore.CacheStoreFactory.cache; - -@Command( - name = "listItems", - description = "List cache contents" -) -public class ListCacheItems extends CacheOperation { - - @Override - public CommandResponse call() throws CacheStoreException { - Map resultMap = cache().getCacheItems(); - if (resultMap == null || resultMap.isEmpty()) { - return CommandResponse.success("IMG-0047"); - } else { - System.out.println("Cache contents"); - - Pattern pattern = Pattern.compile(key == null ? ".*" : key); - resultMap.entrySet().stream() - .filter(entry -> pattern.matcher(entry.getKey()).matches()) - .forEach(System.out::println); - - return CommandResponse.success(null); - } - } - - @Option( - names = {"--key"}, - description = "list only cached items where the key matches this regex" - ) - private String key; -} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/ListInstallers.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/ListInstallers.java new file mode 100644 index 000000000..a4968d027 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/ListInstallers.java @@ -0,0 +1,130 @@ +// 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 com.oracle.weblogic.imagetool.cli.cache; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.oracle.weblogic.imagetool.api.model.CommandResponse; +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; +import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.settings.ConfigManager; +import com.oracle.weblogic.imagetool.util.Utils; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + + +@Command( + name = "listInstallers", + description = "List installers" +) +public class ListInstallers extends CacheOperation { + + @Override + public CommandResponse call() { + ConfigManager configManager = ConfigManager.getInstance(); + Map>> data = configManager.getInstallers(); + verifyInput(); + + for (String itemType : data.keySet()) { + if (type != null && type != InstallerType.fromString(itemType)) { + continue; + } + printLine(itemType + ":"); + + data.get(itemType).forEach((installer, metaData) -> { + + if (commonName != null && !commonName.equalsIgnoreCase(installer)) { + return; + } + // commonName is null or version is specified + if (commonName == null && version != null && !version.equalsIgnoreCase(installer)) { + return; + } + printLine(" " + installer + ":"); + List sortedList = new ArrayList<>(metaData); + + if (commonName != null && version != null) { + sortedList = metaData.stream() + .filter(c -> c.getProductVersion().equals(version)) + .sorted(Comparator.comparing(InstallerMetaData::getArchitecture)) + .collect(Collectors.toList()); + + } else { + sortedList = metaData.stream() + .sorted(Comparator.comparing(InstallerMetaData::getArchitecture)) + .collect(Collectors.toList()); + } + + printDetails(sortedList, InstallerType.fromString(itemType)); + }); + + } + + return CommandResponse.success(null); + } + + private void printDetails(List sortedList, InstallerType type) { + String currentArch = ""; + for (InstallerMetaData meta : sortedList) { + if (!currentArch.equals(meta.getArchitecture())) { + currentArch = meta.getArchitecture(); + printLine(" " + meta.getArchitecture() + ":"); + } + printLine(" version: " + meta.getProductVersion()); + printLine(" location: " + meta.getLocation()); + if (details) { + printLine(" digest: " + meta.getDigest()); + printLine(" dateAdded: " + meta.getDateAdded()); + if (!Utils.isBaseInstallerType(type)) { + printLine(" baseFMWVersion: " + meta.getBaseFMWVersion()); + } + } + } + } + + private void printLine(String line) { + System.out.println(line); + } + + private void verifyInput() { + if (commonName != null && !commonName.isEmpty() && type == null) { + printLine(Utils.getMessage("IMG-0156")); + System.exit(2); + } + + if (version != null && !version.isEmpty() && type == null) { + printLine(Utils.getMessage("IMG-0157")); + System.exit(2); + } + } + + @Option( + names = {"--type"}, + description = "Filter installer type. e.g. wls, jdk, wdt" + ) + private InstallerType type; + + @Option( + names = {"--commonName"}, + description = "Filter installer by common name." + ) + private String commonName; + + @Option( + names = {"--version"}, + description = "Filter installer by version." + ) + private String version; + + @Option( + names = {"--details"}, + description = "Full details of the installers." + ) + private boolean details = false; + +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/ListPatches.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/ListPatches.java new file mode 100644 index 000000000..d77381a69 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/cache/ListPatches.java @@ -0,0 +1,96 @@ +// 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 com.oracle.weblogic.imagetool.cli.cache; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.oracle.weblogic.imagetool.api.model.CommandResponse; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.settings.ConfigManager; +import com.oracle.weblogic.imagetool.util.Utils; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + + +@Command( + name = "listPatches", + description = "List patches" +) +public class ListPatches extends CacheOperation { + + @Override + public CommandResponse call() { + + ConfigManager configManager = ConfigManager.getInstance(); + Map> data = configManager.getAllPatches(); + verifyInput(); + + if (!data.isEmpty() && patchId != null && !data.containsKey(patchId)) { + printLine(Utils.getMessage("IMG-0160", patchId)); + } else { + for (String bug : data.keySet()) { + if (patchId != null && !patchId.equalsIgnoreCase(bug)) { + continue; + } + System.out.println(bug + ":"); + + Map> groupByArchitecture = data.get(bug).stream() + .collect(Collectors.groupingBy(PatchMetaData::getArchitecture)); + + groupByArchitecture.forEach((architecture, metaDatas) -> { + printLine(" " + architecture + ":"); + + metaDatas.forEach(metaData -> { + if (version != null && !metaData.getPatchVersion().equalsIgnoreCase(version)) { + return; + } + printLine(" - version: " + metaData.getPatchVersion()); + printLine(" location: " + metaData.getLocation()); + if (details) { + printLine(" digest: " + metaData.getDigest()); + printLine(" description: " + metaData.getDescription()); + printLine(" dateAdded: " + metaData.getDateAdded()); + } + + }); + + }); + } + } + + return CommandResponse.success(null); + + } + + private void printLine(String line) { + System.out.println(line); + } + + private void verifyInput() { + if (version != null && !version.isEmpty() && patchId == null) { + printLine(Utils.getMessage("IMG-0158")); + System.exit(2); + } + } + + @Option( + names = {"--patchId"}, + description = "Patch id" + ) + private String patchId; + + @Option( + names = {"--version"}, + description = "List only the patch version" + ) + private String version; + + @Option( + names = {"--details"}, + description = "List all details about the patch" + ) + private boolean details = false; +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ConfigAttributeName.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ConfigAttributeName.java new file mode 100644 index 000000000..ee96fc034 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ConfigAttributeName.java @@ -0,0 +1,156 @@ +// 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. + +package com.oracle.weblogic.imagetool.cli.config; + + +import com.oracle.weblogic.imagetool.settings.UserSettingsFile; + +public enum ConfigAttributeName { + buildContextDirectory("BuildContextDirectory") { + @Override + public void set(UserSettingsFile settings, String value) { + settings.setBuildContextDirectory(value); + } + + @Override + public String get(UserSettingsFile settings) { + return settings.getBuildContextDirectory(); + } + }, + patchDirectory("PatchDirectory") { + @Override + public void set(UserSettingsFile settings, String value) { + settings.setPatchDirectory(value); + } + + @Override + public String get(UserSettingsFile settings) { + return settings.getPatchDirectory(); + } + }, + installerDirectory("InstallerDirectory") { + @Override + public void set(UserSettingsFile settings, String value) { + settings.setInstallerDirectory(value); + } + + @Override + public String get(UserSettingsFile settings) { + return settings.getInstallerDirectory(); + } + }, + buildEngine("BuildEngine") { + @Override + public void set(UserSettingsFile settings, String value) { + settings.setBuildEngine(value); + } + + @Override + public String get(UserSettingsFile settings) { + return settings.getBuildEngine(); + } + }, + containerEngine("ContainerEngine") { + @Override + public void set(UserSettingsFile settings, String value) { + settings.setContainerEngine(value); + } + + @Override + public String get(UserSettingsFile settings) { + return settings.getContainerEngine(); + } + }, + aruRetryMax("AruRetryMax") { + @Override + public void set(UserSettingsFile settings, String value) { + if (value != null) { + settings.setAruRetryMax(Integer.parseInt(value)); + } else { + settings.setAruRetryMax(null); + } + } + + @Override + public String get(UserSettingsFile settings) { + //TODO check for null + return settings.getAruRetryMax().toString(); + } + }, + defaultBuildPlatform("DefaultBuildPlatform") { + @Override + public void set(UserSettingsFile settings, String value) { + settings.setDefaultBuildPlatform(value); + } + + @Override + public String get(UserSettingsFile settings) { + return settings.getDefaultBuildPlatform(); + } + }, + defaultWLSVersion("DefaultWLSVersion") { + @Override + public void set(UserSettingsFile settings, String value) { + settings.setDefaultWLSVersion(value); + } + + @Override + public String get(UserSettingsFile settings) { + return settings.returnDefaultWLSVersion(); + } + }, + defaultWDTVersion("DefaultWDTVersion") { + @Override + public void set(UserSettingsFile settings, String value) { + settings.setDefaultWDTVersion(value); + } + + @Override + public String get(UserSettingsFile settings) { + return settings.returnDefaultWDTVersion(); + } + }, + defaultJDKVersion("DefaultJDKVersion") { + @Override + public void set(UserSettingsFile settings, String value) { + settings.setDefaultJDKVersion(value); + } + + @Override + public String get(UserSettingsFile settings) { + return settings.returnDefaultJDKVersion(); + } + }, + aruRetryInterval("AruRetryInterval") { + @Override + public void set(UserSettingsFile settings, String value) { + if (value != null) { + settings.setAruRetryInterval(Integer.parseInt(value)); + } else { + settings.setAruRetryInterval(null); + } + } + + @Override + public String get(UserSettingsFile settings) { + //TODO check for null + return settings.getAruRetryInterval().toString(); + } + }; + + private final String value; + + public abstract void set(UserSettingsFile settings, String value); + + public abstract String get(UserSettingsFile settings); + + ConfigAttributeName(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ConfigCommand.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ConfigCommand.java new file mode 100644 index 000000000..a47f419e0 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ConfigCommand.java @@ -0,0 +1,20 @@ +// 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. + +package com.oracle.weblogic.imagetool.cli.config; + +import picocli.CommandLine; + +@CommandLine.Command( + name = "config", + description = "Set global configuration options and defaults for the Image Tool", + commandListHeading = "%nCommands:%n%n", + subcommands = { + SetCommand.class, + ShowCommand.class, + ResetCommand.class + }, + sortOptions = false +) +public class ConfigCommand { +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ResetCommand.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ResetCommand.java new file mode 100644 index 000000000..a9fefe751 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ResetCommand.java @@ -0,0 +1,41 @@ +// 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. + +package com.oracle.weblogic.imagetool.cli.config; + +import java.util.concurrent.Callable; + +import com.oracle.weblogic.imagetool.api.model.CommandResponse; +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.settings.UserSettingsFile; +import picocli.CommandLine; + +@CommandLine.Command( + name = "reset", + description = "Remove/reset a configuration entry", + sortOptions = false +) +public class ResetCommand implements Callable { + private static final LoggingFacade logger = LoggingFactory.getLogger(ResetCommand.class); + + @CommandLine.Option( + names = {"--name"}, + description = "Name of the setting", + order = 0, + required = true + ) + private ConfigAttributeName name; + + @Override + public CommandResponse call() throws Exception { + logger.entering(); + UserSettingsFile settings = new UserSettingsFile(); + + name.set(settings, null); + settings.save(); + + logger.exiting(); + return new CommandResponse(0, ""); + } +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/SetCommand.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/SetCommand.java new file mode 100644 index 000000000..0f8f70c30 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/SetCommand.java @@ -0,0 +1,87 @@ +// 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 com.oracle.weblogic.imagetool.cli.config; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.concurrent.Callable; + +import com.oracle.weblogic.imagetool.api.model.CommandResponse; +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.settings.UserSettingsFile; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; + +@Command( + name = "set", + description = "Set or update a configuration entry", + sortOptions = false +) +public class SetCommand implements Callable { + private static final LoggingFacade logger = LoggingFactory.getLogger(SetCommand.class); + + @Option( + names = {"--name"}, + description = "Name of the setting", + order = 0, + required = true + ) + private ConfigAttributeName name; + + @Parameters(index = "0", description = "the new configuration setting value") + @SuppressWarnings("UnusedDeclaration") + private String value; + + @Override + public CommandResponse call() throws Exception { + UserSettingsFile settings = new UserSettingsFile(); + logger.entering(); + int status = 0; + String message = "done"; + if (verifyInput(name.toString(), value)) { + name.set(settings, value); + settings.save(); + } else { + status = -1; + message = "Failed to set configuration entry"; + } + + logger.exiting(); + return new CommandResponse(status, message); + } + + private boolean verifyInput(String name, String value) { + if ("aruRetryInterval".equalsIgnoreCase(name) || "aruRetryMax".equalsIgnoreCase(name)) { + try { + int n = Integer.parseInt(value); + if (n < 0) { + logger.severe("IMG-0161", name, value); + return false; + } + } catch (NumberFormatException e) { + logger.severe("IMG-0161", name, value); + return false; + } + } + + if ("defaultBuildPlatform".equalsIgnoreCase(name) || "containerEngine".equalsIgnoreCase(name)) { + if (!"linux/amd64".equals(value) && !"linux/arm64".equals(value)) { + logger.severe("IMG-0162"); + return false; + } + } + + if ("buildContextDirectory".equalsIgnoreCase(name) || "patchDirectory".equalsIgnoreCase(name) + || "installerDirectory".equalsIgnoreCase(name)) { + if (!Files.isDirectory(Paths.get(value))) { + logger.severe("IMG-0163", name, value); + return false; + } + return true; + } + return true; + } +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ShowCommand.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ShowCommand.java new file mode 100644 index 000000000..ae37cfc89 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/config/ShowCommand.java @@ -0,0 +1,88 @@ +// 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 com.oracle.weblogic.imagetool.cli.config; + +import java.util.Map; +import java.util.concurrent.Callable; + +import com.oracle.weblogic.imagetool.api.model.CommandResponse; +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.settings.InstallerSettings; +import com.oracle.weblogic.imagetool.settings.UserSettingsFile; +import picocli.CommandLine; + +@CommandLine.Command( + name = "show", + description = "Print a configuration entry", + sortOptions = false +) +public class ShowCommand implements Callable { + private static final LoggingFacade logger = LoggingFactory.getLogger(ShowCommand.class); + + @CommandLine.Option( + names = {"--name"}, + description = "Name of the setting", + order = 0, + required = false + ) + private ConfigAttributeName name; + + @CommandLine.Option( + names = {"--all"}, + description = "Show all the settings", + order = 1, + required = false + ) + private boolean all; + + @Override + public CommandResponse call() throws Exception { + logger.entering(); + UserSettingsFile settings = new UserSettingsFile(); + + logger.exiting(); + if (all) { + printAllSettings(settings); + return new CommandResponse(0, ""); + } + if (name != null) { + String result = name.get(settings); + printSettingValue(name.toString(), result); + return new CommandResponse(0, ""); + } + return new CommandResponse(0, ""); + } + + private static void printSettingValue(String setting, Object value) { + if (value != null) { + System.out.println(setting + ": " + value); + } else { + System.out.println(setting + ": not set"); + } + } + + private static void printLine(String line) { + System.out.println(line); + } + + private static void printAllSettings(UserSettingsFile settings) { + printSettingValue("aruRetryInterval", settings.getAruRetryInterval()); + printSettingValue("aruRetryMax", settings.getAruRetryMax()); + printSettingValue("defaultBuildPlatform", settings.getDefaultBuildPlatform()); + printSettingValue("buildContextDirectory", settings.getBuildContextDirectory()); + printSettingValue("containerEngine", settings.getContainerEngine()); + printSettingValue("installerDirectory", settings.getInstallerDirectory()); + printSettingValue("patchDirectory", settings.getPatchDirectory()); + Map installerSettings = settings.getInstallerSettings(); + if (installerSettings != null && !installerSettings.isEmpty()) { + printLine("Defaults: "); + for (Map.Entry entry: installerSettings.entrySet()) { + String key = entry.getKey(); + InstallerSettings value = entry.getValue(); + printSettingValue(" " + key, value.getDefaultVersion()); + } + } + } +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonCreateOptions.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonCreateOptions.java index e748e7148..155b65e5a 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonCreateOptions.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonCreateOptions.java @@ -3,23 +3,35 @@ package com.oracle.weblogic.imagetool.cli.menu; +import java.io.FileInputStream; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.List; import javax.xml.xpath.XPathExpressionException; -import com.oracle.weblogic.imagetool.api.model.CachedFile; import com.oracle.weblogic.imagetool.aru.AruException; +import com.oracle.weblogic.imagetool.installer.FmwInstallerType; +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; import com.oracle.weblogic.imagetool.installer.InstallerType; import com.oracle.weblogic.imagetool.installer.MiddlewareInstall; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.settings.ConfigManager; +import com.oracle.weblogic.imagetool.util.Architecture; import com.oracle.weblogic.imagetool.util.Constants; import com.oracle.weblogic.imagetool.util.Utils; import picocli.CommandLine.Option; -import static com.oracle.weblogic.imagetool.cachestore.CacheStoreFactory.cache; +import static com.oracle.weblogic.imagetool.util.Constants.AMD64_BLD; +import static com.oracle.weblogic.imagetool.util.Constants.ARM64_BLD; +import static com.oracle.weblogic.imagetool.util.Constants.CTX_JDK; +import static com.oracle.weblogic.imagetool.util.Constants.DEFAULT_JDK_VERSION; +import static com.oracle.weblogic.imagetool.util.Constants.DEFAULT_WLS_VERSION; public class CommonCreateOptions extends CommonPatchingOptions { @@ -31,22 +43,69 @@ public class CommonCreateOptions extends CommonPatchingOptions { void prepareNewImage() throws IOException, InterruptedException, XPathExpressionException, AruException { logger.entering(); + + if (installerVersion == null && commonName == null) { + installerVersion = DEFAULT_WLS_VERSION; + } + + if (commonName != null) { + // reset version + List dataList = ConfigManager.getInstance() + .listInstallerByCommonName(InstallerType.fromString(getInstallerType().toString()), commonName); + if (dataList != null && !dataList.isEmpty()) { + installerVersion = dataList.get(0).getProductVersion(); + } else { + String errorMsg = Utils.getMessage("IMG-0166", commonName); + throw new IOException(errorMsg); + } + } + copyOptionsFromImage(); + + List buildPlatforms = getBuildPlatform(); + // Verify version and installers exists first + verifyInstallers(buildPlatforms); + if (dockerfileOptions.installJava()) { - CachedFile jdk = new CachedFile(InstallerType.JDK, jdkVersion, getTargetArchitecture()); - Path installerPath = jdk.copyFile(cache(), buildDir()); - dockerfileOptions.setJavaInstaller(installerPath.getFileName().toString()); + + List jdkFilePathList = new ArrayList<>(); + for (String jdkPlatform : buildPlatforms) { + String buildContextDestination = buildDir(); + Architecture arch = Architecture.fromString(jdkPlatform); + if (jdkPlatform.equals(AMD64_BLD)) { + buildContextDestination = buildContextDestination + "/" + CTX_JDK + AMD64_BLD; + dockerfileOptions.setTargetAMDPlatform(true); + } else if (jdkPlatform.equals(ARM64_BLD)) { + buildContextDestination = buildContextDestination + "/" + CTX_JDK + ARM64_BLD; + dockerfileOptions.setTargetARMPlatform(true); + } + //CachedFile jdk = new CachedFile(InstallerType.JDK, jdkVersion, jdkPlatform); + //Path installerPath = jdk.copyFile(cache(), buildContextDestination); + InstallerMetaData installerMetaData = ConfigManager.getInstance() + .getInstallerForPlatform(InstallerType.JDK, arch, jdkVersion); + if (installerMetaData == null) { + throw new IllegalArgumentException(Utils.getMessage("IMG-0145", InstallerType.JDK, + arch, jdkVersion)); + } + Path installerPath = Paths.get(installerMetaData.getLocation()); + Files.copy(installerPath, Paths.get(buildContextDestination).resolve(installerPath.getFileName())); + jdkFilePathList.add(installerPath.getFileName().toString()); + } + dockerfileOptions.setJavaInstaller(jdkFilePathList); } if (dockerfileOptions.installMiddleware()) { - MiddlewareInstall install = new MiddlewareInstall(getInstallerType(), installerVersion, - installerResponseFiles, getTargetArchitecture()); - install.copyFiles(cache(), buildDir()); + MiddlewareInstall install = + new MiddlewareInstall(getInstallerType(), installerVersion, installerResponseFiles, buildPlatforms, + buildEngine, commonName); + install.copyFiles(buildDir()); dockerfileOptions.setMiddlewareInstall(install); + dockerfileOptions.includeBinaryOsPackages(getInstallerType().equals(FmwInstallerType.OHS)); } else { dockerfileOptions.setWdtBase("os_update"); } + setCommonName(commonName); // resolve required patches handlePatchFiles(); @@ -71,18 +130,152 @@ void prepareNewImage() throws IOException, InterruptedException, XPathExpression logger.exiting(); } + void verifyInstallers(List buildPlatforms) throws IOException { + ConfigManager configManager = ConfigManager.getInstance(); + // Verify version and installers exists first + for (String buildPlatform : buildPlatforms) { + Architecture arch = Architecture.fromString(buildPlatform); + jdkVersion = resetInstallerVersion(InstallerType.JDK, jdkVersion); + verifyInstallerExists(configManager, InstallerType.JDK, arch, jdkVersion, buildPlatform); + if (getInstallerType().equals(FmwInstallerType.OID)) { + verifyOIDInstallers(configManager, arch, buildPlatform, installerVersion); + } else { + verifyNormalInstallers(getInstallerType().installerList(), configManager, arch, buildPlatform); + } + } + + } + + void verifyOIDInstallers(ConfigManager configManager, Architecture arch, + String buildPlatform, String installerVersion) throws IOException { + verifyInstallerExists(configManager, InstallerType.OID, arch, installerVersion, buildPlatform); + InstallerMetaData installerMetaData = configManager.getInstallerForPlatform(InstallerType.OID, + arch, installerVersion); + if (installerMetaData == null) { + throw new IllegalArgumentException(Utils.getMessage("IMG-0145", InstallerType.OID, + arch, installerVersion)); + } + String baseFMWVersion = installerMetaData.getBaseFMWVersion(); + if (baseFMWVersion == null) { + baseFMWVersion = installerVersion; + } + verifyInstallerExists(configManager, InstallerType.FMW, arch, baseFMWVersion, buildPlatform); + } + + void verifyNormalInstallers(List installers, ConfigManager configManager, Architecture arch, + String buildPlatform) throws IOException { + for (InstallerType installerType : installers) { + installerVersion = resetInstallerVersion(installerType, installerVersion); + verifyInstallerExists(configManager, installerType, arch, installerVersion, buildPlatform); + } + } + + void verifyInstallerExists(ConfigManager configManager, InstallerType installerType, Architecture arch, + String installerVersion, String buildPlatform) throws IOException { + + logger.info("IMG-0150", installerType, installerVersion, arch); + + InstallerMetaData installerMetaData = configManager.getInstallerForPlatform(installerType, + arch, installerVersion); + if (installerMetaData == null) { + throw new IllegalArgumentException(Utils.getMessage("IMG-0145", installerType, + buildPlatform, installerVersion)); + } else { + // If needed + verifyInstallerHash(installerMetaData); + } + } + + private String resetJDKVersion(String jdkVersion) { + String defaultJDKVersion = ConfigManager.getInstance().getDefaultJDKVersion(); + if (defaultJDKVersion != null) { + if (DEFAULT_JDK_VERSION.equals(jdkVersion)) { + jdkVersion = defaultJDKVersion; + } + } + return jdkVersion; + } + + private String resetInstallerVersion(InstallerType installerType, String installerVersion) { + String fixedVersion = installerVersion; + String defaultVersion; + + switch (installerType) { + case JDK: + String defaultJDKVersion = ConfigManager.getInstance().getDefaultJDKVersion(); + if (defaultJDKVersion != null) { + if (DEFAULT_JDK_VERSION.equals(installerVersion)) { + fixedVersion = defaultJDKVersion; + } + } + break; + case WLS: + String defaultWLSVersion = ConfigManager.getInstance().getDefaultWLSVersion(); + if (defaultWLSVersion != null) { + if (DEFAULT_WLS_VERSION.equals(installerVersion)) { + fixedVersion = defaultWLSVersion; + } + } + break; + case WDT: + defaultVersion = ConfigManager.getInstance().getDefaultWDTVersion(); + if (defaultVersion != null && installerVersion == null) { + fixedVersion = defaultVersion; + } + break; + default: + break; + } + return fixedVersion; + } + + private static void verifyInstallerHash(InstallerMetaData installerMetaData) throws IOException { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException ex) { + throw new IOException(ex); + } + try (FileInputStream fis = new FileInputStream(installerMetaData.getLocation())) { + byte[] buffer = new byte[1024]; + int read; + while ((read = fis.read(buffer)) != -1) { + digest.update(buffer, 0, read); + } + } + byte[] hash = digest.digest(); + StringBuilder sb = new StringBuilder(); + for (byte b : hash) { + sb.append(String.format("%02x", b)); + } + String hashString = sb.toString(); + if (!hashString.equalsIgnoreCase(installerMetaData.getDigest())) { + throw new IOException(String.format("Installer hash mismatch, expected %s but got %s for file %s", + installerMetaData.getDigest(), hashString, installerMetaData.getLocation())); + } + } + String getInstallerVersion() { return installerVersion; } + String getCommonName() { + return commonName; + } + @Option( names = {"--version"}, - description = "Installer version. Default: ${DEFAULT-VALUE}", - required = true, - defaultValue = Constants.DEFAULT_WLS_VERSION + description = "Installer version.", + defaultValue = DEFAULT_WLS_VERSION ) private String installerVersion; + @Option( + names = {"--commonName"}, + description = "Installer version common name, used with --version" + ) + private String commonName; + @Option( names = {"--jdkVersion"}, description = "Version of server jdk to install. Default: ${DEFAULT-VALUE}", @@ -109,4 +302,5 @@ String getInstallerVersion() { description = "path to where the inventory pointer file (oraInst.loc) should be stored in the image" ) private String inventoryPointerInstallLoc; + } \ No newline at end of file diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonOptions.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonOptions.java index af6bddc9d..5d95af5a5 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonOptions.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonOptions.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 com.oracle.weblogic.imagetool.cli.menu; @@ -10,20 +10,24 @@ import java.nio.file.Paths; import java.time.Duration; import java.time.Instant; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import com.oracle.weblogic.imagetool.api.model.CommandResponse; import com.oracle.weblogic.imagetool.aru.InvalidCredentialException; +import com.oracle.weblogic.imagetool.builder.AbstractCommand; import com.oracle.weblogic.imagetool.builder.BuildCommand; import com.oracle.weblogic.imagetool.cli.HelpVersionProvider; import com.oracle.weblogic.imagetool.inspect.OperatingSystemProperties; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import com.oracle.weblogic.imagetool.util.AdditionalBuildCommands; import com.oracle.weblogic.imagetool.util.Architecture; import com.oracle.weblogic.imagetool.util.Constants; @@ -35,7 +39,11 @@ import picocli.CommandLine.Parameters; import picocli.CommandLine.Spec; +import static com.oracle.weblogic.imagetool.util.Constants.AMD64_BLD; +import static com.oracle.weblogic.imagetool.util.Constants.ARM64_BLD; import static com.oracle.weblogic.imagetool.util.Constants.BUSYBOX_OS_IDS; +import static com.oracle.weblogic.imagetool.util.Constants.CTX_FMW; +import static com.oracle.weblogic.imagetool.util.Constants.CTX_JDK; public abstract class CommonOptions { private static final LoggingFacade logger = LoggingFactory.getLogger(CommonOptions.class); @@ -99,7 +107,7 @@ private void handleAdditionalBuildCommands() throws IOException { } } - void runDockerCommand(String dockerfile, BuildCommand command) throws IOException, InterruptedException { + void runDockerCommand(String dockerfile, AbstractCommand command) throws IOException, InterruptedException { logger.info("IMG-0078", command.toString()); if (dryRun) { @@ -119,9 +127,9 @@ void runDockerCommand(String dockerfile, BuildCommand command) throws IOExceptio */ BuildCommand getInitialBuildCmd(String contextFolder) { logger.entering(); - BuildCommand cmdBuilder = new BuildCommand(buildEngine, contextFolder); - - cmdBuilder.forceRm(!skipcleanup) + BuildCommand cmdBuilder = new BuildCommand(buildEngine, contextFolder) + .useBuildx(useBuildx) + .forceRm(!skipcleanup) .tag(imageTag) .platform(buildPlatform) .network(buildNetwork) @@ -132,6 +140,20 @@ BuildCommand getInitialBuildCmd(String contextFolder) { .buildArg("https_proxy", httpsProxyUrl, httpsProxyUrl != null && httpsProxyUrl.contains("@")) .buildArg("no_proxy", nonProxyHosts); + // if it is multiplatform build and using docker + + if (buildId != null && buildPlatform.size() > 1) { + // if push is specified, ignore load value + if (!buildEngine.equalsIgnoreCase("podman")) { + if (push) { + cmdBuilder.push(push); + } else if (load) { + cmdBuilder.load(load); + } else { + cmdBuilder.push(true); + } + } + } logger.exiting(); return cmdBuilder; } @@ -149,6 +171,24 @@ String buildDir() throws IOException { buildDirectory = tmpDir.toAbsolutePath().toString(); logger.info("IMG-0003", buildDirectory); } + Path fmwamd64 = Paths.get(CTX_FMW + AMD64_BLD); + Path fmwarm64 = Paths.get(CTX_FMW + ARM64_BLD); + Path jdkamd64 = Paths.get(CTX_JDK + AMD64_BLD); + Path jdkarm64 = Paths.get(CTX_JDK + ARM64_BLD); + + if (!Files.exists(fmwamd64)) { + Files.createDirectories(Paths.get(buildDirectory).resolve(fmwamd64)); + } + if (!Files.exists(fmwarm64)) { + Files.createDirectories(Paths.get(buildDirectory).resolve(fmwarm64)); + } + if (!Files.exists(jdkamd64)) { + Files.createDirectories(Paths.get(buildDirectory).resolve(jdkamd64)); + } + if (!Files.exists(jdkarm64)) { + Files.createDirectories(Paths.get(buildDirectory).resolve(jdkarm64)); + } + return buildDirectory; } @@ -315,16 +355,27 @@ public String buildId() { } /** - * Given the provided --buildPlatform, derive the architecture from the provided string. - * Docker/Podman refer to the target architecture as the build platform. - * @return The specified target architecture, or the local OS architecture if none was provided. + * Return build platforms from cli or settings or default system. + * @return architecture list */ - public Architecture getTargetArchitecture() { - if (buildPlatform != null) { - return Architecture.fromString(buildPlatform); + public List getBuildPlatform() { + if (buildPlatform == null) { + buildPlatform = new ArrayList<>(); + java.lang.String platform = ConfigManager.getInstance().getDefaultBuildPlatform(); + if (platform == null) { + platform = Utils.standardPlatform(Architecture.getLocalArchitecture().toString()); + } + buildPlatform.add(platform); } - - return Architecture.getLocalArchitecture(); + for (String platform : buildPlatform) { + boolean valid = Architecture.AMD64.getAcceptableNames().contains(platform) + || Architecture.ARM64.getAcceptableNames().contains(platform); + if (!valid) { + throw new IllegalArgumentException("Unknown build platform: " + platform); + } + } + buildPlatform.replaceAll(value -> Utils.standardPlatform(Architecture.fromString(value).toString())); + return buildPlatform.stream().distinct().collect(Collectors.toList()); } @Option( @@ -446,9 +497,28 @@ public Architecture getTargetArchitecture() { @Option( names = {"--platform"}, paramLabel = "", + split = ",", description = "Set the target platform to build. Example: linux/amd64 or linux/arm64" ) - private String buildPlatform; + private List buildPlatform; + + @Option( + names = {"--push"}, + description = "push the image to the remote repository, only used when building multiplatform images" + ) + private boolean push = false; + + @Option( + names = {"--load"}, + description = "load the image to the local repository, only used when building multiplatform images" + ) + private boolean load = false; + + @Option( + names = {"--useBuildx"}, + description = "Use the BuildKit for building the container image (default when building multi-architecture)" + ) + private boolean useBuildx; @Parameters( description = "Container build options.", @@ -456,6 +526,8 @@ public Architecture getTargetArchitecture() { ) List buildOptions; + @Spec CommandLine.Model.CommandSpec spec; + } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonPatchingOptions.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonPatchingOptions.java index 92a368ee2..48df54ec2 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonPatchingOptions.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonPatchingOptions.java @@ -27,11 +27,16 @@ import com.oracle.weblogic.imagetool.installer.FmwInstallerType; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.settings.ConfigManager; +import com.oracle.weblogic.imagetool.util.Architecture; import com.oracle.weblogic.imagetool.util.InvalidPatchIdFormatException; import com.oracle.weblogic.imagetool.util.Utils; import picocli.CommandLine.Option; -import static com.oracle.weblogic.imagetool.cachestore.CacheStoreFactory.cache; +import static com.oracle.weblogic.imagetool.util.Constants.AMD64_BLD; +import static com.oracle.weblogic.imagetool.util.Constants.ARM64_BLD; + public abstract class CommonPatchingOptions extends CommonOptions { private static final LoggingFacade logger = LoggingFactory.getLogger(CommonPatchingOptions.class); @@ -147,22 +152,50 @@ void handlePatchFiles(List installedPatches) AruUtil.rest().validatePatches(installedPatches, aruPatches, userId, password); String patchesFolderName = createPatchesTempDirectory().toAbsolutePath().toString(); + Files.createDirectories(Paths.get(patchesFolderName, AMD64_BLD)); + Files.createDirectories(Paths.get(patchesFolderName, ARM64_BLD)); // copy the patch JARs to the Docker build context directory from the local cache, downloading them if needed + ConfigManager configManager = ConfigManager.getInstance(); for (AruPatch patch : aruPatches) { - PatchFile patchFile = new PatchFile(patch, userId, password); - String patchLocation = patchFile.resolve(cache()); + String platform = patch.platformName(); + logger.info("Applying patch " + patch.patchId() + " version " + patch.version() + + " to " + platform); + PatchMetaData metaData = configManager.getPatchForPlatform(platform, patch.patchId(), patch.version()); + + if (metaData == null) { + // download the patch first + if (userId != null && password != null) { + PatchFile patchFile = new PatchFile(patch, userId, password); + String filePath = patchFile.resolve(); + metaData = configManager.getPatchForPlatform(platform, patch.patchId(), patch.version()); + } else { + throw logger.throwing(new IOException("No user credentials provided and " + + "no offline patch downloaded: " + patch.patchId())); + } + } + //PatchFile patchFile = new PatchFile(patch, userId, password); + + String patchLocation = metaData.getLocation(); if (patchLocation != null && !Utils.isEmptyString(patchLocation)) { + logger.finer("Patch location " + patchLocation + " platform " + platform); File cacheFile = new File(patchLocation); try { if (patch.fileName() == null) { patch.fileName(cacheFile.getName()); } - Files.copy(Paths.get(patchLocation), Paths.get(patchesFolderName, cacheFile.getName())); + if (platform.equals(ARM64_BLD) || platform.equalsIgnoreCase("generic")) { + Files.copy(Paths.get(patchLocation), Paths.get(patchesFolderName, ARM64_BLD, + cacheFile.getName())); + } + if (platform.equals(AMD64_BLD) || platform.equalsIgnoreCase("generic")) { + Files.copy(Paths.get(patchLocation), Paths.get(patchesFolderName, AMD64_BLD, + cacheFile.getName())); + } } catch (FileAlreadyExistsException ee) { - logger.warning("IMG-0077", patchFile.getKey()); + logger.warning("IMG-0077", patch.patchId()); } } else { - logger.severe("IMG-0024", patchFile.getKey()); + logger.severe("IMG-0024", patch.patchId()); } } if (!aruPatches.isEmpty()) { @@ -227,26 +260,35 @@ List resolveUserRequestedPatches(String psuVersion) providedVersion = patchId.substring(split + 1); patchId = patchId.substring(0, split); } - List patchVersions = AruUtil.rest().getPatches(patchId, userId, password) - .filter(p -> p.isApplicableToTarget(getTargetArchitecture().getAruPlatform())) - .collect(Collectors.toList()); - - // Stack Patch Bundle (SPB) is not a traditional patch. Patches in SPB are duplicates of recommended. - if (patchVersions.stream().anyMatch(AruPatch::isStackPatchBundle)) { - // Do not continue if the user specified a patch number that cannot be applied. - throw logger.throwing(new InvalidPatchNumberException(Utils.getMessage("IMG-0098", patchId))); - } + // If there are multiple platforms patches, we need to copy all of them to build context. + // In runtime, the docker file will determine which to use based on env. + + List buildPlatforms = getBuildPlatform(); + for (String buildPlatform : buildPlatforms) { + List patchVersions = AruUtil.rest().getPatches(patchId, userId, password) + .filter(p -> p.isApplicableToTarget(Architecture.fromString(buildPlatform).getAruPlatform())) + .collect(Collectors.toList()); - if (!patchVersions.isEmpty()) { - // if ARU found patches for the provided bug number, try to select the one the user needs by version - AruPatch selectedVersion = AruPatch.selectPatch(patchVersions, providedVersion, effectivePsuVersion, - getInstallerVersion()); + // Stack Patch Bundle (SPB) is not a traditional patch. Patches in SPB are duplicates of recommended. + if (patchVersions.stream().anyMatch(AruPatch::isStackPatchBundle)) { + // Do not continue if the user specified a patch number that cannot be applied. + throw logger.throwing(new InvalidPatchNumberException(Utils.getMessage("IMG-0098", patchId))); + } + + if (!patchVersions.isEmpty()) { + // if ARU found patches for the provided bug number, try to select the one the user needs by version + AruPatch selectedVersion = AruPatch.selectPatch(patchVersions, providedVersion, effectivePsuVersion, + getInstallerVersion()); - String psuVersionOfSelected = findPsuVersion(selectedVersion); - if (Utils.isEmptyString(psuVersion) && !Utils.isEmptyString(psuVersionOfSelected)) { - effectivePsuVersion = psuVersionOfSelected; + String psuVersionOfSelected = findPsuVersion(selectedVersion); + if (Utils.isEmptyString(psuVersion) && !Utils.isEmptyString(psuVersionOfSelected)) { + effectivePsuVersion = psuVersionOfSelected; + } + result.add(selectedVersion); + } else { + logger.warning("IMG-0123", patchId, buildPlatform); } - result.add(selectedVersion); + } } logger.exiting(result); @@ -272,33 +314,39 @@ List getRecommendedPatchList() throws AruException { throw new IllegalArgumentException(Utils.getMessage("IMG-0031")); } - if (recommendedPatches) { - // Get the latest PSU and its recommended patches - aruPatches = AruUtil.rest() - .getRecommendedPatches(getInstallerType(), getInstallerVersion(), getTargetArchitecture(), + List buildPlatforms = getBuildPlatform(); + for (String buildPlatform : buildPlatforms) { + if (recommendedPatches) { + // Get the latest PSU and its recommended patches + aruPatches = AruUtil.rest() + .getRecommendedPatches(getInstallerType(), getInstallerVersion(), + Architecture.fromString(buildPlatform), getCommonName(), + userId, password); + + if (aruPatches.isEmpty()) { + recommendedPatches = false; + logger.info("IMG-0084", getInstallerVersion()); + } else if (FmwInstallerType.isBaseWeblogicServer(getInstallerType())) { + // find and remove all ADR patches in the recommended patches list for base WLS installers + List discard = aruPatches.stream() + .filter(p -> p.description().startsWith("ADR FOR WEBLOGIC SERVER")) + .collect(Collectors.toList()); + // let the user know that the ADR patches will be discarded + discard.forEach(p -> logger.info("IMG-0085", p.patchId())); + aruPatches.removeAll(discard); + } + } else if (latestPsu) { + // PSUs for WLS and JRF installers are considered WLS patches + aruPatches = AruUtil.rest().getLatestPsu(getInstallerType(), getInstallerVersion(), + Architecture.fromString(buildPlatform), getCommonName(), userId, password); - if (aruPatches.isEmpty()) { - recommendedPatches = false; - logger.info("IMG-0084", getInstallerVersion()); - } else if (FmwInstallerType.isBaseWeblogicServer(getInstallerType())) { - // find and remove all ADR patches in the recommended patches list for base WLS installers - List discard = aruPatches.stream() - .filter(p -> p.description().startsWith("ADR FOR WEBLOGIC SERVER")) - .collect(Collectors.toList()); - // let the user know that the ADR patches will be discarded - discard.forEach(p -> logger.info("IMG-0085", p.patchId())); - aruPatches.removeAll(discard); - } - } else if (latestPsu) { - // PSUs for WLS and JRF installers are considered WLS patches - aruPatches = AruUtil.rest().getLatestPsu(getInstallerType(), getInstallerVersion(), getTargetArchitecture(), - userId, password); - - if (aruPatches.isEmpty()) { - latestPsu = false; - logger.fine("Latest PSU NOT FOUND, ignoring latestPSU flag"); + if (aruPatches.isEmpty()) { + latestPsu = false; + logger.fine("Latest PSU NOT FOUND, ignoring latestPSU flag"); + } } + } return aruPatches; @@ -313,7 +361,7 @@ private Path createPatchesTempDirectory() throws IOException { void prepareOpatchInstaller(String tmpDir, String opatchBugNumber) throws IOException, XPathExpressionException, AruException { logger.entering(opatchBugNumber); - String filePath = OPatchFile.getInstance(opatchBugNumber, userId, password, cache()).resolve(cache()); + String filePath = OPatchFile.getInstance(opatchBugNumber, userId, password).resolve(); String filename = new File(filePath).getName(); Files.copy(Paths.get(filePath), Paths.get(tmpDir, filename)); dockerfileOptions.setOPatchPatchingEnabled(); @@ -329,6 +377,15 @@ String getPassword() { return password; } + private String commonName; + + void setCommonName(String commonName) { + this.commonName = commonName; + } + + String getCommonName() { + return commonName; + } @Option( names = {"--user"}, @@ -405,4 +462,5 @@ String getPassword() { description = "Installer type. Default: WLS. Supported values: ${COMPLETION-CANDIDATES}" ) private FmwInstallerType installerType = FmwInstallerType.WLS; + } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CreateImage.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CreateImage.java index 42e39bdd1..699057d26 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CreateImage.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CreateImage.java @@ -5,9 +5,15 @@ import java.io.File; import java.time.Instant; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Callable; import com.oracle.weblogic.imagetool.api.model.CommandResponse; +import com.oracle.weblogic.imagetool.builder.AbstractCommand; +import com.oracle.weblogic.imagetool.builder.BuildCommand; +import com.oracle.weblogic.imagetool.builder.ManifestCommand; +import com.oracle.weblogic.imagetool.builder.PushCommand; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; import com.oracle.weblogic.imagetool.util.Utils; @@ -40,8 +46,52 @@ public CommandResponse call() throws Exception { // Create Dockerfile String dockerfile = Utils.writeDockerfile(buildDir() + File.separator + "Dockerfile", "Create_Image.mustache", dockerfileOptions, dryRun); + BuildCommand buildCommand = getInitialBuildCmd(buildDir()); + if ("podman".equalsIgnoreCase(buildCommand.getExecutable())) { + List abstractCommands = new ArrayList<>(); + List buildPlatforms = buildCommand.getBuildPlatforms(); + if (buildPlatforms.size() > 1) { + // build each platform first, then build manifest and the push the manifest if needed... + ManifestCommand createManifestCommand = new ManifestCommand(buildCommand.getExecutable(), + buildCommand.getContext()); + String manifestName = buildCommand.getTagName(); + createManifestCommand.create().name(manifestName); + abstractCommands.add(createManifestCommand); + logger.info("IMG-0140", manifestName); - runDockerCommand(dockerfile, getInitialBuildCmd(buildDir())); + for (String buildPlatform : buildPlatforms) { + buildCommand.substitutePlatform(buildPlatform); + String platformTag = buildCommand.substituteTagName(imageTag + "-" + + buildPlatform.replace('/', '-')); + runDockerCommand(dockerfile, buildCommand); + PushCommand pushCommand = new PushCommand(buildCommand.getExecutable(), + buildCommand.getContext()).tag(platformTag); + abstractCommands.add(pushCommand); + logger.info("IMG-0143", platformTag); + + ManifestCommand addManifestCommand = new ManifestCommand(buildCommand.getExecutable(), + buildCommand.getContext()); + addManifestCommand.add().name(manifestName).tag(platformTag); + abstractCommands.add(addManifestCommand); + logger.info("IMG-0141", manifestName, platformTag); + } + + ManifestCommand pushManifestCommand = new ManifestCommand(buildCommand.getExecutable(), + buildCommand.getContext()); + pushManifestCommand.push().name(manifestName).tag(manifestName); + abstractCommands.add(pushManifestCommand); + logger.info("IMG-0142", manifestName); + for (AbstractCommand abstractCommand : abstractCommands) { + logger.info("IMG-0144", abstractCommand.toString()); + runDockerCommand(dockerfile, abstractCommand); + } + + } else { + runDockerCommand(dockerfile, buildCommand); + } + } else { + runDockerCommand(dockerfile, buildCommand); + } if (!dryRun) { wdtOptions.handleResourceTemplates(imageTag()); } @@ -55,6 +105,7 @@ public CommandResponse call() throws Exception { return successfulBuildResponse(startTime); } + @ArgGroup(exclusive = false, heading = "WDT Options%n") private final WdtFullOptions wdtOptions = new WdtFullOptions(); } \ No newline at end of file diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/RebaseImage.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/RebaseImage.java deleted file mode 100644 index cad7be316..000000000 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/RebaseImage.java +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) 2019, 2022, Oracle and/or its affiliates. -// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. - -package com.oracle.weblogic.imagetool.cli.menu; - -import java.io.File; -import java.time.Instant; -import java.util.Properties; -import java.util.concurrent.Callable; - -import com.oracle.weblogic.imagetool.api.model.CommandResponse; -import com.oracle.weblogic.imagetool.logging.LoggingFacade; -import com.oracle.weblogic.imagetool.logging.LoggingFactory; -import com.oracle.weblogic.imagetool.util.Utils; -import picocli.CommandLine.Command; -import picocli.CommandLine.Option; - -@Command( - name = "rebase", - description = "Copy domain from one image to another", - requiredOptionMarker = '*', - abbreviateSynopsis = true -) -public class RebaseImage extends CommonCreateOptions implements Callable { - - private static final LoggingFacade logger = LoggingFactory.getLogger(RebaseImage.class); - - @Override - public CommandResponse call() throws Exception { - logger.entering(); - Instant startTime = Instant.now(); - - String newOracleHome = null; - String newJavaHome = null; - - try { - initializeOptions(); - - if (Utils.isEmptyString(sourceImage)) { - // sourceImage is a required parameter. This error will only occur if the user passes an empty string. - return CommandResponse.error(Utils.getMessage("IMG-0117")); - } - - logger.finer("IMG-0002", sourceImage); - dockerfileOptions.setSourceImage(sourceImage); - - logger.info("IMG-0091", sourceImage); - Properties sourceImageProperties = Utils.getBaseImageProperties(buildEngine, sourceImage, - "/probe-env/inspect-image.sh", buildDir()); - - String oldOracleHome = sourceImageProperties.getProperty("oracleHome", null); - String oldJavaHome = sourceImageProperties.getProperty("javaHome", null); - String domainHome = sourceImageProperties.getProperty("domainHome", null); - String wdtHome = sourceImageProperties.getProperty("wdtHome", null); - String modelHome = sourceImageProperties.getProperty("wdtModelHome", null); - boolean modelOnly = Boolean.parseBoolean(sourceImageProperties.getProperty("wdtModelOnly", null)); - - // If the user specified --targetImage, collect and apply the properties for the new image. - if (!Utils.isEmptyString(targetImage)) { - logger.finer("IMG-0002", targetImage); - dockerfileOptions.setTargetImage(targetImage); - dockerfileOptions.setRebaseToTarget(true); - - Properties targetImageProperties = Utils.getBaseImageProperties(buildEngine, targetImage, - "/probe-env/inspect-image.sh", buildDir()); - newOracleHome = targetImageProperties.getProperty("oracleHome", null); - newJavaHome = targetImageProperties.getProperty("javaHome", null); - useFileOwnerFromTarget(targetImageProperties); - } else { - dockerfileOptions.setRebaseToNew(true); - } - - if (newJavaHome != null && !newJavaHome.equals(oldJavaHome)) { - return CommandResponse.error(Utils.getMessage("IMG-0026")); - } - - if (newOracleHome != null && !newOracleHome.equals(oldOracleHome)) { - return CommandResponse.error(Utils.getMessage("IMG-0021")); - } - - if (Utils.isEmptyString(domainHome)) { - return CommandResponse.error(Utils.getMessage("IMG-0025")); - } - - if (modelOnly) { - logger.info("IMG-0090", domainHome); - if (Utils.isEmptyString(modelHome)) { - logger.info("IMG-0089", dockerfileOptions.wdt_model_home()); - } - } - - dockerfileOptions - .setDomainHome(domainHome) - .setWdtHome(wdtHome) - .setWdtModelHome(modelHome) - .setWdtModelOnly(modelOnly); - - if (dockerfileOptions.isRebaseToNew()) { - prepareNewImage(); - } - - // Create Dockerfile - String dockerfile = Utils.writeDockerfile(buildDir() + File.separator + "Dockerfile", - "Rebase_Image.mustache", dockerfileOptions, dryRun); - - // add directory to pass the context - runDockerCommand(dockerfile, getInitialBuildCmd(buildDir())); - } catch (Exception ex) { - logger.fine("**ERROR**", ex); - return CommandResponse.error(ex.getMessage()); - } finally { - cleanup(); - } - logger.exiting(); - return successfulBuildResponse(startTime); - } - - private void useFileOwnerFromTarget(Properties imageProperties) { - String userid = imageProperties.getProperty("oracleHomeUser", null); - String groupid = imageProperties.getProperty("oracleHomeGroup", null); - if (!Utils.isEmptyString(userid)) { - dockerfileOptions.setUserId(userid); - } - if (!Utils.isEmptyString(groupid)) { - dockerfileOptions.setGroupId(groupid); - } - } - - @Option( - names = {"--sourceImage"}, - required = true, - description = "Docker image containing source domain." - ) - private String sourceImage; - - @Option( - names = {"--targetImage"}, - description = "Docker image with updated JDK or MW Home" - ) - private String targetImage; - -} \ No newline at end of file diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/UpdateImage.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/UpdateImage.java index 8ad02fb16..86189dee5 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/UpdateImage.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/UpdateImage.java @@ -27,7 +27,6 @@ import picocli.CommandLine.Command; import picocli.CommandLine.Option; -import static com.oracle.weblogic.imagetool.cachestore.CacheStoreFactory.cache; @Command( name = "update", @@ -101,8 +100,8 @@ public CommandResponse call() throws Exception { String password = getPassword(); if (shouldUpdateOpatch()) { - OPatchFile opatchFile = OPatchFile.getInstance(opatchBugNumber, userId, password, cache()); - String opatchFilePath = opatchFile.resolve(cache()); + OPatchFile opatchFile = OPatchFile.getInstance(opatchBugNumber, userId, password); + String opatchFilePath = opatchFile.resolve(); // if there is a newer version of OPatch than contained in the image, update OPatch if (Utils.compareVersions(opatchVersion, opatchFile.getVersion()) < 0) { diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/WdtBaseOptions.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/WdtBaseOptions.java index e0aaf2bfd..51742fd22 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/WdtBaseOptions.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/WdtBaseOptions.java @@ -15,11 +15,11 @@ import com.oracle.weblogic.imagetool.installer.InstallerType; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.util.Architecture; import com.oracle.weblogic.imagetool.util.DockerfileOptions; import com.oracle.weblogic.imagetool.util.Utils; import picocli.CommandLine.Option; -import static com.oracle.weblogic.imagetool.cachestore.CacheStoreFactory.cache; public class WdtBaseOptions { @@ -84,8 +84,8 @@ public void handleWdtArgs(DockerfileOptions dockerfileOptions, String tmpDir) th } if (!skipWdtInstaller()) { - CachedFile wdtInstaller = new CachedFile(InstallerType.WDT, wdtVersion); - Path wdtfile = wdtInstaller.copyFile(cache(), tmpDir); + CachedFile wdtInstaller = new CachedFile(InstallerType.WDT, wdtVersion, Architecture.GENERIC); + Path wdtfile = wdtInstaller.copyFile(tmpDir); dockerfileOptions.setWdtInstallerFilename(wdtfile.getFileName().toString()); } logger.exiting(); diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/FmwInstallerType.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/FmwInstallerType.java index d22dbab5a..e83da9452 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/FmwInstallerType.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/FmwInstallerType.java @@ -25,6 +25,7 @@ public enum FmwInstallerType { // Oracle WebLogic Server WLS(Utils.toSet(AruProduct.WLS, AruProduct.COH, AruProduct.FMWPLAT, AruProduct.FIT, AruProduct.JDBC, + AruProduct.FMW_GLCM, AruProduct.OSS), InstallerType.WLS), // Added OSS for a special patching issue for 12.2.1.4 JDBC fix WLSSLIM(Utils.toSet(WLS.products), InstallerType.WLSSLIM), @@ -80,7 +81,7 @@ public enum FmwInstallerType { WCS(Utils.toSet(FMW.products, AruProduct.WCS), InstallerType.FMW, InstallerType.WCS), OHS(Utils.toSet(AruProduct.OHS, AruProduct.OAM_WG, AruProduct.WLS, AruProduct.JDBC, AruProduct.FMWPLAT, - AruProduct.OSS, AruProduct.FIT), + AruProduct.OSS, AruProduct.FIT, AruProduct.JRF, AruProduct.FMW_GLCM), InstallerType.OHS, InstallerType.DB19), ODI(Collections.singleton(AruProduct.ODI), InstallerType.ODI) @@ -111,8 +112,8 @@ public Set products() { private static final LoggingFacade logger = LoggingFactory.getLogger(FmwInstallerType.class); /** - * Return a list of all WebLogic Server types (not JRF types). - * @return list of WLS enum types. + * Returns true if the installer type is a WLS installer, WLS, WLSDEV, or WLSSLIM. + * @return true if the installer is a WLS installer type. */ public static boolean isBaseWeblogicServer(FmwInstallerType value) { return weblogicServerTypes.contains(value); diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/InstallerMetaData.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/InstallerMetaData.java new file mode 100644 index 000000000..2b783584a --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/InstallerMetaData.java @@ -0,0 +1,127 @@ +// 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 com.oracle.weblogic.imagetool.installer; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Objects; + +import com.oracle.weblogic.imagetool.util.Architecture; +import com.oracle.weblogic.imagetool.util.Utils; + +public class InstallerMetaData { + private String architecture; + private String location; + private String digest; + private String dateAdded; + private String productVersion; + private String baseFMWVersion; + + /** + * Constructor InstallerMetaData stores details about this installer. + * @param architecture platform linux/arm64, linux/amd64 + * @param location file path of the installer + * @param digest sha256 hash value + * @param dateAdded date added + * @param productVersion version of this installer + * @param baseFMWVersion base WLS version used by this installer + */ + public InstallerMetaData(String architecture, String location, String digest, String dateAdded, + String productVersion, String baseFMWVersion) { + this.architecture = Utils.standardPlatform(architecture); + this.location = location; + this.digest = digest; + this.dateAdded = dateAdded; + this.productVersion = productVersion; + this.baseFMWVersion = baseFMWVersion; + } + + /** + * Constructor InstallerMetaData stores details about this installer. + * @param architecture platform linux/arm64, linux/amd64 + * @param location file path of the installer + * @param productVersion real version of this installer + */ + public InstallerMetaData(String architecture, String location, String productVersion, String baseFMWVersion) { + this.architecture = Utils.standardPlatform(architecture); + this.location = location; + this.productVersion = productVersion; + if (location != null && Files.exists(Paths.get(location))) { + this.digest = Utils.getSha256Hash(location); + this.dateAdded = Utils.getTodayDate(); + } + this.baseFMWVersion = baseFMWVersion; + } + + public String getArchitecture() { + return architecture; + } + + public String getLocation() { + return location; + } + + public String getDigest() { + return digest; + } + + public String getDateAdded() { + return dateAdded; + } + + public String getProductVersion() { + return productVersion; + } + + public String getBaseFMWVersion() { + return baseFMWVersion; + } + + public void setBaseFMWVersion(String baseFMWVersion) { + this.baseFMWVersion = baseFMWVersion; + } + + /** + * Return standard platform name from the possible names. + * @param platform input value to convert + * @return standardized platform name + */ + public String standardPlatform(String platform) { + if (Architecture.AMD64.getAcceptableNames().contains(platform)) { + return "linux/amd64"; + } + if (Architecture.ARM64.getAcceptableNames().contains(platform)) { + return "linux/arm64"; + } + return "Generic"; + } + + @Override + public String toString() { + return "InstallerMetaData [platform=" + architecture + ", location=" + location + ", hash=" + digest + ", " + + "dateAdded=" + dateAdded + ", version=" + productVersion + ", baseFMWVersion=" + baseFMWVersion + + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InstallerMetaData metaData = (InstallerMetaData) o; + return Objects.equals(architecture, metaData.architecture) + && Objects.equals(location, metaData.location) && Objects.equals(digest, metaData.digest) + && Objects.equals(dateAdded, metaData.dateAdded) + && Objects.equals(baseFMWVersion, metaData.baseFMWVersion) + && Objects.equals(productVersion, metaData.productVersion); + } + + @Override + public int hashCode() { + return Objects.hash(architecture, location, digest, dateAdded, productVersion, baseFMWVersion); + } +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/InstallerType.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/InstallerType.java index d58f354d4..3091777e6 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/InstallerType.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/InstallerType.java @@ -41,8 +41,23 @@ public enum InstallerType { this.value = value; } + /** + * Return the enum value from string. + * @param value input value + * @return InstallerType + */ + public static InstallerType fromString(String value) { + for (InstallerType installerType : InstallerType.values()) { + if (installerType.toString().equalsIgnoreCase(value)) { + return installerType; + } + } + throw new IllegalArgumentException(value); + } + @Override public String toString() { return value; } + } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstall.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstall.java index 2c0ef20fa..1ee6cab7f 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstall.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstall.java @@ -7,6 +7,7 @@ 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.Enumeration; @@ -15,12 +16,16 @@ import java.util.zip.ZipFile; import com.oracle.weblogic.imagetool.api.model.CachedFile; -import com.oracle.weblogic.imagetool.cachestore.CacheStore; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import com.oracle.weblogic.imagetool.util.Architecture; import com.oracle.weblogic.imagetool.util.Utils; +import static com.oracle.weblogic.imagetool.util.Constants.AMD64_BLD; +import static com.oracle.weblogic.imagetool.util.Constants.ARM64_BLD; +import static com.oracle.weblogic.imagetool.util.Constants.CTX_FMW; + public class MiddlewareInstall { private static final LoggingFacade logger = LoggingFactory.getLogger(MiddlewareInstall.class); @@ -33,19 +38,72 @@ public class MiddlewareInstall { * @param type the requested middleware install type */ public MiddlewareInstall(FmwInstallerType type, String version, List responseFiles, - Architecture target) throws FileNotFoundException { + List buildPlatform, String buildEngine, String commonName) + throws FileNotFoundException { logger.info("IMG-0039", type.installerListString(), version); fmwInstallerType = type; + ConfigManager configManager = ConfigManager.getInstance(); + if (buildPlatform == null) { + buildPlatform = new ArrayList<>(); + } + if (buildPlatform.isEmpty()) { + if (configManager.getDefaultBuildPlatform() != null) { + buildPlatform.add(configManager.getDefaultBuildPlatform()); + } else { + buildPlatform.add(Architecture.getLocalArchitecture().name()); + } + } + Architecture localArchitecture = Architecture.getLocalArchitecture(); + String originalType = type.toString(); + + + for (InstallerType installerType : type.installerList()) { + for (String platform : buildPlatform) { + + logger.info("IMG-0153", installerType, version, platform); + platform = Utils.standardPlatform(platform); + MiddlewareInstallPackage pkg = new MiddlewareInstallPackage(); + Architecture arch = Architecture.fromString(platform); + if (localArchitecture != arch) { + logger.warning("IMG-0146"); + } + pkg.type = installerType; + if (AMD64_BLD.equals(platform)) { + pkg.installer = new CachedFile(installerType, version, Architecture.AMD64, commonName); + } + if (ARM64_BLD.equals(platform)) { + pkg.installer = new CachedFile(installerType, version, Architecture.ARM64, commonName); + } + + // get the details from cache of whether there is a base version of WLS required. + String useVersion = version; + if (type.installerList().size() > 1 && installerType == InstallerType.FMW) { + InstallerMetaData productData = configManager.getInstallerForPlatform( + InstallerType.fromString(originalType), + Architecture.fromString(platform), version); + if (productData == null) { + throw new IllegalArgumentException(Utils.getMessage("IMG-0145", + InstallerType.fromString(originalType), + Architecture.fromString(platform), version)); + } + + useVersion = productData.getBaseFMWVersion(); + } - for (InstallerType installer : type.installerList()) { - MiddlewareInstallPackage pkg = new MiddlewareInstallPackage(); - pkg.type = installer; - pkg.installer = new CachedFile(installer, version, target); - pkg.responseFile = new DefaultResponseFile(installer, type); - if (installer.equals(InstallerType.DB19)) { - pkg.preinstallCommands = Collections.singletonList("34761383/changePerm.sh /u01/oracle"); + InstallerMetaData metaData = configManager.getInstallerForPlatform(installerType, arch, useVersion); + if (metaData == null) { + throw new IllegalArgumentException(Utils.getMessage("IMG-0145", installerType, + arch, useVersion)); + } + pkg.installerPath = Paths.get(metaData.getLocation()); + pkg.installerFilename = pkg.installerPath.getFileName().toString(); + pkg.responseFile = new DefaultResponseFile(installerType, type); + pkg.platform = platform; + if (installerType.equals(InstallerType.DB19)) { + pkg.preinstallCommands = Collections.singletonList("34761383/changePerm.sh /u01/oracle"); + } + addInstaller(pkg); } - addInstaller(pkg); } setResponseFiles(responseFiles); } @@ -75,19 +133,26 @@ private static String getJarNameFromInstaller(Path installerFile) throws IOExcep /** * Copy all necessary installers to the build context directory. - * @param cacheStore cache where the installers are defined. * @param buildContextDir the directory where the installers should be copied. * @throws IOException if any of the copy commands fails. */ - public void copyFiles(CacheStore cacheStore, String buildContextDir) throws IOException { + public void copyFiles(String buildContextDir) throws IOException { logger.entering(); + for (MiddlewareInstallPackage installPackage: installerFiles) { - Path filePath = installPackage.installer.copyFile(cacheStore, buildContextDir); - installPackage.installerFilename = filePath.getFileName().toString(); - installPackage.jarName = getJarNameFromInstaller(filePath); + String buildContextDestination = buildContextDir; + // based on the platform copy to sub directory + if (installPackage.platform.equals(AMD64_BLD)) { + buildContextDestination = buildContextDestination + "/" + CTX_FMW + AMD64_BLD; + } else if (installPackage.platform.equals(ARM64_BLD)) { + buildContextDestination = buildContextDestination + "/" + CTX_FMW + ARM64_BLD; + } + Files.copy(installPackage.installerPath, + Paths.get(buildContextDestination).resolve(installPackage.installerPath.getFileName())); + installPackage.jarName = getJarNameFromInstaller(installPackage.installerPath); installPackage.isZip = installPackage.installerFilename.endsWith(".zip"); installPackage.isBin = installPackage.jarName.endsWith(".bin"); - installPackage.responseFile.copyFile(buildContextDir); + installPackage.responseFile.copyFile(buildContextDestination); } logger.exiting(); } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstallPackage.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstallPackage.java index 2cb06550f..e4780df96 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstallPackage.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstallPackage.java @@ -3,17 +3,20 @@ package com.oracle.weblogic.imagetool.installer; +import java.nio.file.Path; import java.util.List; import com.oracle.weblogic.imagetool.api.model.CachedFile; public class MiddlewareInstallPackage { - InstallerType type; - ResponseFile responseFile; - CachedFile installer; - String installerFilename; - String jarName; - List preinstallCommands; - boolean isZip = true; - boolean isBin = false; + public InstallerType type; + public ResponseFile responseFile; + public CachedFile installer; + public Path installerPath; + public String installerFilename; + public String jarName; + public List preinstallCommands; + public boolean isZip = true; + public boolean isBin = false; + public String platform; } diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/logging/ConsoleFormatter.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/logging/ConsoleFormatter.java index 3182cce6c..a31b9f4d6 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/logging/ConsoleFormatter.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/logging/ConsoleFormatter.java @@ -17,7 +17,7 @@ * Format is "[ LEVEL ] message", with optional throw-ables. */ public class ConsoleFormatter extends Formatter { - private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + private static final String LINE_SEPARATOR = System.lineSeparator(); public static final Pattern colorPattern = Pattern.compile("\\[\\[([a-z]+): (.+?)]]"); static { diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/logging/FileFormatter.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/logging/FileFormatter.java index 2a2ea4341..d06937d8d 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/logging/FileFormatter.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/logging/FileFormatter.java @@ -20,7 +20,7 @@ public class FileFormatter extends Formatter { private static final Pattern CATALOG_KEY_PATTERN = Pattern.compile(CATALOG_KEY_PATTERN_STRING); private static final String DATE_FORMAT_STRING = "####<{0,date,yyyy.MM.dd} {0,time,HH:mm:ss}>"; - private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + private static final String LINE_SEPARATOR = System.lineSeparator(); private Object[] args; private MessageFormat formatter; diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/patch/PatchMetaData.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/patch/PatchMetaData.java new file mode 100644 index 000000000..1d72e40d1 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/patch/PatchMetaData.java @@ -0,0 +1,126 @@ +// 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 com.oracle.weblogic.imagetool.patch; + +import java.util.Objects; + +import com.oracle.weblogic.imagetool.util.Utils; + +public class PatchMetaData { + private String architecture; + private String location; + private String digest; + private String dateAdded; + private String patchVersion; + private String description; + + /** + * Constructor. + * @param architecture platform + * @param location file path of the patch + * @param digest sha256 hash + * @param dateAdded date added + * @param patchVersion version + */ + public PatchMetaData(String architecture, String location, String digest, String dateAdded, String patchVersion, + String description) { + this.architecture = Utils.standardPlatform(architecture);; + this.location = location; + this.digest = digest; + this.dateAdded = dateAdded; + this.patchVersion = patchVersion; + this.description = description; + } + + /** + * Constructor. + * @param architecture platform + * @param location file path of the patch + * @param patchVersion version + * @param description description of the patch + */ + public PatchMetaData(String architecture, String location, String patchVersion, String description) { + this.architecture = Utils.standardPlatform(architecture);; + this.location = location; + this.digest = Utils.getSha256Hash(location); + this.dateAdded = Utils.getTodayDate(); + this.patchVersion = patchVersion; + this.description = description; + } + + /** + * Constructor. + * @param architecture platform + * @param location file path of the patch + * @param patchVersion version + * @param description description of the patch + * @param dateAdded date added + */ + public PatchMetaData(String architecture, String location, String patchVersion, String description, + String dateAdded) { + this.architecture = architecture; + this.location = location; + this.digest = Utils.getSha256Hash(location); + this.dateAdded = dateAdded; + this.patchVersion = patchVersion; + this.description = description; + } + + public String getArchitecture() { + return architecture; + } + + public String getLocation() { + return location; + } + + public String getDigest() { + return digest; + } + + public String getDateAdded() { + return dateAdded; + } + + public String getPatchVersion() { + return patchVersion; + } + + public String getDescription() { + return description; + } + + @Override + public String toString() { + return "PatchMetaData{" + + "platform='" + architecture + '\'' + + ", location='" + location + '\'' + + ", digest='" + digest + '\'' + + ", dateAdded='" + dateAdded + '\'' + + ", patchVersion='" + patchVersion + '\'' + + ", description='" + description + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PatchMetaData metaData = (PatchMetaData) o; + return Objects.equals(architecture, metaData.architecture) + && Objects.equals(location, metaData.location) && Objects.equals(digest, metaData.digest) + && Objects.equals(dateAdded, metaData.dateAdded) + && Objects.equals(patchVersion, metaData.patchVersion) + && Objects.equals(description, metaData.description); + } + + @Override + public int hashCode() { + return Objects.hash(architecture, location, digest, dateAdded, patchVersion, description); + } +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/ConfigManager.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/ConfigManager.java new file mode 100644 index 000000000..9db289956 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/ConfigManager.java @@ -0,0 +1,305 @@ +// 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 com.oracle.weblogic.imagetool.settings; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import com.oracle.weblogic.imagetool.aru.AruPatch; +import com.oracle.weblogic.imagetool.cachestore.CacheStore; +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; +import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.util.Architecture; + +import static com.oracle.weblogic.imagetool.cachestore.CacheStore.CACHE_DIR_ENV; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.ARCHITECTURE; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.BASE_FMW_VERSION; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.DATE_ADDED; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.DESCRIPTION; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.DIGEST; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.LOCATION; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.PATCH_VERSION; +import static com.oracle.weblogic.imagetool.settings.YamlFileConstants.PRODUCT_VERSION; +import static com.oracle.weblogic.imagetool.util.Utils.getTodayDate; + +public class ConfigManager { + private UserSettingsFile userSettingsFile; + private SettingsFile installerSettingsFile; + private SettingsFile patchSettingsFile; + private static ConfigManager instance; + private static final LoggingFacade logger = LoggingFactory.getLogger(UserSettingsFile.class); + private static CacheStore cacheStore = new CacheStore(); + + private ConfigManager(Path userSettingsFileName) { + if (userSettingsFileName != null) { + userSettingsFile = new UserSettingsFile(userSettingsFileName); + } else { + userSettingsFile = new UserSettingsFile(); + } + + } + + /** + * Return the singleton instance of ConfigManager. + * @return ConfigManager instance + */ + public static synchronized ConfigManager getInstance() { + try { + Path file; + if (instance == null) { + String result = System.getenv(CACHE_DIR_ENV); + if (result != null) { + file = Paths.get(result, "settings.yaml"); + } else { + file = Paths.get(System.getProperty("user.home"), ".imagetool", + "settings.yaml"); + } + if (!Files.exists(file)) { + Files.createDirectories(file.getParent()); + Files.createFile(file); + initImageTool(file); + } + return new ConfigManager(file); + } + return instance; + + } catch (IOException e) { + throw new IllegalArgumentException(e.getMessage()); + } + } + + /** + * Return the singleton instance of ConfigManager. + * @return ConfigManager instance + */ + public static synchronized ConfigManager getInstance(Path fileName) { + // Always reload with file provided + instance = new ConfigManager(fileName); + return instance; + } + + private static void initImageTool(Path file) throws IOException { + Path parentPath = file.getParent(); + Files.createFile(Paths.get(parentPath.toString(), "patches.yaml")); + Files.createFile(Paths.get(parentPath.toString(), "installer.yaml")); + String dirName = "downloaded_patches"; + Path path = Paths.get(parentPath.toString(), dirName); + List fileContents = Arrays.asList("patchDirectory: " + path.toString()); + Files.createDirectory(path); + Files.write(file, fileContents); + } + + public String getPatchDirectory() { + return userSettingsFile.getPatchDirectory(); + } + + public String getPatchDetailsFile() { + return userSettingsFile.returnPatchSettingsFile(); + } + + public String getBuildEngine() { + return userSettingsFile.getBuildEngine(); + } + + public String getInstallerDetailsFile() { + return userSettingsFile.returnInstallerSettingsFile(); + } + + /** + * Return the ARU retry interval. + * @return retry interval + */ + public int getAruRetryInterval() { + return userSettingsFile.getAruRetryInterval(); + } + + /** + * Return the ARU retry max. + * @return retry max + */ + public int getAruRetryMax() { + return userSettingsFile.getAruRetryMax(); + } + + /** + * Return the installer setting file. + * @return SettingsFile + */ + public SettingsFile getInstallerSettingsFile() { + if (installerSettingsFile == null) { + installerSettingsFile = new SettingsFile(Paths.get(userSettingsFile.returnInstallerSettingsFile())); + } + return installerSettingsFile; + } + + /** + * Return the patch setting file. + * @return SettingsFile + */ + public SettingsFile getPatchSettingsFile() { + if (patchSettingsFile == null) { + patchSettingsFile = new SettingsFile(Paths.get(userSettingsFile.returnPatchSettingsFile())); + } + return patchSettingsFile; + } + + public String getDefaultBuildPlatform() { + return userSettingsFile.getDefaultBuildPlatform(); + } + + public Map> getAllPatches() { + return cacheStore.getAllPatches(); + } + + public void saveAllPatches(Map> allPatches) throws IOException { + cacheStore.saveAllPatches(allPatches); + } + + public void addPatch(String bugNumber, String patchArchitecture, String patchLocation, + String patchVersion, String description) throws IOException { + cacheStore.addPatch(bugNumber, patchArchitecture, patchLocation, patchVersion, description); + } + + public void addPatch(String bugNumber, String patchArchitecture, String patchLocation, + String patchVersion, String description, String dateAdded) throws IOException { + cacheStore.addPatch(bugNumber, patchArchitecture, patchLocation, patchVersion, description, dateAdded); + } + + public PatchMetaData getPatchForPlatform(String platformName, String bugNumber, String version) { + return cacheStore.getPatchForPlatform(platformName, bugNumber, version); + } + + /** + * Return the metadata for the platformed installer. + * @param platformName platform name + * @param installerVersion version of the installer + * @return InstallerMetaData meta data for the installer + */ + public InstallerMetaData getInstallerForPlatform(InstallerType installerType, Architecture platformName, + String installerVersion) { + return cacheStore.getInstallerForPlatform(installerType, platformName, installerVersion, installerVersion); + } + + /** + * Return the metadata for the platformed installer. + * @param installerType installer type + * @param platformName platform name + * @param installerVersion version of the installer + * @param commonName common name of the installer + * @return InstallerMetaData meta data for the installer + */ + public InstallerMetaData getInstallerForPlatform(InstallerType installerType, Architecture platformName, + String installerVersion, String commonName) { + return cacheStore.getInstallerForPlatform(installerType, platformName, installerVersion, commonName); + } + + /** + * Return the metadata for the platformed installer. + * @param installerType installer type + * @param commonName common name of the installer + * @return InstallerMetaData meta data for the installer + */ + public List listInstallerByCommonName(InstallerType installerType, String commonName) { + return cacheStore.listInstallerByCommonName(installerType, commonName); + } + + /** + * Return all the installers based on the configured directory for the yaml file. + * @return map of installers + */ + public Map>> getInstallers() { + return cacheStore.getInstallers(); + } + + /** + * Return default wls installer version. + * @return default wls version if set + */ + public String getDefaultWLSVersion() { + return userSettingsFile.returnDefaultWLSVersion(); + } + + /** + * Return default jdk installer version. + * @return default wls version if set + */ + public String getDefaultJDKVersion() { + return userSettingsFile.returnDefaultJDKVersion(); + } + + /** + * Return default wdt installer version. + * @return default wls version if set + */ + public String getDefaultWDTVersion() { + return userSettingsFile.returnDefaultWDTVersion(); + } + + /** + * Add installer. + * @param installerType installer type + * @param commonName common name + * @param metaData meta data of the installer + * @throws IOException when error + */ + public void addInstaller(InstallerType installerType, String commonName, InstallerMetaData metaData) + throws IOException { + cacheStore.addInstaller(installerType, commonName, metaData); + } + + /** + * Save all installers. + * @param allInstallers map of installers + * @throws IOException any error + */ + public void saveAllInstallers(Map>> allInstallers) throws IOException { + cacheStore.saveAllInstallers(allInstallers); + } + + /** + * Return the metadata for the patches by bug number. + * @param bugNumber version of the installer + * @return list of AruPatch + */ + + public List getAruPatchForBugNumber(String bugNumber) { + return cacheStore.getAruPatchForBugNumber(bugNumber); + } + + + private InstallerMetaData createInstallerMetaData(Map objectData) { + String hash = (String) objectData.get(DIGEST); + String dateAdded = (String) objectData.get(DATE_ADDED); + if (dateAdded == null) { + dateAdded = getTodayDate(); + } + String location = (String) objectData.get(LOCATION); + String productVersion = (String) objectData.get(PRODUCT_VERSION); + String platform = (String) objectData.get(ARCHITECTURE); + String baseFMWVersion = (String) objectData.get(BASE_FMW_VERSION); + return new InstallerMetaData(platform, location, hash, dateAdded, productVersion, baseFMWVersion); + } + + private PatchMetaData createPatchMetaData(Map objectData) { + String hash = (String) objectData.get(DIGEST); + String dateAdded = (String) objectData.get(DATE_ADDED); + if (dateAdded == null) { + dateAdded = getTodayDate(); + } + String location = (String) objectData.get(LOCATION); + String productVersion = (String) objectData.get(PATCH_VERSION); + String platform = (String) objectData.get(ARCHITECTURE); + String description = (String) objectData.get(DESCRIPTION); + return new PatchMetaData(platform, location, hash, dateAdded, productVersion, description); + } + +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/ConfigSettings.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/ConfigSettings.java new file mode 100644 index 000000000..51024a738 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/ConfigSettings.java @@ -0,0 +1,102 @@ +// 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. + +package com.oracle.weblogic.imagetool.settings; + +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.util.Utils; + +/** + * Wrapper class for UserSettings where default values are returned when no user setting exists for a given attribute. + */ +public class ConfigSettings { + private static final LoggingFacade logger = LoggingFactory.getLogger(ConfigSettings.class); + + UserSettingsFile userSettings; + + /** + * Parent directory for the build context directory. + * A temporary folder created under "Build Directory" with the prefix "wlsimgbuilder_tempXXXXXXX" will be created + * to hold the image build context (files, and Dockerfile). + */ + public String imageBuildContextDirectory() { + String result = userSettings.getBuildContextDirectory(); + if (Utils.isEmptyString(result)) { + return "."; + } + return result; + } + + /** + * Patch download directory. + * The directory for storing and using downloaded patches. + */ + public String patchDirectory() { + String result = userSettings.getPatchDirectory(); + if (Utils.isEmptyString(result)) { + return UserSettingsFile.getSettingsDirectory().resolve("patches").toString(); + } + return result; + } + + /** + * Installer download directory. + * The directory for storing and using downloaded Java and middleware installers. + */ + public String installerDirectory() { + String result = userSettings.getInstallerDirectory(); + if (Utils.isEmptyString(result)) { + return UserSettingsFile.getSettingsDirectory().resolve("installers").toString(); + } + return result; + } + + /** + * Container image build tool. + * Allow the user to specify the executable that will be used to build the container image. For example, + * "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker". + */ + public String buildEngine() { + String result = userSettings.getBuildEngine(); + if (Utils.isEmptyString(result)) { + return "docker"; + } + return result; + } + + /** + * Container image runtime tool. + * Allow the user to specify the executable that will be used to run and/or interrogate images. For example, + * "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker". + */ + public String containerEngine() { + String result = userSettings.getContainerEngine(); + if (Utils.isEmptyString(result)) { + return buildEngine(); + } + return result; + } + + /** + * REST calls to ARU should be retried up to this number of times. + */ + public Integer aruRetryMax() { + Integer result = userSettings.getAruRetryMax(); + if (result == null) { + return 10; + } + return result; + } + + /** + * The time between each ARU REST call in milliseconds. + */ + public int aruRetryInterval() { + Integer result = userSettings.getAruRetryInterval(); + if (result == null) { + return 500; + } + return result; + } +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/InstallerSettings.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/InstallerSettings.java new file mode 100644 index 000000000..267caebdb --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/InstallerSettings.java @@ -0,0 +1,31 @@ +// 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. + +package com.oracle.weblogic.imagetool.settings; + +import java.util.Map; + + +public class InstallerSettings { + public String defaultVersion; + + public InstallerSettings(Map settings) { + applySettings(settings); + } + + /** + * Apply settings that were loaded from YAML. + * @param settings map from YAML parser + */ + private void applySettings(Map settings) { + //if (settings == null || settings.isEmpty()) { + // return; + //} + defaultVersion = SettingsFile.getValue("defaultVersion", String.class, settings); + } + + public String getDefaultVersion() { + return defaultVersion; + } + +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/SettingsFile.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/SettingsFile.java new file mode 100644 index 000000000..21b376e4f --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/SettingsFile.java @@ -0,0 +1,223 @@ +// 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. + +package com.oracle.weblogic.imagetool.settings; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.EnumMap; +import java.util.Map; + +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; +import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.introspector.Property; +import org.yaml.snakeyaml.introspector.PropertyUtils; +import org.yaml.snakeyaml.nodes.NodeTuple; +import org.yaml.snakeyaml.nodes.Tag; +import org.yaml.snakeyaml.representer.Representer; + +public class SettingsFile { + private static final LoggingFacade logger = LoggingFactory.getLogger(SettingsFile.class); + + private Path filePath; + + public SettingsFile(Path pathToFile) { + filePath = pathToFile; + } + + /** + * Utility method to convert the InputStream with YAML into UserSettings. + * @param settings An InputStream with raw YAML text. + * @return The UserSettings containing the parsed values from the InputStream. + */ + private Map load(InputStream settings) { + // While yaml.loadAs() will do about the same thing, I opted to use Map because Map is more forgiving. + // Bad fields or extra data fields not in this version of ImageTool will cause yaml.loadAs to completely fail. + logger.entering(); + Yaml yaml = new Yaml(); + Map map = yaml.load(settings); + logger.exiting(map); + return map; + } + + /** + * Loads the settings from the YAML file and returns the loaded file as a Map. + */ + public Map load() { + logger.entering(filePath); + Map map = Collections.emptyMap(); + try (InputStream input = Files.newInputStream(filePath)) { + map = load(input); + } catch (IOException ioe) { + // If exception occurs, map will be empty. parseMap() must handle both empty and populated map. + } + logger.exiting(map); + return map; + } + + static class CustomRepresenter extends Representer { + private DumperOptions options; + + public CustomRepresenter(DumperOptions options) { + super(options); + this.options = options; + this.representers.put(EnumMap.class, data -> representMapping(Tag.MAP, (Map)data, + null)); + } + + @Override + protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, + Tag customTag) { + // if value of property is null, ignore it. + if (propertyValue == null) { + return null; + } else { + return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag); + } + } + + } + + private static Representer getYamlRepresenter() { + // Created this inline override to suppress the output of null for all unset user settings + //DumperOptions options = new DumperOptions(); + //options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + //CustomRepresenter representer = new CustomRepresenter(options); + Representer representer = new Representer() { + @Override + protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, + Tag customTag) { + // if value of property is null, ignore it. + this.representers.put(EnumMap.class, data -> representMapping(Tag.MAP, (Map)data, null)); + if (propertyValue == null) { + return null; + } else { + return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag); + } + } + }; + representer.addClassTag(UserSettingsFile.class, Tag.MAP); + representer.addClassTag(InstallerType.class, Tag.MAP); + representer.addClassTag(InstallerSettings.class, Tag.MAP); + representer.addClassTag(InstallerMetaData.class, Tag.MAP); + representer.addClassTag(PatchMetaData.class, Tag.MAP); + + PropertyUtils propertyUtils = new PropertyUtils(); + propertyUtils.setAllowReadOnlyProperties(true); + representer.setPropertyUtils(propertyUtils); + return representer; + } + + /** + * Save all settings to the specified file. + * @throws IOException if an error occurs saving to the filesystem + */ + public void save(Object data) throws IOException { + Path parent = filePath.getParent(); + if (parent != null && Files.notExists(parent)) { + Files.createDirectories(parent); + } + + try (OutputStreamWriter output = new OutputStreamWriter(Files.newOutputStream(filePath))) { + //DumperOptions options = new DumperOptions(); + //options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + Yaml yaml = new Yaml(getYamlRepresenter()); + yaml.dump(data, output); + } catch (IOException ioe) { + logger.severe("Failed saved user settings: {0}", ioe.getMessage(), ioe); + throw ioe; + } + } + + /** + * Get the value for the setting by name from the provided settings map. + * This method is intended to be used for leaf attributes (not folders). + * @param settingName The attribute name of the setting + * @param type The type of the attribute value (for cast) + * @param settings The map of settings from which the attribute is to be retrieved + * @return The value of the requested attribute. + */ + public static T getValue(String settingName, Class type, Map settings) { + if (settings == null) { + return null; + } + + Object value = settings.get(settingName); + if (value == null) { + return null; + } + + if (type.isInstance(value)) { + return type.cast(value); + } + + logger.severe("Setting for {0} could not be loaded. Expected {1}, but found {2}. Invalid value: {3}", + settingName, type, value.getClass(), value.toString()); + return null; + } + + /** + * Get the value for the setting by name from the provided settings map or default value. + * This method is intended to be used for leaf attributes (not folders). + * @param settingName The attribute name of the setting + * @param type The type of the attribute value (for cast) + * @param settings The map of settings from which the attribute is to be retrieved + * @param Default value + * @return The value of the requested attribute. + */ + public static T getValueOrDefault(String settingName, Class type, Map settings, + Object defaultValue) { + Object result = SettingsFile.getValue(settingName, type, settings); + if (result == null && defaultValue != null) { + result = defaultValue; + } + return type.cast(result); + } + + /** + * Get the folder for the settings by name from the provided settings map. + * For nested maps withing the settings map. + * + * @param folderName The key/name of the map/folder to be returned + * @param settings The settings from which the map/folder is to be retrieved. + * @return A map of settings matching the folder name provided, or empty map if not found. + */ + @SuppressWarnings("unchecked") + public static Map getFolder(String folderName, Map settings) { + if (settings == null) { + return Collections.emptyMap(); + } + + Object value = settings.get(folderName); + if (value == null) { + return Collections.emptyMap(); + } + + if (value instanceof Map) { + return (Map) value; + } + + //TODO needs to be an exception that will be caught and displayed as a parsing config error. + logger.severe("Setting for {0} could not be loaded. Invalid value: {3}", + folderName, value.toString()); + return Collections.emptyMap(); + } + + /** + * Convert settings object to a YAML string. + * @return formatted YAML text. + */ + public static String asYaml(Object value) { + Yaml yaml = new Yaml(getYamlRepresenter()); + return yaml.dump(value); + } +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/SettingsStore.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/SettingsStore.java new file mode 100644 index 000000000..045619a6a --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/SettingsStore.java @@ -0,0 +1,169 @@ +// 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. + +package com.oracle.weblogic.imagetool.settings; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Map; + +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.introspector.Property; +import org.yaml.snakeyaml.introspector.PropertyUtils; +import org.yaml.snakeyaml.nodes.NodeTuple; +import org.yaml.snakeyaml.nodes.Tag; +import org.yaml.snakeyaml.representer.Representer; + +public class SettingsStore { + private static final LoggingFacade logger = LoggingFactory.getLogger(SettingsStore.class); + + private Path filePath; + + public SettingsStore(Path pathToFile) { + filePath = pathToFile; + } + + /** + * Utility method to convert the InputStream with YAML into UserSettings. + * @param settings An InputStream with raw YAML text. + * @return The UserSettings containing the parsed values from the InputStream. + */ + private Map load(InputStream settings) { + // While yaml.loadAs() will do about the same thing, I opted to use Map because Map is more forgiving. + // Bad fields or extra data fields not in this version of ImageTool will cause yaml.loadAs to completely fail. + logger.entering(); + Yaml yaml = new Yaml(); + Map map = yaml.load(settings); + logger.exiting(map); + return map; + } + + /** + * Loads the settings from the YAML file and returns the loaded file as a Map. + */ + public Map load() { + logger.entering(filePath); + Map map = Collections.emptyMap(); + try (InputStream input = Files.newInputStream(filePath)) { + map = load(input); + } catch (IOException ioe) { + // If exception occurs, map will be empty. parseMap() must handle both empty and populated map. + } + logger.exiting(map); + return map; + } + + private static Representer getYamlRepresenter() { + // Created this inline override to suppress the output of null for all unset user settings + Representer representer = new Representer() { + @Override + protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, + Tag customTag) { + // if value of property is null, ignore it. + if (propertyValue == null) { + return null; + } else { + return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag); + } + } + }; + representer.addClassTag(UserSettingsFile.class, Tag.MAP); + representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + + PropertyUtils propertyUtils = new PropertyUtils(); + propertyUtils.setAllowReadOnlyProperties(true); + representer.setPropertyUtils(propertyUtils); + return representer; + } + + /** + * Save all settings to the specified file. + * @throws IOException if an error occurs saving to the filesystem + */ + public void save(Object data) throws IOException { + Path parent = filePath.getParent(); + if (parent != null && Files.notExists(parent)) { + Files.createDirectories(parent); + } + + try (OutputStreamWriter output = new OutputStreamWriter(Files.newOutputStream(filePath))) { + Yaml yaml = new Yaml(getYamlRepresenter()); + yaml.dump(data, output); + } catch (IOException ioe) { + logger.severe("Failed saved user settings: {0}", ioe.getMessage(), ioe); + throw ioe; + } + } + + /** + * Get the value for the setting by name from the provided settings map. + * This method is intended to be used for leaf attributes (not folders). + * @param settingName The attribute name of the setting + * @param type The type of the attribute value (for cast) + * @param settings The map of settings from which the attribute is to be retrieved + * @return The value of the requested attribute. + */ + public static T getValue(String settingName, Class type, Map settings) { + if (settings == null) { + return null; + } + + Object value = settings.get(settingName); + if (value == null) { + return null; + } + + if (type.isInstance(value)) { + return type.cast(value); + } + + logger.severe("Setting for {0} could not be loaded. Expected {1}, but found {2}. Invalid value: {3}", + settingName, type, value.getClass(), value.toString()); + return null; + } + + /** + * Get the folder for the settings by name from the provided settings map. + * For nested maps withing the settings map. + * + * @param folderName The key/name of the map/folder to be returned + * @param settings The settings from which the map/folder is to be retrieved. + * @return A map of settings matching the folder name provided, or empty map if not found. + */ + @SuppressWarnings("unchecked") + public static Map getFolder(String folderName, Map settings) { + if (settings == null) { + return Collections.emptyMap(); + } + + Object value = settings.get(folderName); + if (value == null) { + return Collections.emptyMap(); + } + + if (value instanceof Map) { + return (Map) value; + } + + //TODO needs to be an exception that will be caught and displayed as a parsing config error. + logger.severe("Setting for {0} could not be loaded. Invalid value: {3}", + folderName, value.toString()); + return Collections.emptyMap(); + } + + /** + * Convert settings object to a YAML string. + * @return formatted YAML text. + */ + public static String asYaml(Object value) { + Yaml yaml = new Yaml(getYamlRepresenter()); + return yaml.dump(value); + } +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/UserSettingsFile.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/UserSettingsFile.java new file mode 100644 index 000000000..fc9789dbf --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/UserSettingsFile.java @@ -0,0 +1,348 @@ +// 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 com.oracle.weblogic.imagetool.settings; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; + +public class UserSettingsFile { + private static final LoggingFacade logger = LoggingFactory.getLogger(UserSettingsFile.class); + /** + * Configured defaults associated with each installer. + */ + private final Map installerSettings; + + /** + * Parent directory for the build context directory. + * A temporary folder created under "Build Directory" with the prefix "wlsimgbuilder_tempXXXXXXX" will be created + * to hold the image build context (files, and Dockerfile). + */ + private String buildContextDirectory = null; + /** + * Patch download directory. + * The directory for storing and using downloaded patches. + */ + private String patchDirectory = null; + /** + * Installer download directory. + * The directory for storing and using downloaded Java and middleware installers. + */ + private String installerDirectory = null; + /** + * Container image build tool. + * Allow the user to specify the executable that will be used to build the container image. For example, + * "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker". + */ + private String buildEngine = null; + /** + * Container image runtime tool. + * Allow the user to specify the executable that will be used to run and/or interrogate images. For example, + * "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker". + */ + private String containerEngine = null; + /** + * REST calls to ARU should be retried up to this number of times. + */ + private Integer aruRetryMax = null; + /** + * The time between each ARU REST call in milliseconds. + */ + private Integer aruRetryInterval = null; + + private final SettingsFile settingsFile; + + private String defaultBuildPlatform = null; + + private String installerSettingsFile = null; + private String patchSettingsFile = null; + + /** + * DLoads the settings.yaml file from ~/.imagetool/settings.yaml and applies the values found. + */ + public UserSettingsFile() { + this(getSettingsFilePath()); + } + + /** + * Extract the Map of settings (i.e., from a YAML file), into this bean, UserSettings. + * Used for internal tests to override default settings file location. + * @param pathToSettingsFile A map of key-value pairs read in from the YAML user settings file. + */ + public UserSettingsFile(Path pathToSettingsFile) { + settingsFile = new SettingsFile(pathToSettingsFile); + installerSettingsFile = pathToSettingsFile.getParent().resolve("installers.yaml").toString(); + patchSettingsFile = pathToSettingsFile.getParent().resolve("patches.yaml").toString(); + installerSettings = new HashMap<>(); + applySettings(settingsFile.load()); + } + + /** + * Save all settings to the ~/.imagetool/settings.yaml. + * @throws IOException if an error occurs saving to the filesystem + */ + public void save() throws IOException { + settingsFile.save(this); + } + + /** + * The path to the directory where the settings file should be. + * @return The path to ~/.imagetool + */ + public static Path getSettingsDirectory() { + return Paths.get(System.getProperty("user.home"), ".imagetool"); + } + + public static Path getSettingsFilePath() { + return getSettingsDirectory().resolve("settings.yaml"); + } + + + /** + * Parent directory for the build context directory. + * A temporary folder created under "Build Directory" with the prefix "wlsimgbuilder_tempXXXXXXX" will be created + * to hold the image build context (files, and Dockerfile). + */ + public String getBuildContextDirectory() { + return buildContextDirectory; + } + + /** + * Parent directory for the build context directory. + * A temporary folder created under "Build Directory" with the prefix "wlsimgbuilder_tempXXXXXXX" will be created + * to hold the image build context (files, and Dockerfile). + */ + public void setBuildContextDirectory(String value) { + buildContextDirectory = value; + } + + /** + * Patch download directory. + * The directory for storing and using downloaded patches. + */ + public String getPatchDirectory() { + return patchDirectory; + } + + /** + * Patch download directory. + * The directory for storing and using downloaded patches. + */ + public void setPatchDirectory(String value) { + patchDirectory = value; + } + + /** + * Installer download directory. + * The directory for storing and using downloaded Java and middleware installers. + */ + public String getInstallerDirectory() { + return installerDirectory; + } + + /** + * Installer download directory. + * The directory for storing and using downloaded Java and middleware installers. + */ + public void setInstallerDirectory(String value) { + installerDirectory = value; + } + + /** + * Container image build tool. + * Allow the user to specify the executable that will be used to build the container image. For example, + * "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker". + */ + public String getBuildEngine() { + return buildEngine; + } + + /** + * Container image build tool. + * Allow the user to specify the executable that will be used to build the container image. For example, + * "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker". + */ + public void setBuildEngine(String value) { + buildEngine = value; + } + + /** + * Container image runtime tool. + * Allow the user to specify the executable that will be used to run and/or interrogate images. For example, + * "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker". + */ + public String getContainerEngine() { + return containerEngine; + } + + /** + * Container image runtime tool. + * Allow the user to specify the executable that will be used to run and/or interrogate images. For example, + * "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker". + */ + public void setContainerEngine(String value) { + containerEngine = value; + } + + /** + * REST calls to ARU should be retried up to this number of times. + */ + public Integer getAruRetryMax() { + return aruRetryMax; + } + + /** + * REST calls to ARU should be retried up to this number of times. + */ + public void setAruRetryMax(Integer value) { + aruRetryMax = value; + } + + /** + * The time between each ARU REST call in milliseconds. + */ + public Integer getAruRetryInterval() { + return aruRetryInterval; + } + + /** + * The time between each ARU REST call in milliseconds. + */ + public void setAruRetryInterval(Integer value) { + aruRetryInterval = value; + } + + /** + * Set the default WLS version. + * @param value version of WLS installer + */ + public void setDefaultWLSVersion(String value) { + setDefaultVersion(value, InstallerType.WLS); + } + + private void setDefaultVersion(String value, InstallerType type) { + Map installerSettingsMap = new HashMap<>(); + installerSettingsMap.put("defaultVersion", value); + InstallerSettings versionSettings = new InstallerSettings(installerSettingsMap); + installerSettings.put(type.toString().toUpperCase(), versionSettings); + } + + // Do not use getXXX Snake Yaml will add separate entry in the serialized yaml file + public String returnDefaultWLSVersion() { + return Optional.ofNullable(installerSettings.get(InstallerType.WLS.toString().toUpperCase())) + .map(InstallerSettings::getDefaultVersion).orElse(null); + } + + public void setDefaultWDTVersion(String value) { + setDefaultVersion(value, InstallerType.WDT); + } + + public String returnDefaultWDTVersion() { + return Optional.ofNullable(installerSettings.get(InstallerType.WDT.toString().toUpperCase())) + .map(InstallerSettings::getDefaultVersion).orElse(null); + } + + public void setDefaultJDKVersion(String value) { + setDefaultVersion(value, InstallerType.JDK); + } + + public String returnDefaultJDKVersion() { + return Optional.ofNullable(installerSettings.get(InstallerType.JDK.toString().toUpperCase())) + .map(InstallerSettings::getDefaultVersion).orElse(null); + } + + /** + * The user settings for installer type. + * @param installerType Installer type such as JDK, WLS, SOA, etc. + * @return the settings for the requested installer type + */ + public InstallerSettings getInstallerSettings(InstallerType installerType) { + if (installerSettings == null) { + return null; + } + return installerSettings.get(installerType.toString().toUpperCase()); + } + + public Map getInstallerSettings() { + return installerSettings; + } + + private void applySettings(Map settings) { + logger.entering(); + if (settings == null || settings.isEmpty()) { + logger.exiting(); + return; + } + + patchDirectory = SettingsFile.getValue("patchDirectory", String.class, settings); + installerDirectory = SettingsFile.getValue("installerDirectory", String.class, settings); + buildContextDirectory = SettingsFile.getValue("buildContextDirectory", String.class, settings); + buildEngine = SettingsFile.getValue("buildEngine", String.class, settings); + containerEngine = SettingsFile.getValue("containerEngine", String.class, settings); + defaultBuildPlatform = SettingsFile.getValue("defaultBuildPlatform", String.class, settings); + + aruRetryMax = SettingsFile.getValueOrDefault("aruRetryMax", Integer.class, settings, + new Integer(10)); + aruRetryInterval = SettingsFile.getValueOrDefault("aruRetryInterval", Integer.class, settings, + new Integer(10)); + // Just the settings about the installer not the individual installers + Map installerFolder = SettingsFile.getFolder("installerSettings", settings); + for (Map.Entry entry: installerFolder.entrySet()) { + String key = entry.getKey(); + if (key != null && !key.isEmpty()) { + key = key.toUpperCase(); + try { + installerSettings.put( + key, + new InstallerSettings((Map) entry.getValue())); + } catch (IllegalArgumentException illegal) { + logger.warning("settings for {0} could not be loaded. {0} is not a valid installer type: {1}", + key, InstallerType.class.getEnumConstants()); + } + } + } + + logger.exiting(); + } + + public String getDefaultBuildPlatform() { + return defaultBuildPlatform; + } + + public void setDefaultBuildPlatform(String value) { + defaultBuildPlatform = value; + } + + public String returnInstallerSettingsFile() { + return installerSettingsFile; + } + + public String returnPatchSettingsFile() { + return patchSettingsFile; + } + + @Override + public String toString() { + return "UserSettingsFile{" + + "installerSettings=" + installerSettings + + ", buildContextDirectory='" + buildContextDirectory + '\'' + + ", patchDirectory='" + patchDirectory + '\'' + + ", installerDirectory='" + installerDirectory + '\'' + + ", buildEngine='" + buildEngine + '\'' + + ", containerEngine='" + containerEngine + '\'' + + ", aruRetryMax=" + aruRetryMax + + ", aruRetryInterval=" + aruRetryInterval + + ", settingsFile=" + settingsFile + + ", installerSettings='" + installerSettings + '\'' + + '}'; + } + +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/YamlFileConstants.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/YamlFileConstants.java new file mode 100644 index 000000000..2b56d9cc7 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/settings/YamlFileConstants.java @@ -0,0 +1,17 @@ +// 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 com.oracle.weblogic.imagetool.settings; + +public class YamlFileConstants { + + // Do not change the values, it affect the persisted YAML file and will break compatibility + public static final String ARCHITECTURE = "architecture"; + public static final String DATE_ADDED = "dateAdded"; + public static final String DESCRIPTION = "description"; + public static final String DIGEST = "digest"; + public static final String LOCATION = "location"; + public static final String PATCH_VERSION = "patchVersion"; + public static final String PRODUCT_VERSION = "productVersion"; + public static final String BASE_FMW_VERSION = "baseFMWVersion"; +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Architecture.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Architecture.java index f0253c539..a8928301d 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Architecture.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Architecture.java @@ -15,7 +15,8 @@ */ public enum Architecture { ARM64(541, "arm64", "linux/arm64", "aarch64"), // Linux ARM 64-bit - AMD64(226, "amd64", "linux/amd64", "x86_64"); // Linux AMD 64-bit + AMD64(226, "amd64", "linux/amd64", "x86_64"), // Linux AMD 64-bit + GENERIC(2000, "Generic"); private static final LoggingFacade logger = LoggingFactory.getLogger(Architecture.class); @@ -64,8 +65,9 @@ public static Architecture fromString(String value) { } } } - logger.warning("IMG-0121", value); - return AMD64; + + logger.warning("IMG-0121", value, Architecture.getLocalArchitecture()); + return Architecture.getLocalArchitecture(); } /** diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/CacheConversion.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/CacheConversion.java new file mode 100644 index 000000000..fe04772a4 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/CacheConversion.java @@ -0,0 +1,105 @@ +// 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 com.oracle.weblogic.imagetool.util; + +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.List; + +import com.oracle.weblogic.imagetool.api.model.CommandResponse; +import com.oracle.weblogic.imagetool.cachestore.CacheStore; +import com.oracle.weblogic.imagetool.cli.cache.CacheOperation; +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import picocli.CommandLine; + +/** + * Utility to convert image tool 1.x cache store to 2.0 format + */ +@CommandLine.Command( + name = "convert", + description = "Convert cache settings from v1 to v2", + sortOptions = false +) +public class CacheConversion extends CacheOperation { + + private static final LoggingFacade logger = LoggingFactory.getLogger(CacheConversion.class); + + private boolean initializeSettingFiles(String directory) { + boolean success = true; + logger.entering("Entering initializeSettingFiles " + directory); + try { + + if (directory != null) { + Path directoryPath = Paths.get(directory); + if (!Files.exists(directoryPath)) { + createIfNotExists(directoryPath, true); + } + + Path settingsPath = directoryPath.resolve("settings.yaml"); + if (!Files.exists(settingsPath)) { + createNewSettingsYaml(settingsPath); + } + } + } catch (Exception ex) { + logger.warning("IMG-0138", ex.getLocalizedMessage()); + return false; + } + logger.exiting("initializeSettingFiles"); + return success; + } + + private void createNewSettingsYaml(Path settingsPath) throws IOException { + logger.fine("No existing settings file creating it"); + createIfNotExists(settingsPath, false); + Path parentPath = settingsPath.getParent(); + List lines = new ArrayList<>(); + lines.add("installerDirectory: " + parentPath.resolve("installers").toString()); + lines.add("patchDirectory: " + parentPath.resolve("patches").toString()); + createIfNotExists(parentPath.resolve("installers"), true); + createIfNotExists(parentPath.resolve("patches"), true); + createIfNotExists(parentPath.resolve("installers.yaml"), false); + createIfNotExists(parentPath.resolve("patches.yaml"), false); + + Files.write(settingsPath, lines); + } + + private void createIfNotExists(Path entry, boolean isDir) throws IOException { + if (Files.exists(entry)) { + return; + } + if (isDir) { + Files.createDirectory(entry); + } else { + Files.createFile(entry); + } + } + + @Override + public CommandResponse call() throws Exception { + String cacheEnv = System.getenv(CacheStore.CACHE_DIR_ENV); + Path cacheMetaFile; + if (cacheEnv != null) { + cacheMetaFile = Paths.get(cacheEnv, ".metadata"); + if (!initializeSettingFiles(cacheMetaFile.getParent().toString())) { + return CommandResponse.error("IMG-0139"); + } + } else { + cacheMetaFile = Paths.get(System.getProperty("user.home"), "cache", ".metadata"); + if (!initializeSettingFiles(Paths.get(System.getProperty("user.home"), ".imagetool").toString())) { + return CommandResponse.error("IMG-0139"); + } + } + if (Files.exists(cacheMetaFile)) { + CacheConverterUtil.convert(cacheMetaFile.toString()); + } else { + logger.info("IMG-0133", cacheMetaFile.toString()); + } + return CommandResponse.success("IMG-0134"); + } + +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/CacheConverterUtil.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/CacheConverterUtil.java new file mode 100644 index 000000000..cc67b8138 --- /dev/null +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/CacheConverterUtil.java @@ -0,0 +1,250 @@ +// 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 com.oracle.weblogic.imagetool.util; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileTime; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; +import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.settings.ConfigManager; + + +public class CacheConverterUtil { + + private static final LoggingFacade logger = LoggingFactory.getLogger(CacheConverterUtil.class); + public static final String INSTALLER_PATTERN = "^(wls|fmw|ohs|wlsdev|wlsslim|soa|osb|b2b|mft|idm|db19|" + + "oud|oid|wcc|wcp|wcs|jdk|wls|odi|wdt)$"; + public static final String IMG_0128 = "IMG-0128"; + public static final String IMG_0129 = "IMG-0129"; + + /** + * convert cache file to new format. + * @param inputFile input cache file + * @throws IOException when error + */ + public static void convert(String inputFile) throws IOException { + Pattern installerPattern = Pattern.compile(INSTALLER_PATTERN); + try (BufferedReader br = new BufferedReader(new FileReader(inputFile))) { + String line; + while ((line = br.readLine()) != null) { + if (line.charAt(0) == '#') { + continue; + } + logger.info("IMG-0137", line); + // patches + if (Character.isDigit(line.charAt(0))) { + handlePatchPattern(line); + } else { + // installer + handleInstallerPattern(installerPattern, line); + } + } + } + } + + public static class ParsedInfo { + private final String filePath; + private final String key; + private final String version; + private final String architecture; + private final String fileDate; + + /** + * Constructor. + * @param filePath File Path + * @param key Installer type + * @param version Version + * @param architecture Architecture + * @param fileDate File date + */ + public ParsedInfo(String filePath, String key, String version, String architecture, String fileDate) { + this.filePath = filePath; + this.key = key; + this.version = version; + this.architecture = architecture; + this.fileDate = fileDate; + } + + public String getFilePath() { + return filePath; + } + + public String getKey() { + return key; + } + + public String getVersion() { + return version; + } + + public String getArchitecture() { + return architecture; + } + + public String getFileDate() { + return fileDate; + } + + } + + /** + * Convert installer line v1 to v2. + * @param installerPattern Installer pattern + * @param line input line + * @return ParsedInfo for temporary holder + */ + public static ParsedInfo convertInstallerEntry(Pattern installerPattern, String line) { + String[] tokens = line.split("="); + if (tokens.length == 2) { + String filepath = tokens[1]; + String key = null; + String version = null; + String arch = null; + tokens = tokens[0].split("_"); + + if (tokens.length == 2) { + key = tokens[0]; + version = tokens[1]; + Matcher matcher = installerPattern.matcher(key); + if (!matcher.matches()) { + logger.warning(IMG_0129, key, line); + return null; + } + } else if (tokens.length == 3) { + key = tokens[0]; + version = convertVersionString(tokens[1]); + arch = tokens[2]; + arch = Architecture.fromString(arch).toString(); + } else { + logger.warning(IMG_0128, line); + return null; + } + String fileDate = getFileDate(filepath); + if (arch == null) { + arch = Utils.standardPlatform(Architecture.getLocalArchitecture().toString()); + } + if (fileDate != null) { + return new ParsedInfo(filepath, key, version, arch, fileDate); + } + } else { + logger.warning(IMG_0128, line); + return null; + } + return null; + } + + + private static void handleInstallerPattern(Pattern installerPattern, String line) throws IOException { + + ParsedInfo info = convertInstallerEntry(installerPattern, line); + if (info != null) { + logger.info("IMG-0147", info.getKey(), info.getVersion(), info.getFilePath(), info.getArchitecture()); + InstallerMetaData metaData = new InstallerMetaData(info.getArchitecture(), info.getFilePath(), + Utils.getSha256Hash(info.getFilePath()), info.getFileDate(), info.getVersion(), info.getVersion()); + ConfigManager.getInstance().addInstaller(InstallerType.fromString(info.getKey()), info.getVersion(), + metaData); + } + } + + /** + * Convert patch line v1 to v2. + * @param line input line + * @return ParsedInfo for temporary holder + */ + public static ParsedInfo convertPatchEntry(String line) { + String[] tokens = line.split("="); + if (tokens.length == 2) { + String filepath = tokens[1]; + String key = null; + String version = null; + String arch = null; + tokens = tokens[0].split("_"); + if (tokens.length == 2) { + key = tokens[0]; + version = tokens[1]; + } else if (tokens.length == 3) { + key = tokens[0]; + version = convertVersionString(tokens[1]); + arch = tokens[2]; + arch = Architecture.fromString(arch).toString(); + } else { + logger.warning(IMG_0128, line); + return null; + } + String fileDate = getFileDate(filepath); + if (arch == null) { + arch = Utils.standardPlatform(Architecture.getLocalArchitecture().toString()); + } + if (fileDate != null) { + return new ParsedInfo(filepath, key, version, arch, fileDate); + } + } else { + logger.warning(IMG_0128, line); + } + return null; + } + + private static void handlePatchPattern(String line) throws IOException { + ParsedInfo info = convertPatchEntry(line); + if (info != null) { + logger.info("IMG-0148", info.getKey(), info.getVersion(), info.getFilePath()); + ConfigManager.getInstance().addPatch(info.getKey(), info.getArchitecture(), + info.getFilePath(), info.getVersion(), + "Converted from v1", info.getFileDate()); + } + } + + /** + * Convert a version string to standard format. + * @param version input string + * @return converted version format string + */ + public static String convertVersionString(String version) { + if (version != null && version.length() == 6) { + // e.g. 122140 141100 + return String.format("%s.%s.%s.%s.%s", + version.substring(0,2), + version.substring(2,3), + version.substring(3,4), + version.substring(4,5), + version.substring(5)); + } else { + return version; + } + } + + private static String getFileDate(String filepath) { + try { + Path path = Paths.get(filepath); + if (Files.exists(path)) { + FileTime modifiedTime = Files.getLastModifiedTime(path); + LocalDate today = modifiedTime.toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDate(); + return today.format(DateTimeFormatter.ISO_LOCAL_DATE); + } else { + logger.warning("IMG-0131", filepath); + return null; + } + } catch (IOException ioe) { + logger.warning("IMG-0132", filepath, ioe.getLocalizedMessage()); + return null; + } + } + + +} diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Constants.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Constants.java index dd0245f6c..d08596ef7 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Constants.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Constants.java @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2024, Oracle and/or its affiliates. +// Copyright (c) 2019, 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 com.oracle.weblogic.imagetool.util; @@ -17,6 +17,7 @@ public final class Constants { + "/search?patch_type=all&life_cycle=Recommended&product=%s&release=%s"; public static final String ARU_LANG_URL = ARU_REST_URL + "/metadata?table=aru_languages"; public static final String CONFLICTCHECKER_URL = ARU_REST_URL + "/conflict_checks"; + public static final String CACHE_DIR_KEY = "cache.dir"; public static final String DEFAULT_WLS_VERSION = "12.2.1.3.0"; public static final String DEFAULT_JDK_VERSION = "8u202"; public static final String DEFAULT_META_FILE = ".metadata"; @@ -27,6 +28,10 @@ public final class Constants { public static final List BUSYBOX_OS_IDS = Collections.unmodifiableList(Arrays.asList("bb", "alpine")); public static final String ORACLE_LINUX = "ghcr.io/oracle/oraclelinux:8-slim"; public static final String BUILDER_DEFAULT = Utils.getEnvironmentProperty("WLSIMG_BUILDER", () -> "docker"); + public static final String CTX_JDK = "jdk/"; + public static final String CTX_FMW = "fmw/"; + public static final String AMD64_BLD = "linux/amd64"; + public static final String ARM64_BLD = "linux/arm64"; private Constants() { //restrict access diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/DockerfileOptions.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/DockerfileOptions.java index 30b8c2a39..36fa03175 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/DockerfileOptions.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/DockerfileOptions.java @@ -24,6 +24,10 @@ import com.oracle.weblogic.imagetool.logging.LoggingFactory; import com.oracle.weblogic.imagetool.wdt.WdtOperation; +import static com.oracle.weblogic.imagetool.util.Constants.AMD64_BLD; +import static com.oracle.weblogic.imagetool.util.Constants.ARM64_BLD; + + /** * Provides the data used by the Dockerfile templates (in mustache). */ @@ -39,6 +43,7 @@ public class DockerfileOptions { private static final List DEFAULT_OS_PACKAGES = Arrays.asList( "gzip", "tar", "unzip", "libaio", "libnsl", "jq", "findutils", "diffutils"); + private static final List BINARY_OS_PACKAGES = Arrays.asList("binutils", "make", "glibc-devel"); private static final String WLSIMG_OS_PACKAGES = System.getenv("WLSIMG_OS_PACKAGES"); private static final String DEFAULT_ORAINV_DIR = "/u01/oracle/oraInventory"; @@ -54,7 +59,7 @@ public class DockerfileOptions { private boolean isRebaseToNew; private boolean strictPatchOrdering; - private String javaInstaller; + private List javaInstaller; private String username; private String groupname; private String javaHome; @@ -72,6 +77,7 @@ public class DockerfileOptions { private MiddlewareInstall mwInstallers; private boolean useOwnerPermsForGroup; private boolean usingBusybox; + private boolean includeBinaryOsPackages; private List buildArgs; // WDT values @@ -104,6 +110,7 @@ public DockerfileOptions(String buildId) { skipMiddlewareInstall = false; useOwnerPermsForGroup = false; usingBusybox = false; + includeBinaryOsPackages = false; buildArgs = new ArrayList<>(); javaHome = DEFAULT_JAVA_HOME; @@ -1060,12 +1067,44 @@ public List installPackages() { return mwInstallers.getInstallers(); } - public void setJavaInstaller(String value) { + /** + * List of packaged to be installed for ARM. + * @return list of packages + */ + public List installPackagesForARM() { + List result = mwInstallers.getInstallers() + .stream().filter(obj -> ARM64_BLD.equalsIgnoreCase(obj.platform)) + .collect(Collectors.toList()); + if (result.isEmpty()) { + result = mwInstallers.getInstallers() + .stream().filter(obj -> "Generic".equalsIgnoreCase(obj.platform)) + .collect(Collectors.toList()); + } + return result; + } + + /** + * List of packaged to be installed for AMD. + * @return list of packages + */ + public List installPackagesForAMD() { + List result = mwInstallers.getInstallers() + .stream().filter(obj -> AMD64_BLD.equalsIgnoreCase(obj.platform)) + .collect(Collectors.toList()); + if (result.isEmpty()) { + result = mwInstallers.getInstallers() + .stream().filter(obj -> "Generic".equalsIgnoreCase(obj.platform)) + .collect(Collectors.toList()); + } + return result; + } + + public void setJavaInstaller(List value) { javaInstaller = value; } @SuppressWarnings("unused") - public String java_pkg() { + public List java_pkg() { return javaInstaller; } @@ -1109,6 +1148,47 @@ public boolean useOwnerPermsForGroup() { return useOwnerPermsForGroup; } + public boolean targetARMPlatform; + + public DockerfileOptions setTargetARMPlatform(boolean value) { + this.targetARMPlatform = value; + return this; + } + + public boolean targetARMPlatform() { + return targetARMPlatform; + } + + public boolean targetAMDPlatform; + + public DockerfileOptions setTargetAMDPlatform(boolean value) { + this.targetAMDPlatform = value; + return this; + } + + public boolean targetAMDPlatform() { + return targetAMDPlatform; + } + + /** + * Include OS packages for binary patching such as make for OPatch. + * @param value true if additional OS patches for binary patching should be added to the image. + * @return this + */ + public DockerfileOptions includeBinaryOsPackages(boolean value) { + includeBinaryOsPackages = value; + return this; + } + + /** + * Returns true if additional OS patches for binary patching should be added to the image. + * @return true if additional OS patches for binary patching should be added to the image, false otherwise. + */ + public boolean includeBinaryOsPackages() { + return includeBinaryOsPackages; + } + + /** * Returns true if BusyBox options should be used in the Dockerfile. * @@ -1160,6 +1240,9 @@ public List osPackages() { if (Utils.isEmptyString(WLSIMG_OS_PACKAGES)) { // If the user did not provide a list of OS packages, use the default list result.addAll(DEFAULT_OS_PACKAGES); + if (includeBinaryOsPackages()) { + result.addAll(BINARY_OS_PACKAGES); + } } else { // When provided in the environment variable, use the list of OS packages provided by the user. result.addAll(Stream.of(WLSIMG_OS_PACKAGES.split(" ")).collect(Collectors.toList())); diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/HttpUtil.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/HttpUtil.java index 97b7ea6d6..bf3c085d5 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/HttpUtil.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/HttpUtil.java @@ -16,6 +16,7 @@ import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpHost; @@ -179,7 +180,7 @@ public static Executor getHttpExecutor(String supportUserName, String supportPas private static HttpRequestRetryHandler retryHandler() { return (exception, executionCount, context) -> { - if (executionCount > 10) { + if (executionCount > ConfigManager.getInstance().getAruRetryMax()) { // Do not retry if over max retries return false; } @@ -195,7 +196,7 @@ private static HttpRequestRetryHandler retryHandler() { boolean retriable = !(request instanceof HttpEntityEnclosingRequest); if (retriable) { try { - long waitTime = executionCount < 5 ? 2 : 10; + long waitTime = executionCount < 5 ? 2 : ConfigManager.getInstance().getAruRetryInterval(); logger.warning("Connect failed, retrying in {0} seconds, attempts={1} ", waitTime, executionCount); Thread.sleep(waitTime * 1000); } catch (InterruptedException e) { diff --git a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Utils.java b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Utils.java index 0c752f5d7..24b7d59a0 100644 --- a/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Utils.java +++ b/imagetool/src/main/java/com/oracle/weblogic/imagetool/util/Utils.java @@ -5,6 +5,7 @@ import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; @@ -18,7 +19,11 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.text.MessageFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.Base64; import java.util.Collection; @@ -40,6 +45,7 @@ import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; +import com.oracle.weblogic.imagetool.installer.InstallerType; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; import org.jetbrains.annotations.NonNls; @@ -778,4 +784,101 @@ public static Predicate not(Predicate target) { Objects.requireNonNull(target); return (Predicate)target.negate(); } + + /** + * Return the sha256 hash string of the file. + * @param filename file to check for sha256 hash + * @return sha256 hash value + */ + public static String getSha256Hash(String filename) { + try { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException ex) { + throw new IOException(ex); + } + try (FileInputStream fis = new FileInputStream(filename)) { + byte[] buffer = new byte[1024]; + int read; + while ((read = fis.read(buffer)) != -1) { + digest.update(buffer, 0, read); + } + } + byte[] hash = digest.digest(); + StringBuilder sb = new StringBuilder(); + for (byte b : hash) { + sb.append(String.format("%02x", b)); + } + return sb.toString().toUpperCase(); + + } catch (Exception ie) { + return ""; + } + } + + /** + * Get today's date in yyyy-mm-dd format. + * @return date format + */ + public static String getTodayDate() { + LocalDate today = LocalDate.now(); + return today.format(DateTimeFormatter.ISO_LOCAL_DATE); + } + + /** + * Get epoch date in yyyy-mm-dd HH:MM:SS format. + * @return date format + */ + public static String getReleaseDate(String epoch) { + if (epoch == null) { + return getTodayDate(); + } else { + String [] dates = epoch.split(" "); + if (dates.length == 2) { + return dates[0]; + } else { + return getTodayDate(); + } + } + } + + /** + * Return standard platform name from the possible names. + * @param platform input value to convert + * @return standardized platform name + */ + public static String standardPlatform(String platform) { + if (Architecture.AMD64.getAcceptableNames().contains(platform)) { + return "linux/amd64"; + } + if (Architecture.ARM64.getAcceptableNames().contains(platform)) { + return "linux/arm64"; + } + return "Generic"; + } + + /** + * Return true if it is ok to return the generic installer if the specific architecture is not available. + * @param type installer type + * @return true if it is ok return the generic installer if the specific architecture is not available + */ + public static boolean isGenericInstallerAcceptable(InstallerType type) { + List types = Arrays.asList(InstallerType.WDT); + return types.contains(type); + } + + /** + * Return true if the type is a base installer JDK, WDT, WLS, or FMW. + * @param type installer type + * @return true if the type is a base installer JDK, WDT, WLS, or FMW + */ + public static boolean isBaseInstallerType(InstallerType type) { + return type.equals(InstallerType.WDT) + || type.equals(InstallerType.WLS) + || type.equals(InstallerType.JDK) + || type.equals(InstallerType.DB19) + || type.equals(InstallerType.OHS) + || type.equals(InstallerType.FMW); + } } diff --git a/imagetool/src/main/resources/ImageTool.properties b/imagetool/src/main/resources/ImageTool.properties index 4d9fefda2..6d0cd1864 100644 --- a/imagetool/src/main/resources/ImageTool.properties +++ b/imagetool/src/main/resources/ImageTool.properties @@ -9,7 +9,7 @@ IMG-0007={0} is not a directory IMG-0008=Updating OPatch in final image from version {0} to version {1} IMG-0009=No credentials provided, skipping patch conflict check. IMG-0010=Oracle Home will be set to [[cyan: {0}]] -IMG-0011=Installer with key="{0}" is not in the local cache, please download the installer and add it to the cache with "imagetool cache addInstaller ..." +IMG-0011=Installer with type="{0}", version={1}, architecture={2} filepath={3}, is not in the local cache, please download the installer and add it to the cache with "imagetool cache addInstaller ..." IMG-0012=Checking for conflicting patches IMG-0013=Error parsing additional build commands file {0} IMG-0014=Additional build command section, {0}, was not one of the valid sections: {1} @@ -32,7 +32,7 @@ IMG-0030=Additional build command file does not exist: {0} IMG-0031=No credentials provided. Cannot determine latestPSU or recommendedPatches. Please provide the --user and one of the --password options. IMG-0032=Failed to find latest PSU for {0}, version {1} IMG-0033=No credentials provided, skipping validation of patches -IMG-0034=Unable to determine the correct version for patch ID {0}, please retry the command using one of the available patch versions: {1} +IMG-0034=Unable to find the correct version for patch ID {0}, please retry the command using one of the available patch versions: {1} IMG-0035=additionalBuildFile could not be copied: {0} IMG-0036=Unable to find installer inventory file: {0} IMG-0037=Failed to find patch {0} for version {1} @@ -48,7 +48,7 @@ IMG-0046=Deleted all entries from cache IMG-0047=cache is empty IMG-0048=Entry {0} already exists as {1}. Use --force to override the entry, or remove the existing entry first: imagetool cache deleteEntry --key={0} IMG-0049=File not found, invalid value for --path {0} -IMG-0050=Successfully added to cache. [[cyan: {0}]]={1} +IMG-0050=Successfully added installer to cache. [[cyan: type:]] {0} [[cyan: version:]] {1} [[cyan: filepath:]] {2} IMG-0051=Deleted entry [[cyan: {0}]]={1} IMG-0052=Nothing to delete for key: {0} IMG-0053=[[brightgreen: Build successful.]] Build time={0}s. Image tag={1} @@ -60,7 +60,7 @@ IMG-0058=A patch cannot have an empty/null bug number: {0} IMG-0059=Invalid response from Oracle ARU, unable to locate download URL for {0} IMG-0060=Adding patch [[cyan: {0}]] to cache, path={1} IMG-0061=Could not find key {0} in the cache for patch {1} -IMG-0062=An OPatch version could not be found in the cache store and you have not provided Oracle Support credentials. Please provide --user with one of the password options or populate the cache store manually. +IMG-0062=An OPatch version {0} could not be found in the cache store and you have not provided Oracle Support credentials. Please provide --user with one of the password options or populate the cache store manually. IMG-0063=Requesting patch information for patch {0} IMG-0064=Failed to copy file, {0}, from cache to build context directory, {1} IMG-0065=Skipping OPatch version update, --skipOPatchUpdate @@ -119,5 +119,51 @@ IMG-0117=The value for --sourceImage must not be empty. IMG-0118=Failed to clean up intermediate container images for build ID {0} IMG-0119=All parameters and options provided after the -- will be passed to the container image build command. IMG-0120=Retries exhausted, unable to download patch from Oracle. Try again later or manually add the patch to the cache to skip this download step. -IMG-0121=Did not recognize architecture name {0}. Defaulted to AMD64. +IMG-0121=Did not recognize '-platform' name {0}. Defaulted to {1}. IMG-0122=Invalid patch {0} for version {1}. A patch cannot be both generic and architecture specific. Remove the invalid entry from the cache, like {0}_{1}_xxx64. +IMG-0123=Cannot find the patch {0} for platform {1}, no patch for this platform will be applied. +IMG-0124=You must specify --type (--version or --commonName) and --architecture for deleting an installer. +IMG-0125=Specified installer for deletion cannot be found. +IMG-0126=You must specify --patchId --version and --architecture for deleting a patch. +IMG-0127=Specified patch for deletion cannot be found. +IMG-0128=Cannot parse image tool version 1 metadata file line: {0}, skipping. +IMG-0129=Cannot parse image tool version 1 metadata file line: unrecognized product {0} in line {1}. +IMG-0130=Successfully added patch to cache. [[cyan: bug:]] {0} [[cyan: version:]] {1} [[cyan: filepath:]] {2} +IMG-0131=Image tool v1 cache entry specified file {0} no longer exists, skipping conversion. You can add it manually. +IMG-0132=Image tool v1 cache entry specified file {0} cannot be accessed {1}, skipping conversion. You can add it manually. +IMG-0133=Image tool cache conversion cannot find .metadata file in {0}. +IMG-0134=Image tool successfully convert v1 cache data to v2. +IMG-0135=Installer [{0}] already exists in cache. +IMG-0136=Patch [{0}] already exists in cache. +IMG-0137=Image tool v1 cache converting line: {0}.GG +IMG-0138=Image tool conversion failed to initialize files {0}. +IMG-0139=Image tool conversion failed. +IMG-0140=Creating multiplatform image manifest {0}. +IMG-0141=Creating command: adding image {0} to manifest {1}. +IMG-0142=Creating command: push image manifest {0} to repo. +IMG-0143=Creating command: push image to repo {0}. +IMG-0144=Setting up manifest and image: {0}. +IMG-0145=Cannot find installer [type: {0}, platform: {1}, version: {2}]. +IMG-0146=When building a cross-platform image using podman, it may hang during middleware install. If it hangs, use \ + a machine matching with the target platform. +IMG-0147=Converting installer {0} version {1} path {2} architecture {3}. +IMG-0148=Converting patch {0} version {1} path {2} architecture {3}. +IMG-0149=Base FMW installer version {0} does not exists in the cache for adding installer {1} version {2}. +IMG-0150=Verifying installer type {0}, version {1}, architecture {2} exists. +IMG-0151=Locating installer type {0}, version {1}, architecture {2}. +IMG-0152=Cannot find installer type {0}, version {1}, architecture {2} in the cache, trying Generic architecture. +IMG-0153=Preparing Middleware Installer {0}, version {1}, architecture {2}. +IMG-0154=No installer found with commonName: {0}. +IMG-0155=No installer found for version: {0}. +IMG-0156=--type cannot be null when commonName is specified. +IMG-0157=--type cannot be null when version is specified. +IMG-0158=--patchId cannot be null when version is specified. +IMG-0160=patchId not found {0}. +IMG-0161=Attribute {0} value {1} must be an integer greater than 1. +IMG-0162=defaultBuildPlatform must be either "linux/amd64" or "linux/arm64". +IMG-0163={0} must be a valid directory, {1} is not a directory. +IMG-0164=You can only add one version of installers with --commonName. There is already an existing version {0}. +IMG-0165=Using version {0} based on --commonName {1} instead. +IMG-0166=No installers found for commonName {0}. +IMG-0168=Patch {0} version {1} architecture {2} deleted. +IMG-0169=Installer {0} version {1} architecture {2} deleted. diff --git a/imagetool/src/main/resources/docker-files/Create_Image.mustache b/imagetool/src/main/resources/docker-files/Create_Image.mustache index 3b2463032..927abd00b 100644 --- a/imagetool/src/main/resources/docker-files/Create_Image.mustache +++ b/imagetool/src/main/resources/docker-files/Create_Image.mustache @@ -2,7 +2,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # -FROM {{baseImage}} as os_update +FROM {{baseImage}} AS os_update {{#buildArgs}}ARG {{{.}}} {{/buildArgs}} LABEL com.oracle.weblogic.imagetool.buildid="{{buildId}}" @@ -30,7 +30,7 @@ USER root {{> run-wdt }} {{/isWdtEnabled}} -FROM os_update as final_build +FROM os_update AS final_build {{#buildArgs}}ARG {{{.}}} {{/buildArgs}} ENV ORACLE_HOME={{{oracle_home}}} \ diff --git a/imagetool/src/main/resources/docker-files/Rebase_Image.mustache b/imagetool/src/main/resources/docker-files/Rebase_Image.mustache deleted file mode 100644 index ba5820ea3..000000000 --- a/imagetool/src/main/resources/docker-files/Rebase_Image.mustache +++ /dev/null @@ -1,91 +0,0 @@ -# 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. -# - -{{#isRebaseToTarget}} -FROM {{sourceImage}} as source_image -FROM {{targetImage}} as final_build -{{#buildArgs}}ARG {{{.}}} -{{/buildArgs}} - -ENV DOMAIN_HOME={{{domain_home}}} -LABEL com.oracle.weblogic.imagetool.buildid="{{buildId}}" - -{{/isRebaseToTarget}} -{{#isRebaseToNew}} - FROM {{sourceImage}} as source_image - FROM {{baseImage}} as os_update - - ENV DOMAIN_HOME={{{domain_home}}} - - LABEL com.oracle.weblogic.imagetool.buildid="{{buildId}}" - USER root - {{#initialBuildCommands}} - {{{.}}} - {{/initialBuildCommands}} - # Use package manager to make sure that unzip, tar, and other required packages are installed - {{> package-managers}} - - # Create the Oracle user that will be the owner of the installed software - {{> create-user-group}} - - # If Java is not already in the base image, install it - {{#installJava}} - {{> install-java }} - {{/installJava}} - - # If an Oracle Home is not already in the base image, install the middleware components - {{#installMiddleware}} - {{> install-middleware }} - {{/installMiddleware}} - - FROM os_update as final_build - {{#buildArgs}}ARG {{{.}}} - {{/buildArgs}} - ENV ORACLE_HOME={{{oracle_home}}} \ - LD_LIBRARY_PATH={{{oracle_home}}}/oracle_common/adr:$LD_LIBRARY_PATH \ - {{#installJava}} - JAVA_HOME={{{java_home}}} \ - {{/installJava}} - PATH=${PATH}:{{{java_home}}}/bin:{{{oracle_home}}}/oracle_common/common/bin:{{{oracle_home}}}/wlserver/common/bin:{{{oracle_home}}} - - LABEL com.oracle.weblogic.imagetool.buildid="{{buildId}}" - - {{#installJava}} - COPY --from=jdk_build --chown={{userid}}:{{groupid}} {{{java_home}}} {{{java_home}}}/ - {{/installJava}} - - {{#installMiddleware}} - COPY --from=wls_build --chown={{userid}}:{{groupid}} {{{oracle_home}}} {{{oracle_home}}}/ - {{/installMiddleware}} - - {{#copyOraInst}} - COPY --from=wls_build --chown={{userid}}:{{groupid}} {{inv_loc}}/oraInst.loc {{inv_loc}}/oraInst.loc - {{/copyOraInst}} - {{#copyOraInventoryDir}} - COPY --from=wls_build --chown={{userid}}:{{groupid}} {{orainv_dir}} {{orainv_dir}}/ - {{/copyOraInventoryDir}} -{{/isRebaseToNew}} - -USER {{userid}} -RUN mkdir -p {{domain_home}} -{{^modelOnly}} - COPY --from=source_image --chown={{userid}}:{{groupid}} {{domain_home}} {{domain_home}}/ -{{/modelOnly}} -{{#modelOnly}} - COPY --from=source_image --chown={{userid}}:{{groupid}} {{{wdt_home}}} {{{wdt_home}}}/ - {{#isWdtModelHomeOutsideWdtHome}} - COPY --from=wdt_build --chown={{userid}}:{{groupid}} {{wdt_model_home}} {{wdt_model_home}}/ - {{/isWdtModelHomeOutsideWdtHome}} -{{/modelOnly}} - -{{#useOwnerPermsForGroup}} - RUN chmod -R g=u {{{domain_home}}} -{{/useOwnerPermsForGroup}} - -WORKDIR {{{work_dir}}} - -{{#finalBuildCommands}} - {{{.}}} -{{/finalBuildCommands}} - diff --git a/imagetool/src/main/resources/docker-files/Update_Image.mustache b/imagetool/src/main/resources/docker-files/Update_Image.mustache index 03388629a..c70a327d2 100644 --- a/imagetool/src/main/resources/docker-files/Update_Image.mustache +++ b/imagetool/src/main/resources/docker-files/Update_Image.mustache @@ -9,6 +9,8 @@ FROM {{baseImage}} as final_build {{#buildArgs}}ARG {{{.}}} {{/buildArgs}} +ARG TARGETPLATFORM + USER root ENV OPATCH_NO_FUSER=true diff --git a/imagetool/src/main/resources/docker-files/fmw-patching.mustache b/imagetool/src/main/resources/docker-files/fmw-patching.mustache index 33118cb08..75e188330 100644 --- a/imagetool/src/main/resources/docker-files/fmw-patching.mustache +++ b/imagetool/src/main/resources/docker-files/fmw-patching.mustache @@ -13,19 +13,23 @@ {{/isOpatchPatchingEnabled}} {{#isPatchingEnabled}} - COPY --chown={{userid}}:{{groupid}} patches/* {{{tempDir}}}/patches/ + COPY --chown={{userid}}:{{groupid}} patches/ {{{tempDir}}}/patches/ {{^strictPatchOrdering}} # Apply all patches provided at the same time - RUN {{{oracle_home}}}/OPatch/opatch napply -silent -oh {{{oracle_home}}} -nonrollbackable -phBaseDir {{{tempDir}}}/patches \ + RUN if [ "$(ls -A {{{tempDir}}}/patches/$TARGETPLATFORM)" ] ; then \ + {{{oracle_home}}}/OPatch/opatch napply -silent -oh {{{oracle_home}}} -nonrollbackable -phBaseDir {{{tempDir}}}/patches/$TARGETPLATFORM \ && test $? -eq 0 \ && {{{oracle_home}}}/OPatch/opatch util cleanup -silent -oh {{{oracle_home}}} \ - || (cat {{{oracle_home}}}/cfgtoollogs/opatch/opatch*.log && exit 1) + || (cat {{{oracle_home}}}/cfgtoollogs/opatch/opatch*.log && exit 1) \ + fi {{/strictPatchOrdering}} {{#strictPatchOrdering}} # Apply one patch at a time in the order they were specified {{#patches}} - RUN {{{oracle_home}}}/OPatch/opatch apply -silent -oh {{{oracle_home}}} -nonrollbackable {{{tempDir}}}/patches/{{{.}}} + RUN if [ "$(ls -A {{{tempDir}}}/patches/$TARGETPLATFORM)" ] ; then \ + {{{oracle_home}}}/OPatch/opatch apply -silent -oh {{{oracle_home}}} -nonrollbackable {{{tempDir}}}/$TARGETPLATFORMpatches/$TARGETPLATFORM/{{{.}}} \ + fi {{/patches}} RUN {{{oracle_home}}}/OPatch/opatch util cleanup -silent -oh {{{oracle_home}}} {{/strictPatchOrdering}} diff --git a/imagetool/src/main/resources/docker-files/install-java.mustache b/imagetool/src/main/resources/docker-files/install-java.mustache index bf47e95df..9cae51b4a 100644 --- a/imagetool/src/main/resources/docker-files/install-java.mustache +++ b/imagetool/src/main/resources/docker-files/install-java.mustache @@ -3,14 +3,16 @@ # # Installing Java -FROM os_update as jdk_build +FROM os_update AS jdk_build {{#buildArgs}}ARG {{{.}}} {{/buildArgs}} +ARG TARGETPLATFORM + LABEL com.oracle.weblogic.imagetool.buildid="{{buildId}}" ENV JAVA_HOME={{{java_home}}} - -COPY --chown={{userid}}:{{groupid}} ["{{java_pkg}}", "{{{tempDir}}}/"] +# stage directory +COPY --chown={{userid}}:{{groupid}} jdk {{{tempDir}}}/jdk USER {{userid}} @@ -18,7 +20,8 @@ USER {{userid}} {{{.}}} {{/beforeJdkInstall}} -RUN tar xzf "{{{tempDir}}}/{{java_pkg}}" -C /u01 \ + +RUN jdkfile=$(ls {{{tempDir}}}/jdk/$TARGETPLATFORM/*.gz) && tar xzf "$jdkfile" -C /u01 \ && $(test -d /u01/jdk* && mv /u01/jdk* {{{java_home}}} || mv /u01/graal* {{{java_home}}}) \ && rm -rf {{{tempDir}}} \ && rm -f {{{java_home}}}/javafx-src.zip {{{java_home}}}/src.zip diff --git a/imagetool/src/main/resources/docker-files/install-middleware-pkg.mustache b/imagetool/src/main/resources/docker-files/install-middleware-pkg.mustache new file mode 100644 index 000000000..0227a2a1e --- /dev/null +++ b/imagetool/src/main/resources/docker-files/install-middleware-pkg.mustache @@ -0,0 +1,16 @@ +&& echo "INSTALLING {{type}}" \ +{{#isZip}}&& unzip -q "{{{tempDir}}}/{{{type}}}/$TARGETPLATFORM/{{installerFilename}}" -d {{{tempDir}}}/{{{type}}} {{/isZip}}{{^isZip}}&& : {{/isZip}}\ +{{#preinstallCommands}}&& {{{tempDir}}}/{{{type}}}/{{{.}}} {{/preinstallCommands}}{{^preInstallCommands}}&& : {{/preInstallCommands}}\ +{{^isBin}}&& [ -f {{{tempDir}}}/{{{type}}}/{{jarName}} ] || cp {{{tempDir}}}/{{{type}}}/$TARGETPLATFORM/{{installerFilename}} {{{tempDir}}}/{{{type}}} \ +&& {{{java_home}}}/bin/java -Xmx1024m -jar {{{tempDir}}}/{{{type}}}/{{jarName}} \ +-silent ORACLE_HOME={{{oracle_home}}} \ +-responseFile {{{tempDir}}}/{{{type}}}/$TARGETPLATFORM/{{responseFile.name}} \ +-invPtrLoc {{inv_loc}}/oraInst.loc -ignoreSysPrereqs -force -novalidation {{/isBin}}{{#isBin}}&& : {{/isBin}}\ +{{#isBin}}&& [ -f {{{tempDir}}}/{{{type}}}/{{jarName}} ] || cp {{{tempDir}}}/{{{type}}}/$TARGETPLATFORM/{{installerFilename}} {{{tempDir}}}/{{{type}}} \ +&& chmod +x {{{tempDir}}}/{{{type}}}/{{jarName}} \ +&& {{{tempDir}}}/{{{type}}}/{{jarName}} -force -ignoreSysPrereqs \ +-silent -responseFile "{{{tempDir}}}/{{{type}}}/$TARGETPLATFORM/{{responseFile.name}}" \ +-invPtrLoc {{inv_loc}}/oraInst.loc ORACLE_HOME={{{oracle_home}}} \ +-jreLoc {{{java_home}}} {{/isBin}}{{^isBin}}&& : {{/isBin}}\ +&& chmod -R g+r {{{oracle_home}}} \ +&& { grep -vh "NOTIFICATION" /tmp/OraInstall*/install*.log || true; } \ \ No newline at end of file diff --git a/imagetool/src/main/resources/docker-files/install-middleware.mustache b/imagetool/src/main/resources/docker-files/install-middleware.mustache index 1beccc6a9..eb315ce10 100644 --- a/imagetool/src/main/resources/docker-files/install-middleware.mustache +++ b/imagetool/src/main/resources/docker-files/install-middleware.mustache @@ -3,9 +3,11 @@ # # Installing Middleware -FROM os_update as wls_build +FROM os_update AS wls_build {{#buildArgs}}ARG {{{.}}} {{/buildArgs}} +ARG TARGETPLATFORM + LABEL com.oracle.weblogic.imagetool.buildid="{{buildId}}" ENV JAVA_HOME={{{java_home}}} \ @@ -21,10 +23,10 @@ RUN mkdir -p {{{oracle_home}}} \ && chown {{userid}}:{{groupid}} {{orainv_dir}} \ && chown {{userid}}:{{groupid}} {{{oracle_home}}} -{{#installJava}}COPY --from=jdk_build --chown={{userid}}:{{groupid}} {{{java_home}}} {{{java_home}}}/ +{{#installJava}}COPY --from=jdk_build --chown={{userid}}:{{groupid}} ["{{{java_home}}}", "{{{java_home}}}/"] {{/installJava}} - -{{#installPackages}}COPY --chown={{userid}}:{{groupid}} {{installerFilename}} {{responseFile.name}} {{{tempDir}}}/{{{type}}}/ +# copy all installers and response files to the stage directory +{{#installPackages}}COPY --chown={{userid}}:{{groupid}} fmw {{{tempDir}}}/{{{type}}} {{/installPackages}} COPY --chown={{userid}}:{{groupid}} oraInst.loc {{inv_loc}}/ @@ -34,28 +36,31 @@ USER {{userid}} {{{.}}} {{/beforeFmwInstall}} -RUN echo "INSTALLING MIDDLEWARE" \ -{{#installPackages}} - && echo "INSTALLING {{type}}" \ - # If installer is packaged in a ZIP, extract it before running it - {{#isZip}}&& unzip -q {{{tempDir}}}/{{{type}}}/{{installerFilename}} -d {{{tempDir}}}/{{{type}}} {{/isZip}} \ - {{#preinstallCommands}}&& {{{tempDir}}}/{{{type}}}/{{{.}}} {{/preinstallCommands}} \ - # IF the installer is a JAR file (not a .bin), run the silent install using Java - {{^isBin}} && {{{java_home}}}/bin/java -Xmx1024m -jar {{{tempDir}}}/{{{type}}}/{{jarName}} \ - -silent ORACLE_HOME={{{oracle_home}}} \ - -responseFile {{{tempDir}}}/{{{type}}}/{{responseFile.name}} \ - -invPtrLoc {{inv_loc}}/oraInst.loc \ - -ignoreSysPrereqs -force -novalidation {{/isBin}} \ - # If the installer is a BIN, make sure it is executable and run the installer - {{#isBin}} && chmod +x {{{tempDir}}}/{{{type}}}/{{jarName}} \ - && {{{tempDir}}}/{{{type}}}/{{jarName}} \ - -force -ignoreSysPrereqs -silent \ - -responseFile {{{tempDir}}}/{{{type}}}/{{responseFile.name}} \ - -invPtrLoc {{inv_loc}}/oraInst.loc ORACLE_HOME={{{oracle_home}}} -jreLoc {{{java_home}}} {{/isBin}} \ -{{/installPackages}} - && test $? -eq 0 \ - && chmod -R g+r {{{oracle_home}}} \ - || (grep -vh "NOTIFICATION" /tmp/OraInstall*/install*.log && exit 1) +# If installer is packaged in a ZIP, extract it before running it +# IF the installer is a JAR file (not a .bin), run the silent install using Java +# If the installer is a BIN, make sure it is executable and run the installer +# +# installPackagesForARM - list of packages to install for arm64 platform +# installPackagesForAMD - list of packages to install for amd64 platform + + +RUN case "$TARGETPLATFORM" in \ + "linux/arm64") \ + echo "INSTALLING MIDDLEWARE" \ + {{#installPackagesForARM}} + {{> install-middleware-pkg }} + {{/installPackagesForARM}} + ;; \ + "linux/amd64") \ + echo "INSTALLING MIDDLEWARE" \ + {{#installPackagesForAMD}} + {{> install-middleware-pkg }} + {{/installPackagesForAMD}} + ;; \ + esac \ + && chmod -R g+r {{{oracle_home}}} \ + || { grep -vh "NOTIFICATION" /tmp/OraInstall*/install*.log || true; } + {{#useOwnerPermsForGroup}} # OPatch needs write permissions to the logs folder and lock file when running in OpenShift diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/aru/AruUtilTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/aru/AruUtilTest.java index 179d5c151..350b99cd9 100644 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/aru/AruUtilTest.java +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/aru/AruUtilTest.java @@ -86,7 +86,8 @@ void testRecommendedPatches() throws Exception { // if no recommended patches are found, method should return an empty list (test data does not have 12.2.1.4) recommendedPatches = - AruUtil.rest().getRecommendedPatches(FmwInstallerType.WLS, "12.2.1.4.0", Architecture.AMD64, "x", "x"); + AruUtil.rest().getRecommendedPatches(FmwInstallerType.WLS, "12.2.1.4.0", Architecture.AMD64, + "12.2.1.4.0", "x", "x"); assertTrue(recommendedPatches.isEmpty()); } diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/builder/BuilderTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/builder/BuilderTest.java index bfd029ce7..8352c3b4a 100644 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/builder/BuilderTest.java +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/builder/BuilderTest.java @@ -17,7 +17,7 @@ class BuilderTest { private static final String BUILD_ENGINE = "docker"; private String expected(String options) { - return String.format("%s build %s %s", BUILD_ENGINE, options, BUILD_CONTEXT); + return String.format("%s build %s %s --progress=plain", BUILD_ENGINE, options, BUILD_CONTEXT); } @Test diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/CacheStoreTestImpl.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/CacheStoreTestImpl.java deleted file mode 100644 index 71e1cce24..000000000 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/CacheStoreTestImpl.java +++ /dev/null @@ -1,63 +0,0 @@ -// 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. - -package com.oracle.weblogic.imagetool.cachestore; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class CacheStoreTestImpl implements CacheStore { - - private final HashMap cache = new HashMap<>(); - private final Path cacheDir; - - public CacheStoreTestImpl(Path cacheDir) { - this.cacheDir = cacheDir; - } - - @Override - public String getCacheDir() { - return cacheDir.toString(); - } - - @Override - public String getValueFromCache(String key) { - return cache.get(key); - } - - @Override - public boolean containsKey(String key) { - if (key == null) { - return false; - } - return cache.containsKey(key.toLowerCase()); - } - - @Override - public void addToCache(String key, String value) { - cache.put(key.toLowerCase(), value); - } - - @Override - public String deleteFromCache(String key) { - return null; - } - - @Override - public void clearCache() { - cache.clear(); - } - - @Override - public Map getCacheItems() { - return cache; - } - - @Override - public List getKeysForType(String type) { - return new ArrayList<>(); - } -} diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/CachedFileTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/CachedFileTest.java index bac0be3a0..399c9892a 100644 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/CachedFileTest.java +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/CachedFileTest.java @@ -7,15 +7,23 @@ 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.Arrays; import java.util.List; +import java.util.Map; import javax.xml.xpath.XPathExpressionException; import com.oracle.weblogic.imagetool.api.model.CachedFile; import com.oracle.weblogic.imagetool.aru.AruException; +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import com.oracle.weblogic.imagetool.test.annotations.ReduceTestLogging; import com.oracle.weblogic.imagetool.util.Architecture; +import com.oracle.weblogic.imagetool.util.TestSetup; +import com.oracle.weblogic.imagetool.util.Utils; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -46,102 +54,150 @@ static void setup(@TempDir Path tempDir, @TempDir Path cacheDir) throws IOExcept Path path1411 = tempDir.resolve("installer.file.141100.jar"); Files.write(path1411, fileContents); - CachedFileTest.cacheDir = cacheDir; - cacheStore = new CacheStoreTestImpl(cacheDir); - // build a fake cache with several installers - cacheStore.addToCache("wls_" + VER_12213, path12213.toString()); - cacheStore.addToCache("wls_12.2.1.4.0_" + Architecture.getLocalArchitecture(), path12214.toString()); - cacheStore.addToCache("wls_14.1.1.0.0_amd64", path1411.toString()); - cacheStore.addToCache("wls_14.1.1.0.0_linux/arm64", path1411.toString()); + + TestSetup.setup(tempDir); + ConfigManager configManager = ConfigManager.getInstance(); + Path patchFile = Paths.get(configManager.getPatchDetailsFile()); + + + InstallerMetaData installer1 = new InstallerMetaData("Generic", path12213.toString(), + VER_12213, VER_12213); + InstallerMetaData installer2 = new InstallerMetaData(Architecture.getLocalArchitecture().toString(), + path12214.toString(), + "12.2.1.4.0", "12.2.1.4.0"); + InstallerMetaData installer3 = new InstallerMetaData("linux/amd64", path1411.toString(), + "14.1.1.0.0", "14.1.1.0.0"); + InstallerMetaData installer4 = new InstallerMetaData("linux/arm64", path1411.toString(), + "14.1.1.0.0", "14.1.1.0.0"); + + configManager.addInstaller(InstallerType.WLS, VER_12213, installer1); + configManager.addInstaller(InstallerType.WLS, "12.2.1.4.0", installer2); + configManager.addInstaller(InstallerType.WLS, "14.1.1.0.0", installer3); + configManager.addInstaller(InstallerType.WLS, "14.1.1.0.0", installer4); + + addPatchesToLocal(tempDir, configManager, patchFile, DEFAULT_BUG_NUM, + "Generic", "patch1.zip", "13.9.2.0.0"); + addPatchesToLocal(tempDir, configManager, patchFile, DEFAULT_BUG_NUM, + "Generic", "patch1.zip", "13.9.4.0.0"); + addPatchesToLocal(tempDir, configManager, patchFile, DEFAULT_BUG_NUM, + "Generic", "patch1.zip", "13.9.2.2.2"); // OPatch files - cacheStore.addToCache(DEFAULT_BUG_NUM + "_13.9.2.0.0", "/not/used"); - cacheStore.addToCache(DEFAULT_BUG_NUM + "_13.9.4.0.0", "/not/used"); - cacheStore.addToCache(DEFAULT_BUG_NUM + "_13.9.2.2.2", "/not/used"); + //cacheStore.addToCache(DEFAULT_BUG_NUM + "_13.9.2.0.0", "/not/used"); + //cacheStore.addToCache(DEFAULT_BUG_NUM + "_13.9.4.0.0", "/not/used"); + //cacheStore.addToCache(DEFAULT_BUG_NUM + "_13.9.2.2.2", "/not/used"); + } + + private static void addPatchesToLocal(Path tempDir, ConfigManager configManager, Path patchListingFile, + String bugNumber, String patchArchitecture, String patchLocation, + String patchVersion) throws IOException { + Map> patches = configManager.getAllPatches(); + List latestPatches = patches.get(bugNumber); + if (latestPatches == null) { + latestPatches = new ArrayList<>(); + } + Path path = tempDir.resolve(patchLocation); + Files.write(path, fileContents); + PatchMetaData latestPatch = new PatchMetaData(patchArchitecture, path.toAbsolutePath().toString(), + Utils.getSha256Hash(path.toAbsolutePath().toString()),"2024-10-17", patchVersion,""); + latestPatches.add(latestPatch); + patches.put(bugNumber, latestPatches); + configManager.saveAllPatches(patches); } @Test void versionString() { CachedFile wlsInstallerFile = new CachedFile(InstallerType.WLS, "12.2.1.3.0"); - - assertEquals("wls_12.2.1.3.0", wlsInstallerFile.getKey(), - "CachedFile getKey() did not construct the cache key correctly"); assertEquals("12.2.1.3.0", wlsInstallerFile.getVersion(), "CachedFile should return the version from the constructor"); } - @Test - void userProvidedPatchVersionAsId() { - // User provided a patch ID with the version string in the ID - CachedFile cf = new CachedFile("something_versionString", "12.2.1.2.0", Architecture.AMD64); - // if the patch ID has the version, CachedFile should ignore the installer version passed to the constructor, - // and use the version in the ID. - assertEquals("something_versionString", cf.getKey(), - "CachedFile getKey() failed when version string was provided by the user in the ID"); - - // getVersion should always return the version that was provided in the constructor, not the one in the ID - assertEquals("12.2.1.2.0", cf.getVersion(), "CachedFile returned wrong version"); - } + ////@Test + //void userProvidedPatchVersionAsId() { + // // User provided a patch ID with the version string in the ID + // CachedFile cf = new CachedFile("something_versionString", "12.2.1.2.0", Architecture.AMD64); + // // if the patch ID has the version, CachedFile should ignore the installer version passed to the constructor, + // // and use the version in the ID. + // assertEquals("something_versionString", cf.getPatchId(), + // "CachedFile getKey() failed when version string was provided by the user in the ID"); + // + // // getVersion should always return the version that was provided in the constructor, not the one in the ID + // assertEquals("12.2.1.2.0", cf.getVersion(), "CachedFile returned wrong version"); + //} @Test void resolveFileNotFound() { // resolve should fail for a CachedFile that is not in the store CachedFile fakeFile = new CachedFile(InstallerType.WLS, "10.3.6.0.0"); - assertThrows(FileNotFoundException.class, () -> fakeFile.resolve(cacheStore)); + assertThrows(FileNotFoundException.class, () -> fakeFile.resolve()); } @Test void resolveFileFindsFile() throws IOException { // Resolve a CachedFile stored in the cache (created in test setup above) CachedFile wlsInstallerFile = new CachedFile(InstallerType.WLS, VER_12213); - String expected = cacheStore.getValueFromCache("wls_" + VER_12213); - assertEquals(expected, wlsInstallerFile.resolve(cacheStore), "CachedFile did not resolve file"); + InstallerMetaData metaData = ConfigManager.getInstance().getInstallerForPlatform(InstallerType.WLS, + Architecture.GENERIC, VER_12213); + assertNotNull(metaData); + String expected = metaData.getLocation(); + assertEquals(expected, wlsInstallerFile.resolve(), "CachedFile did not resolve file"); } @Test void resolveNoArchFile() throws IOException { // Look for a cache entry where the user specified the architecture/platform amd64 - CachedFile wlsNoArch = new CachedFile(InstallerType.WLS, VER_12213, Architecture.fromString("amd64")); + CachedFile wlsNoArch = new CachedFile(InstallerType.WLS, VER_12213, Architecture.getLocalArchitecture()); + InstallerMetaData metaData = ConfigManager.getInstance().getInstallerForPlatform(InstallerType.WLS, + Architecture.AMD64, VER_12213); // verify the cache is setup as expected. // wls_12.2.1.3.0 is in the cache, but wls_12.2.1.3.0_amd64 is NOT in the cache - assertNull(cacheStore.getValueFromCache("wls_12.2.1.3.0_amd64")); - String expected = cacheStore.getValueFromCache("wls_12.2.1.3.0"); - assertNotNull(expected); - - assertEquals(expected, wlsNoArch.resolve(cacheStore), "CachedFile returned wrong file"); + assertNull(metaData); + metaData = ConfigManager.getInstance().getInstallerForPlatform(InstallerType.WLS, + Architecture.GENERIC, VER_12213); + assertNotNull(metaData); + String expected = metaData.getLocation(); + assertEquals(expected, wlsNoArch.resolve(), "CachedFile returned wrong file"); } @Test void resolveWithArchitecture() throws IOException { // Look for a cache entry where the user specified the architecture/platform amd64 CachedFile wlsArch = new CachedFile(InstallerType.WLS, "14.1.1.0.0", Architecture.fromString("amd64")); - + InstallerMetaData metaData = ConfigManager.getInstance().getInstallerForPlatform(InstallerType.WLS, + Architecture.AMD64, "14.1.1.0.0"); + assertNotNull(metaData); // verify the cache is setup as expected. wls_14.1.1.0.0_amd64 is in the cache - String expected = cacheStore.getValueFromCache("wls_14.1.1.0.0_amd64"); + String expected = metaData.getLocation(); assertNotNull(expected); - assertEquals(expected, wlsArch.resolve(cacheStore), "CachedFile failed to find specific architecture"); + assertEquals(expected, wlsArch.resolve(), "CachedFile failed to find specific architecture"); } @Test void resolveFallbackToLocalArch() throws IOException { // Look for a cache entry where the user did not specify the architecture/platform CachedFile wlsArch = new CachedFile(InstallerType.WLS, "12.2.1.4.0"); + InstallerMetaData metaData = ConfigManager.getInstance().getInstallerForPlatform(InstallerType.WLS, + Architecture.GENERIC, "12.2.1.4.0"); // verify the cache is setup as expected. wls_14.1.1.0.0_amd64 is in the cache, but wls_14.1.1.0.0 is not - assertNull(cacheStore.getValueFromCache("wls_12.2.1.4.0")); - String expected = cacheStore.getValueFromCache("wls_12.2.1.4.0_" + Architecture.getLocalArchitecture()); + assertNull(metaData); + metaData = ConfigManager.getInstance().getInstallerForPlatform(InstallerType.WLS, + Architecture.getLocalArchitecture(), "12.2.1.4.0"); + assertNotNull(metaData); + String expected = metaData.getLocation(); + //cacheStore.getValueFromCache("wls_12.2.1.4.0_" + Architecture.getLocalArchitecture()); assertNotNull(expected); - assertEquals(expected, wlsArch.resolve(cacheStore), "CachedFile failed to check local architecture"); + assertEquals(expected, wlsArch.resolve(), "CachedFile failed to check local architecture"); } @Test void copyFile(@TempDir Path contextDir) throws Exception { CachedFile wlsInstallerFile = new CachedFile(InstallerType.WLS, VER_12213); // copy the file from the cache store to the fake build context directory - Path result = wlsInstallerFile.copyFile(cacheStore, contextDir.toString()); + Path result = wlsInstallerFile.copyFile(contextDir.toString()); // check to see if the file was copied correctly by examining the contents of the resulting file assertLinesMatch(fileContents, Files.readAllLines(result), "copied file contents do not match source"); @@ -150,16 +206,19 @@ void copyFile(@TempDir Path contextDir) throws Exception { @Test void latestOpatchVersion() throws IOException, AruException, XPathExpressionException { // OPatch file should default to the default OPatch bug number and the latest version found in cache - OPatchFile patchFile = OPatchFile.getInstance(null, null, null, cacheStore); - assertEquals(DEFAULT_BUG_NUM + "_13.9.4.0.0", patchFile.getKey(), - "failed to get latest Opatch version from the cache"); + OPatchFile patchFile = OPatchFile.getInstance(null, null, null); + assertEquals("13.9.4.0.0", patchFile.getVersion(), "wrong version selected"); + + //assertEquals(DEFAULT_BUG_NUM + "_13.9.4.0.0", patchFile.getPatchId(), + // "failed to get latest Opatch version from the cache"); } @Test void specificOpatchVersion() throws IOException, AruException, XPathExpressionException { // OPatch file should default to the default OPatch bug number and the latest version found in cache - OPatchFile patchFile = OPatchFile.getInstance(DEFAULT_BUG_NUM + "_13.9.2.2.2", null, null, cacheStore); - assertEquals(DEFAULT_BUG_NUM + "_13.9.2.2.2", patchFile.getKey(), + OPatchFile patchFile = OPatchFile.getInstance(DEFAULT_BUG_NUM, "13.9.2.2.2", + null, null); + assertEquals("13.9.2.2.2", patchFile.getVersion(), "failed to get specific Opatch version from the cache"); } } diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/FileCacheStoreTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/FileCacheStoreTest.java deleted file mode 100644 index 4464897a4..000000000 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/FileCacheStoreTest.java +++ /dev/null @@ -1,81 +0,0 @@ -// 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 com.oracle.weblogic.imagetool.cachestore; - -import java.io.File; - -import org.junit.jupiter.api.BeforeAll; -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; -import org.junit.jupiter.api.io.TempDir; - -import static com.oracle.weblogic.imagetool.cachestore.CacheStoreFactory.cache; -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.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@Tag("unit") -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -class FileCacheStoreTest { - - private static final String TEST_KEY = "abc_xyz_123"; - private static final String TEST_VAL = "this_is_a_test"; - - @BeforeAll - static void init(@TempDir File tempDir) throws CacheStoreException { - System.setProperty(FileCacheStore.CACHE_DIR_ENV, tempDir.getAbsolutePath()); - cache().clearCache(); - } - - @Test - @Order(1) - void addingValueToCache() { - // add value to cache - assertDoesNotThrow(() -> cache().addToCache(TEST_KEY, TEST_VAL), "Add to cache threw an exception"); - } - - @Test - @Order(2) - void checkValueInCache() { - // check to see if the key that was just added is there, and value matches expected value - assertDoesNotThrow(() -> - assertTrue(cache().containsKey(TEST_KEY)), - "containsKey failed to find key or value that was just added"); - - // check (another way) that the value was just added - assertDoesNotThrow(() -> - assertEquals(TEST_VAL, cache().getValueFromCache(TEST_KEY), "Found unexpected value in cache"), - "Get from cache threw an exception"); - } - - @Test - @Order(3) - void deleteValueFromCache() { - assertDoesNotThrow(() -> - assertNull(cache().deleteFromCache("non_existent_key"), - "Deleting non-existent key should not have a value"), - "Delete from cache threw an exception"); - - assertDoesNotThrow(() -> - assertEquals(TEST_VAL, cache().deleteFromCache(TEST_KEY), "Value from deleted key did not match"), - "Delete from cache threw an exception"); - } - - @Test - @Order(4) - void verifyCacheSize() { - assertDoesNotThrow(() -> - assertNotNull(cache().getCacheItems(), "Get cache items should never be null"), - "getCacheItems threw an exception"); - - assertDoesNotThrow(() -> - assertEquals(0, cache().getCacheItems().size(), "Get cache items should never be null"), - "getCacheItems threw an exception"); - } -} diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/OPatchFileTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/OPatchFileTest.java index ba48b685f..97b6c6943 100644 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/OPatchFileTest.java +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/OPatchFileTest.java @@ -6,13 +6,21 @@ 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.Arrays; +import java.util.List; +import java.util.Map; import javax.xml.xpath.XPathExpressionException; import com.oracle.weblogic.imagetool.aru.AruException; import com.oracle.weblogic.imagetool.aru.MockAruUtil; import com.oracle.weblogic.imagetool.cli.menu.CommonOptions; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import com.oracle.weblogic.imagetool.test.annotations.ReduceTestLogging; +import com.oracle.weblogic.imagetool.util.TestSetup; +import com.oracle.weblogic.imagetool.util.Utils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; @@ -28,6 +36,7 @@ @Tag("unit") class OPatchFileTest { private static CacheStore cacheStore; + static final List fileContents = Arrays.asList("A", "B", "C"); @BeforeAll static void setup(@TempDir Path tempDir, @TempDir Path cacheDir) @@ -36,11 +45,30 @@ static void setup(@TempDir Path tempDir, @TempDir Path cacheDir) // Mock REST calls to ARU for patches MockAruUtil.insertMockAruInstance(new MockAruUtil()); - // Create a fake cache with a fake OPatch install - cacheStore = new CacheStoreTestImpl(cacheDir); - Path installer = tempDir.resolve("opatch_install.zip"); - Files.write(installer, Arrays.asList("A", "B", "C")); - cacheStore.addToCache("28186730_13.9.4.2.10", installer.toString()); + TestSetup.setup(tempDir); + ConfigManager configManager = ConfigManager.getInstance(); + Path patchFile = Paths.get(configManager.getPatchDetailsFile()); + + addPatchesToLocal(tempDir, configManager, patchFile, "28186730", + "Generic", "patch1.zip","13.9.4.2.10"); + + } + + private static void addPatchesToLocal(Path tempDir, ConfigManager configManager, Path patchListingFile, + String bugNumber, String patchArchitecture, String patchLocation, + String patchVersion) throws IOException { + Map> patches = configManager.getAllPatches(); + List latestPatches = patches.get(bugNumber); + if (latestPatches == null) { + latestPatches = new ArrayList<>(); + } + Path path = tempDir.resolve(patchLocation); + Files.write(path, fileContents); + PatchMetaData latestPatch = new PatchMetaData(patchArchitecture, path.toAbsolutePath().toString(), + Utils.getSha256Hash(path.toAbsolutePath().toString()),"2024-10-17", patchVersion, ""); + latestPatches.add(latestPatch); + patches.put(bugNumber, latestPatches); + configManager.saveAllPatches(patches); } @AfterAll @@ -61,7 +89,7 @@ void opatchPatchIdTest() { private void checkOpatchVersion(String expectedVersion, String patchId) throws XPathExpressionException, IOException, AruException { - OPatchFile opatchFile = OPatchFile.getInstance(patchId, "xxxx", "yyyy", cacheStore); + OPatchFile opatchFile = OPatchFile.getInstance(patchId, "xxxx", "yyyy"); assertNotNull(opatchFile); assertEquals(expectedVersion, opatchFile.getVersion()); } @@ -90,7 +118,7 @@ void onlineFindAruVersion() throws XPathExpressionException, IOException, AruExc void offlineFindHighestVersion() throws XPathExpressionException, IOException, AruException { // if the user does not specify an opatchBugNumber, // the code should search the local cache for the highest version - OPatchFile opatchFile = OPatchFile.getInstance(null, null, null, cacheStore); + OPatchFile opatchFile = OPatchFile.getInstance(null, null, null); assertNotNull(opatchFile); assertEquals("13.9.4.2.10", opatchFile.getVersion()); } diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/PatchFileTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/PatchFileTest.java index 4d4e42948..47945c72e 100644 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/PatchFileTest.java +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cachestore/PatchFileTest.java @@ -9,6 +9,8 @@ import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -26,7 +28,11 @@ import com.oracle.weblogic.imagetool.aru.VersionNotFoundException; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.settings.ConfigManager; +import com.oracle.weblogic.imagetool.settings.UserSettingsFile; import com.oracle.weblogic.imagetool.util.Architecture; +import com.oracle.weblogic.imagetool.util.TestSetup; import com.oracle.weblogic.imagetool.util.Utils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -35,6 +41,7 @@ import org.junit.jupiter.api.io.TempDir; import org.w3c.dom.Document; +import static com.oracle.weblogic.imagetool.util.Constants.ARM64_BLD; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -49,28 +56,33 @@ class PatchFileTest { static final String BUGNUMBER = "456789"; static final String SOME_VERSION = "12.2.1.3.0"; static Level originalLogLevel; + static UserSettingsFile userSettingsFile; - private static void addToCache(Path tempDir, String key, String filename) throws IOException { - Path path = tempDir.resolve(filename); - Files.write(path, fileContents); - cacheStore.addToCache(key, path.toString()); - } @BeforeAll static void setup(@TempDir Path tempDir) throws IOException, NoSuchFieldException, IllegalAccessException { + TestSetup.setup(tempDir); + ConfigManager configManager = ConfigManager.getInstance(); + Path patchFile = Paths.get(configManager.getPatchDetailsFile()); + + addPatchesToLocal(tempDir, configManager, patchFile, BUGNUMBER, + "Generic", "patch1.zip",SOME_VERSION); + addPatchesToLocal(tempDir, configManager, patchFile, "11100003", + "Generic", "p11100003_12213181016_Generic.zip","12.2.1.3.0.181016"); + addPatchesToLocal(tempDir, configManager, patchFile, "11100003", + "Generic", "p11100003_122130_Generic.zip","12.2.1.3.0"); + addPatchesToLocal(tempDir, configManager, patchFile, "11100007", + "linux/arm64", "p11100007_122140_ARM64.zip","12.2.1.4.0"); + addPatchesToLocal(tempDir, configManager, patchFile, "11100007", + "linux/amd64", "p11100007_122140_AMD64.zip","12.2.1.4.0"); + addPatchesToLocal(tempDir, configManager, patchFile, "11100008", + "linux/arm64", "p11100008_122140_ARM64.zip","12.2.1.4.0"); + addPatchesToLocal(tempDir, configManager, patchFile, "11100008", + "linux/amd64", "p11100008_122140_AMD64.zip","12.2.1.4.0"); + addPatchesToLocal(tempDir, configManager, patchFile, "11100008", + "Generic", "p11100008_122140_GENERIC.zip","12.2.1.4.0"); - cacheStore = new FileStoreTestImpl(); - // build a fake cache with fake installers - addToCache(tempDir, BUGNUMBER + "_" + SOME_VERSION, "patch1.zip"); - addToCache(tempDir, "wls_12.2.1.4.0", "installer.file.122140.jar"); - addToCache(tempDir, "11100003_12.2.1.3.181016", "p11100003_12213181016_Generic.zip"); - addToCache(tempDir, "11100003_12.2.1.3.0", "p11100003_122130_Generic.zip"); - addToCache(tempDir, "11100007_12.2.1.4.0_arm64", "p11100007_122140_ARM64.zip"); - addToCache(tempDir, "11100007_12.2.1.4.0_amd64", "p11100007_122140_AMD64.zip"); - addToCache(tempDir, "11100008_12.2.1.4.0_arm64", "p11100008_122140_ARM64.zip"); - addToCache(tempDir, "11100008_12.2.1.4.0_amd64", "p11100008_122140_AMD64.zip"); - addToCache(tempDir, "11100008_12.2.1.4.0", "p11100008_122140_GENERIC.zip"); // disable console logging LoggingFacade logger = LoggingFactory.getLogger(PatchFile.class); originalLogLevel = logger.getLevel(); @@ -82,22 +94,29 @@ static void setup(@TempDir Path tempDir) aruRest.set(aruRest, new TestAruUtil()); // insert test class into CacheStoreFactory to intercept cache calls - Field cacheFactory = CacheStoreFactory.class.getDeclaredField("store"); - cacheFactory.setAccessible(true); - cacheFactory.set(cacheFactory, cacheStore); + //Field cacheFactory = CacheStoreFactory.class.getDeclaredField("store"); + //cacheFactory.setAccessible(true); + //cacheFactory.set(cacheFactory, cacheStore); } - public static class FileStoreTestImpl extends FileCacheStore { - public FileStoreTestImpl() throws CacheStoreException { - super(); - } - - @Override - String getCacheDirSetting() { - return PatchFileTest.cacheDir.toString(); + private static void addPatchesToLocal(Path tempDir, ConfigManager configManager, Path patchListingFile, + String bugNumber, String patchArchitecture, String patchLocation, + String patchVersion) throws IOException { + Map> patches = configManager.getAllPatches(); + List latestPatches = patches.get(bugNumber); + if (latestPatches == null) { + latestPatches = new ArrayList<>(); } + Path path = tempDir.resolve(patchLocation); + Files.write(path, fileContents); + PatchMetaData latestPatch = new PatchMetaData(patchArchitecture, path.toAbsolutePath().toString(), + Utils.getSha256Hash(path.toAbsolutePath().toString()),"2024-10-17", patchVersion, ""); + latestPatches.add(latestPatch); + patches.put(bugNumber, latestPatches); + configManager.saveAllPatches(patches); } + /** * Intercept calls to the ARU REST API during unit testing. */ @@ -145,11 +164,6 @@ static void teardown() throws NoSuchFieldException, IllegalAccessException { aruRest.setAccessible(true); aruRest.set(aruRest, null); - // remove test class from CacheStoreFactory instance - Field cacheStore = CacheStoreFactory.class.getDeclaredField("store"); - cacheStore.setAccessible(true); - cacheStore.set(cacheStore, null); - // restore original logging level after this test suite completes LoggingFacade logger = LoggingFactory.getLogger(PatchFile.class); logger.setLevel(originalLogLevel); @@ -160,13 +174,15 @@ void resolveFile() throws IOException { // resolve should fail for a PatchFile that is not in the store AruPatch aruPatch1 = new AruPatch().patchId("99999").version(SOME_VERSION); PatchFile p1 = new PatchFile(aruPatch1, null,null); - assertThrows(FileNotFoundException.class, () -> p1.resolve(cacheStore)); + assertThrows(FileNotFoundException.class, () -> p1.resolve()); // PatchFile resolve should result in the same behavior has getting the path from the cache store AruPatch aruPatch2 = new AruPatch().patchId(BUGNUMBER).version(SOME_VERSION); PatchFile patch2 = new PatchFile(aruPatch2, null,null); - String expected = cacheStore.getValueFromCache(BUGNUMBER + "_" + SOME_VERSION); - assertEquals(expected, patch2.resolve(cacheStore), "failed to resolve patch in cache"); + PatchMetaData patchMetaData = ConfigManager.getInstance().getPatchForPlatform( + "Generic", BUGNUMBER, SOME_VERSION); + String expected = patchMetaData.getLocation(); + assertEquals(expected, patch2.resolve(), "failed to resolve patch in cache"); } @Test @@ -185,10 +201,13 @@ void gettingNewPatch() throws Exception { assertNotNull(aruPatch); PatchFile patchFile = new PatchFile(aruPatch, "x", "x"); - String filePath = patchFile.resolve(cacheStore); + String filePath = patchFile.resolve(); assertNotNull(filePath, "Patch resolve() failed to get file path from XML"); - String filePathFromCache = cacheStore.getValueFromCache(patchId + "_12.2.1.3.0"); + PatchMetaData patchMetaData = ConfigManager.getInstance().getPatchForPlatform( + "Generic", patchId, "12.2.1.3.0"); + + String filePathFromCache = patchMetaData.getLocation(); assertNotNull(filePathFromCache, "Could not find new patch in cache"); assertEquals(filePath, filePathFromCache, "Patch in cache does not match"); } @@ -228,10 +247,13 @@ void multiplePatchVersionsNoVersionSupplied() throws Exception { assertNotNull(aruPatch); PatchFile patchFile = new PatchFile(aruPatch, "x", "x"); - String filePath = patchFile.resolve(cacheStore); + String filePath = patchFile.resolve(); assertNotNull(filePath, "Patch resolve() failed to get file path from XML"); - String filePathFromCache = cacheStore.getValueFromCache(patchId + "_12.2.1.3.0"); + PatchMetaData patchMetaData = ConfigManager.getInstance().getPatchForPlatform( + "Generic", patchId, aruPatch.version()); + + String filePathFromCache = patchMetaData.getLocation(); assertNotNull(filePathFromCache, "Could not find new patch in cache"); assertEquals(filePath, filePathFromCache, "Patch in cache does not match"); } @@ -253,10 +275,13 @@ void psuInvolvedNoVersionSupplied() throws Exception { AruPatch aruPatch = AruPatch.selectPatch(aruPatches, null, "12.2.1.3.181016", "12.2.1.3.0"); assertNotNull(aruPatch); PatchFile patchFile = new PatchFile(aruPatch, "x", "x"); - String filePath = patchFile.resolve(cacheStore); + String filePath = patchFile.resolve(); assertNotNull(filePath, "Patch resolve() failed to get file path from XML"); - String filePathFromCache = cacheStore.getValueFromCache(patchId + "_12.2.1.3.0"); + PatchMetaData patchMetaData = ConfigManager.getInstance().getPatchForPlatform( + "Generic", patchId, aruPatch.version()); + + String filePathFromCache = patchMetaData.getLocation(); assertNotNull(filePathFromCache, "Could not find new patch in cache"); assertEquals(filePath, filePathFromCache, "Patch in cache does not match"); } @@ -279,10 +304,14 @@ void psuInvolvedNoVersionSuppliedHasPsuVersions() throws Exception { assertNotNull(aruPatch); PatchFile patchFile = new PatchFile(aruPatch, "x", "x"); - String filePath = patchFile.resolve(cacheStore); + String filePath = patchFile.resolve(); assertNotNull(filePath, "Patch resolve() failed to get file path from XML"); - String filePathFromCache = cacheStore.getValueFromCache(patchId + "_12.2.1.3.181016"); + PatchMetaData patchMetaData = ConfigManager.getInstance().getPatchForPlatform( + "Generic", patchId, aruPatch.version()); + + String filePathFromCache = patchMetaData.getLocation(); + assertNotNull(filePathFromCache, "Could not find new patch in cache"); assertEquals(filePath, filePathFromCache, "Patch in cache does not match"); } @@ -305,10 +334,14 @@ void noVersionSuppliedNoAru() throws Exception { assertNotNull(aruPatch); PatchFile patchFile = new PatchFile(aruPatch, null, null); - String filePath = patchFile.resolve(cacheStore); + String filePath = patchFile.resolve(); assertNotNull(filePath, "Patch resolve() failed to get file path from cache"); - String filePathFromCache = cacheStore.getValueFromCache(patchId + "_" + SOME_VERSION); + PatchMetaData patchMetaData = ConfigManager.getInstance().getPatchForPlatform( + "Generic", patchId, aruPatch.version()); + + String filePathFromCache = patchMetaData.getLocation(); + assertNotNull(filePathFromCache, "Could not find new patch in cache"); assertEquals(filePath, filePathFromCache, "Patch in cache does not match"); } @@ -330,10 +363,13 @@ void psuInvolvedVersionSuppliedHasPsuVersions() throws Exception { assertNotNull(aruPatch); PatchFile patchFile = new PatchFile(aruPatch, "x", "x"); - String filePath = patchFile.resolve(cacheStore); + String filePath = patchFile.resolve(); assertNotNull(filePath, "Patch resolve() failed to get file path from XML"); - String filePathFromCache = cacheStore.getValueFromCache(patchId + "_12.2.1.3.0"); + PatchMetaData patchMetaData = ConfigManager.getInstance().getPatchForPlatform( + "Generic", patchId, aruPatch.version()); + + String filePathFromCache = patchMetaData.getLocation(); assertNotNull(filePathFromCache, "Could not find new patch in cache"); assertEquals(filePath, filePathFromCache, "Patch in cache does not match"); } @@ -352,7 +388,8 @@ void throwsVersionNotFound() throws Exception { String patchId = "11100002"; List aruPatches = AruUtil.rest().getPatches(patchId, "x", "x").collect(Collectors.toList()); assertThrows(VersionNotFoundException.class, - () -> AruPatch.selectPatch(aruPatches, "12.2.1.3.0", "12.2.1.3.181016", "12.2.1.3.1")); + () -> AruPatch.selectPatch(aruPatches, "12.2.1.3.0", "12.2.1.3.181016", + "12.2.1.3.1")); } @Test @@ -367,13 +404,17 @@ void opatchDefaultTest() throws Exception { // 28186730 has multiple patches available, but none are specified String patchId = null; - OPatchFile patchFile = OPatchFile.getInstance(patchId, "x", "x", cacheStore); + OPatchFile patchFile = OPatchFile.getInstance(patchId, "x", "x"); - String filePath = patchFile.resolve(cacheStore); + String filePath = patchFile.resolve(); assertNotNull(filePath, "Patch resolve() failed to get file path from XML"); assertEquals("13.9.4.2.5", patchFile.getVersion(), "wrong version selected"); - String filePathFromCache = cacheStore.getValueFromCache("28186730_13.9.4.2.5"); + PatchMetaData patchMetaData = ConfigManager.getInstance().getPatchForPlatform( + "Generic", "28186730", "13.9.4.2.5"); + + String filePathFromCache = patchMetaData.getLocation(); + assertNotNull(filePathFromCache, "Could not find new patch in cache"); assertEquals(filePath, filePathFromCache, "Patch in cache does not match"); } @@ -390,7 +431,7 @@ void opatchProvidedVersionTest() throws Exception { // 28186730 has multiple patches available, but none are specified String patchId = "28186730_13.9.4.2.5"; - OPatchFile patchFile = OPatchFile.getInstance(patchId, "x", "x", cacheStore); + OPatchFile patchFile = OPatchFile.getInstance(patchId, "x", "x"); assertEquals("13.9.4.2.5", patchFile.getVersion(), "wrong version selected"); } @@ -408,7 +449,7 @@ void opatchProvidedWrongVersionTest() { // 28186730 has multiple patches available, but none are specified String patchId = "28186730_13.9.4.2.2"; assertThrows(VersionNotFoundException.class, () -> - OPatchFile.getInstance(patchId, "x", "x", cacheStore)); + OPatchFile.getInstance(patchId, "x", "x")); } @@ -425,14 +466,18 @@ void opatchNoRecommendedTest() throws Exception { // 28186730 has multiple patches available, but none are specified String patchId = "2818673x"; - OPatchFile patchFile = OPatchFile.getInstance(patchId, "x", "x", cacheStore); + OPatchFile patchFile = OPatchFile.getInstance(patchId, "x", "x"); assertEquals("13.9.4.2.5", patchFile.getVersion()); - String filePath = patchFile.resolve(cacheStore); + String filePath = patchFile.resolve(); assertNotNull(filePath, "Patch resolve() failed to get file path from XML"); assertEquals("13.9.4.2.5", patchFile.getVersion(), "wrong version selected"); - String filePathFromCache = cacheStore.getValueFromCache("28186730_13.9.4.2.5"); + PatchMetaData patchMetaData = ConfigManager.getInstance().getPatchForPlatform( + "Generic", "28186730", "13.9.4.2.5"); + + String filePathFromCache = patchMetaData.getLocation(); + assertNotNull(filePathFromCache, "Could not find new patch in cache"); assertEquals(filePath, filePathFromCache, "Patch in cache does not match"); } @@ -444,13 +489,15 @@ void resolveArmFile() throws IOException { aruPatch.platform("541"); // if local system is ARM PatchFile p1 = new PatchFile(aruPatch, null,null); - assertFalse(Utils.isEmptyString(p1.resolve(cacheStore))); - assertEquals("11100007_12.2.1.4.0_arm64", p1.toString()); + String filePath = p1.resolve(); + assertFalse(Utils.isEmptyString(filePath)); + assertEquals("p11100007_122140_ARM64.zip", Paths.get(filePath).getFileName().toString()); aruPatch.platform("226"); // if local system is x86-64 PatchFile p2 = new PatchFile(aruPatch, null,null); - assertFalse(Utils.isEmptyString(p2.resolve(cacheStore))); - assertEquals("11100007_12.2.1.4.0_amd64", p2.toString()); + filePath = p2.resolve(); + assertFalse(Utils.isEmptyString(filePath)); + assertEquals("p11100007_122140_AMD64.zip", Paths.get(filePath).getFileName().toString()); } @Test @@ -458,6 +505,8 @@ void findArmPatch() throws Exception { // 11100007 has multiple patches, 1 ARM and 1 AMD String patchId = "11100007"; String version = "12.2.1.4.0"; + + List aruPatches1 = AruUtil.rest().getPatches(patchId, null, null).collect(Collectors.toList()); List aruPatches = AruUtil.rest().getPatches(patchId, null, null) .filter(p -> p.isApplicableToTarget(Architecture.ARM64.getAruPlatform())) .collect(Collectors.toList()); @@ -465,11 +514,14 @@ void findArmPatch() throws Exception { assertNotNull(aruPatch); PatchFile patchFile = new PatchFile(aruPatch, "x", "x"); - String filePath = patchFile.resolve(cacheStore); + String filePath = patchFile.resolve(); assertNotNull(filePath, "Patch resolve() failed to get file path from XML"); - String key = patchId + "_" + version + "_" + Architecture.ARM64; - String filePathFromCache = cacheStore.getValueFromCache(key); + PatchMetaData patchMetaData = ConfigManager.getInstance().getPatchForPlatform( + ARM64_BLD, patchId, version); + + String filePathFromCache = patchMetaData.getLocation(); + assertNotNull(filePathFromCache, "Could not find new patch in cache"); assertEquals(filePath, filePathFromCache, "Patch in cache does not match"); } diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/cache/AddEntryTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/cache/AddEntryTest.java deleted file mode 100644 index 0d70615a4..000000000 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/cache/AddEntryTest.java +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2019, 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 com.oracle.weblogic.imagetool.cli.cache; - -import java.io.ByteArrayOutputStream; -import java.io.PrintWriter; - -import com.oracle.weblogic.imagetool.api.model.CommandResponse; -import com.oracle.weblogic.imagetool.cli.ImageTool; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@Tag("unit") -class AddEntryTest { - - private ByteArrayOutputStream byteArrayOutputStream = null; - private PrintWriter printStream = null; - - @BeforeEach - void setup() { - byteArrayOutputStream = new ByteArrayOutputStream(); - printStream = new PrintWriter(byteArrayOutputStream); - } - - @AfterEach - void teardown() { - if (printStream != null) { - printStream.close(); - } - } - - @Test - void testMissingParameters() { - ImageTool.run(new AddEntry(), printStream, printStream); - assertTrue(new String(byteArrayOutputStream.toByteArray()).contains("Missing required options")); - } - - @Test - void testMissingKey() { - ImageTool.run(new AddEntry(), printStream, printStream, "--value", "some_value"); - assertTrue(new String(byteArrayOutputStream.toByteArray()).contains("Missing required option: '--key='")); - } - - @Test - void testMissingValue() { - ImageTool.run(new AddEntry(), printStream, printStream, "--key", "some_key"); - assertTrue(new String(byteArrayOutputStream.toByteArray()) - .contains("Missing required option: '--value='")); - } - - @Test - void testInvalidParameters() { - CommandResponse response = ImageTool.run(new AddEntry(), printStream, printStream, "--key", "", "--value", ""); - assertEquals(1, response.getStatus()); - } -} diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/cache/AddInstallerEntryTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/cache/AddInstallerEntryTest.java index ff9d21f2b..f0f2d7a95 100644 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/cache/AddInstallerEntryTest.java +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/cache/AddInstallerEntryTest.java @@ -3,9 +3,15 @@ package com.oracle.weblogic.imagetool.cli.cache; +import java.io.IOException; +import java.nio.file.Path; + import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.util.TestSetup; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import picocli.CommandLine; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -14,6 +20,12 @@ @Tag("unit") class AddInstallerEntryTest { + + @BeforeAll + static void setup(@TempDir Path tempDir) throws IOException { + TestSetup.setup(tempDir); + } + @Test void testMissingParameters() { final AddInstallerEntry addCommand = new AddInstallerEntry(); @@ -29,13 +41,13 @@ void testWrongType() { // The value for --type must be one of the pre-defined types CommandLine.ParameterException pe = assertThrows(CommandLine.ParameterException.class, () -> new CommandLine(addCommand) - .parseArgs("--type", "a2z", "--version", "some_value", "--path", "/path/to/a/file") + .parseArgs("--type", "a2z", "--version", "some_value", "-a", "amd64", "--path", "/path/to/a/file") ); assertTrue(pe.getMessage().contains("--type")); // repeat same command but use a valid type. No exception should be thrown. new CommandLine(addCommand) - .parseArgs("--type", "WLS", "--version", "some_value", "--path", "/path/to/a/file"); + .parseArgs("--type", "WLS", "--version", "some_value", "-a", "amd64", "--path", "/path/to/a/file"); } @Test @@ -54,8 +66,8 @@ void testValidParameters() { final AddInstallerEntry addCommand = new AddInstallerEntry(); // The cache key should be a string made up of the type and version seperated by an underscore new CommandLine(addCommand) - .parseArgs("--type", "WLS", "--version", "12.2.1.4", "--path", "/path/to/a/file"); - assertEquals("wls_12.2.1.4", addCommand.getKey()); + .parseArgs("--type", "WLS", "--version", "12.2.1.4", "-a", "amd64", "--path", "/path/to/a/file"); + assertEquals("wls", addCommand.getKey()); } @Test @@ -64,6 +76,6 @@ void testArchKey() { // The cache key should be a string made up of the type, version, and architecture seperated by an underscore new CommandLine(addCommand) .parseArgs("--type", "WLS", "--version", "12.2.1.4", "-a", "amd64", "--path", "/path/to/a/file"); - assertEquals("wls_12.2.1.4_amd64", addCommand.getKey()); + assertEquals("wls", addCommand.getKey()); } } diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/cache/AddPatchEntryTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/cache/AddPatchEntryTest.java index e0538b402..7594346a2 100644 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/cache/AddPatchEntryTest.java +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/cache/AddPatchEntryTest.java @@ -3,15 +3,22 @@ package com.oracle.weblogic.imagetool.cli.cache; +import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; +import java.util.List; import com.oracle.weblogic.imagetool.api.model.CommandResponse; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import com.oracle.weblogic.imagetool.util.InvalidPatchIdFormatException; import com.oracle.weblogic.imagetool.util.Utils; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import picocli.CommandLine; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -22,6 +29,27 @@ @Tag("unit") class AddPatchEntryTest { + @BeforeAll + static void setup(@TempDir Path tempDir) throws IOException { + Path settingsFileName = tempDir.resolve("settings.yaml"); + Path installerFile = tempDir.resolve("installers.yaml"); + Path patchFile = tempDir.resolve("patches.yaml"); + Files.createFile(settingsFileName); + Files.createFile(installerFile); + Files.createFile(patchFile); + + + List lines = Arrays.asList( + "installerSettingsFile: " + installerFile.toAbsolutePath().toString(), + "patchSettingsFile: " + patchFile.toAbsolutePath().toString(), + "installerDirectory: " + tempDir.toAbsolutePath().toString(), + "patchDirectory: " + tempDir.toAbsolutePath().toString() + ); + Files.write(settingsFileName, lines); + ConfigManager.getInstance(settingsFileName); + } + + private static CommandLine getCommand() { AddPatchEntry app = new AddPatchEntry(); CommandLine cmd = new CommandLine(app); @@ -45,20 +73,20 @@ void testMissingParameters() { void testInvalidFileParameter() { CommandLine cmd = getCommand(); // invalid file (file does not exist), should generate an error response - cmd.execute("--patchId=12345678_12.2.1.3.0", "--path=/here/there"); + cmd.execute("--patchId=12345678_12.2.1.3.0", "--path=/here/there", "--version=12.2.1.3.0", + "-a=amd64"); CommandResponse result = cmd.getExecutionResult(); assertNotNull(result, "Response missing from call to addPatch"); assertEquals(1, result.getStatus()); } @Test - void testInvalidPatchId() { + void testValidPatchId() { CommandLine cmd = getCommand(); - // invalid patch ID should generate an error response - cmd.execute("--patchId=12345678", "--path=pom.xml"); + cmd.execute("--patchId=12345678", "--path=pom.xml", "--version=12.2.1.3.0", "-a=amd64"); CommandResponse result = cmd.getExecutionResult(); assertNotNull(result, "Response missing from call to addPatch"); - assertEquals(1, result.getStatus()); + assertEquals(0, result.getStatus()); } @Test diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/menu/CommonPatchingOptionsTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/menu/CommonPatchingOptionsTest.java index cc2107257..00fa36abe 100644 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/menu/CommonPatchingOptionsTest.java +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/cli/menu/CommonPatchingOptionsTest.java @@ -48,7 +48,7 @@ public boolean checkCredentials(String username, String password) { @Override public List getRecommendedPatches(FmwInstallerType type, String version, Architecture architecture, - String userId, String password) { + String commonName, String userId, String password) { if (type.equals(FmwInstallerType.WLS)) { List list = new ArrayList<>(); list.add(new AruPatch().patchId("1").product("15991").description("psu") @@ -63,7 +63,7 @@ public List getRecommendedPatches(FmwInstallerType type, String versio @Override public List getLatestPsu(FmwInstallerType type, String version, Architecture architecture, - String userId, String password) { + String commonName, String userId, String password) { if (type.equals(FmwInstallerType.WLS)) { List list = new ArrayList<>(); list.add(new AruPatch().patchId("1").description("psu").psuBundle("x")); diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/installer/InstallerTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/installer/InstallerTest.java index 4e441429e..c12dcf03c 100644 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/installer/InstallerTest.java +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/installer/InstallerTest.java @@ -32,18 +32,19 @@ void fmwInstallerTypeListTest() { @Test void fmwInstallerProductIds() { - AruProduct[] list1 = {AruProduct.WLS, AruProduct.COH, AruProduct.FMWPLAT, AruProduct.JDBC, AruProduct.FIT, - AruProduct.OSS}; + AruProduct[] list1 = {AruProduct.WLS, AruProduct.FMW_GLCM, AruProduct.COH, AruProduct.FMWPLAT, AruProduct.JDBC, + AruProduct.FIT, AruProduct.OSS}; assertEquals(Utils.toSet(list1), FmwInstallerType.WLS.products(), "WLS product list is incorrect or out of order"); - AruProduct[] list2 = {AruProduct.WLS, AruProduct.COH, AruProduct.FMWPLAT, AruProduct.JDBC, AruProduct.FIT, - AruProduct.OSS, AruProduct.JRF, AruProduct.JDEV, AruProduct.OPSS, AruProduct.OWSM}; + AruProduct[] list2 = {AruProduct.WLS, AruProduct.COH, AruProduct.FMW_GLCM, AruProduct.FMWPLAT, AruProduct.JDBC, + AruProduct.FIT, AruProduct.OSS, AruProduct.JRF, AruProduct.JDEV, AruProduct.OPSS, AruProduct.OWSM}; assertEquals(Utils.toSet(list2), FmwInstallerType.FMW.products(), "FMW product list is incorrect or out of order"); - AruProduct[] list3 = {AruProduct.WLS, AruProduct.COH, AruProduct.FMWPLAT, AruProduct.JDBC, AruProduct.FIT, - AruProduct.OSS, AruProduct.JRF, AruProduct.JDEV, AruProduct.OPSS, AruProduct.OWSM, AruProduct.SOA}; + AruProduct[] list3 = {AruProduct.WLS, AruProduct.COH, AruProduct.FMW_GLCM, AruProduct.FMWPLAT, AruProduct.JDBC, + AruProduct.FIT, AruProduct.OSS, AruProduct.JRF, AruProduct.JDEV, AruProduct.OPSS, AruProduct.OWSM, + AruProduct.SOA}; assertEquals(Utils.toSet(list3), FmwInstallerType.SOA.products(), "SOA product list is incorrect or out of order"); } diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstallTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstallTest.java index 3a1d2837a..5c3c43e94 100644 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstallTest.java +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/installer/MiddlewareInstallTest.java @@ -6,12 +6,12 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.Collections; import java.util.List; import com.oracle.weblogic.imagetool.ResourceUtils; -import com.oracle.weblogic.imagetool.cachestore.CacheStore; -import com.oracle.weblogic.imagetool.cachestore.CacheStoreTestImpl; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import com.oracle.weblogic.imagetool.test.annotations.ReduceTestLogging; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; @@ -26,21 +26,41 @@ @ReduceTestLogging(loggerClass = MiddlewareInstall.class) class MiddlewareInstallTest { static Path cacheDir; - static CacheStore cacheStore; @BeforeAll static void setup(@TempDir Path cacheDir) throws IOException { MiddlewareInstallTest.cacheDir = cacheDir; - cacheStore = new CacheStoreTestImpl(cacheDir); - cacheStore.addToCache("wls_12.2.1.4.0", - ResourceUtils.resourcePath("/dummyInstallers/test-installer.zip").toString()); + + Path path12214 = ResourceUtils.resourcePath("/dummyInstallers/test-installer.zip"); + + Path settingsFileName = cacheDir.resolve("settings.yaml"); + Path installerFile = cacheDir.resolve("installers.yaml"); + Path patchFile = cacheDir.resolve("patches.yaml"); + Files.createFile(settingsFileName); + Files.createFile(installerFile); + Files.createFile(patchFile); + + List lines = Arrays.asList( + "installerSettingsFile: " + installerFile.toAbsolutePath().toString(), + "patchSettingsFile: " + patchFile.toAbsolutePath().toString(), + "installerDirectory: " + cacheDir.toAbsolutePath().toString(), + "patchDirectory: " + cacheDir.toAbsolutePath().toString() + ); + Files.write(settingsFileName, lines); + ConfigManager configManager = ConfigManager.getInstance(settingsFileName); + InstallerMetaData installer2 = new InstallerMetaData("Generic", + path12214.toString(), + "12.2.1.4.0", "12.2.1.4.0"); + + configManager.addInstaller(InstallerType.WLS, "12.2.1.4.0", installer2); } @Test void copyInstaller(@TempDir Path buildContextDir) throws IOException { // Test a simple WLS install type, and copy the files to the build context folder - MiddlewareInstall install = new MiddlewareInstall(FmwInstallerType.WLS, "12.2.1.4.0", null, null); - install.copyFiles(cacheStore, buildContextDir.toString()); + MiddlewareInstall install = new MiddlewareInstall(FmwInstallerType.WLS, "12.2.1.4.0", + null, null, "docker", null); + install.copyFiles(buildContextDir.toString()); // 2 files should be copied from cache to build context folder assertTrue(Files.isRegularFile(buildContextDir.resolve("test-installer.zip"))); assertTrue(Files.isRegularFile(buildContextDir.resolve("wls.rsp")), "Response file not found"); @@ -60,8 +80,9 @@ void customResponseFile(@TempDir Path buildContextDir) throws IOException { List customResponse = Collections.singletonList(ResourceUtils.resourcePath("/dummyInstallers/dummyResponse.txt")); // Test a simple WLS install type, and copy the files to the build context folder - MiddlewareInstall install = new MiddlewareInstall(FmwInstallerType.WLS, "12.2.1.4.0", customResponse, null); - install.copyFiles(cacheStore, buildContextDir.toString()); + MiddlewareInstall install = new MiddlewareInstall(FmwInstallerType.WLS, "12.2.1.4.0", customResponse, + null, "docker", null); + install.copyFiles(buildContextDir.toString()); // 2 files should be copied from cache to build context folder assertTrue(Files.isRegularFile(buildContextDir.resolve("test-installer.zip"))); assertTrue(Files.isRegularFile(buildContextDir.resolve("dummyResponse.txt")), "Response file not found"); diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/settings/ConfigManagerTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/settings/ConfigManagerTest.java new file mode 100644 index 000000000..f201e38ff --- /dev/null +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/settings/ConfigManagerTest.java @@ -0,0 +1,67 @@ +// 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. + +package com.oracle.weblogic.imagetool.settings; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; + +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; +import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.patch.PatchMetaData; +import com.oracle.weblogic.imagetool.util.Architecture; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +class ConfigManagerTest { + + Path settingsPath = Paths.get("src/test/resources/settings/basic_settings.yaml"); + + @Test + void testBasicInstallerSettings() { + ConfigManager cfgMgr = ConfigManager.getInstance(settingsPath); + assertNotNull(cfgMgr); + assertEquals(200, cfgMgr.getAruRetryInterval()); + assertEquals(10, cfgMgr.getAruRetryMax()); + assertEquals("12.2.1.4.0", cfgMgr.getDefaultWLSVersion()); + } + + @Test + void testGettingInstallers() { + ConfigManager cfgMgr = ConfigManager.getInstance(settingsPath); + assertNotNull(cfgMgr); + Map>> installers = cfgMgr.getInstallers(); + assertNotNull(installers); + assertEquals(4, installers.size()); + InstallerMetaData metaData = cfgMgr.getInstallerForPlatform(InstallerType.JDK, Architecture.AMD64, + "8u241"); + assertNotNull(metaData); + assertEquals("8u241", metaData.getProductVersion()); + metaData = cfgMgr.getInstallerForPlatform(InstallerType.JDK, Architecture.AMD64, + "8u301"); + assertNull(metaData); + } + + @Test + void testGettingPatches() { + ConfigManager cfgMgr = ConfigManager.getInstance(settingsPath); + assertNotNull(cfgMgr); + Map> patches = cfgMgr.getAllPatches(); + assertNotNull(patches); + assertEquals(2, patches.size()); + PatchMetaData metaData = cfgMgr.getPatchForPlatform("Generic", "37258699", + "12.2.1.4.241107"); + assertNull(metaData); + metaData = cfgMgr.getPatchForPlatform("Generic", "37258699", + "12.2.1.4.0"); + assertNotNull(metaData); + assertEquals("12.2.1.4.0", metaData.getPatchVersion()); + assertEquals("JDBC19.25 BUNDLE PATCH 12.2.1.4.241107", metaData.getDescription()); + } + +} diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/settings/UserSettingsTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/settings/UserSettingsTest.java new file mode 100644 index 000000000..86b8e30a5 --- /dev/null +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/settings/UserSettingsTest.java @@ -0,0 +1,77 @@ +// 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. + +package com.oracle.weblogic.imagetool.settings; + +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Paths; + +import com.oracle.weblogic.imagetool.cli.config.ConfigAttributeName; +import com.oracle.weblogic.imagetool.installer.InstallerType; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +@Tag("unit") +class UserSettingsTest { + + private UserSettingsFile getResourceFile(String resourcePath) { + URL resource = this.getClass().getClassLoader().getResource(resourcePath); + assertNotNull(resource, "Unable to load settings file: " + resourcePath); + try { + return new UserSettingsFile(Paths.get(resource.toURI())); + } catch (URISyntaxException e) { + throw new IllegalStateException("Yaml resource file unavailable", e); + } + } + + @Test + void testSimpleSettingsFile() { + UserSettingsFile settings = getResourceFile("settings/basic_settings.yaml"); + assertEquals("/home/user/patches", settings.getPatchDirectory()); + assertEquals("./builds", settings.getBuildContextDirectory()); + assertNull(settings.getBuildEngine()); + assertNull(settings.getContainerEngine()); + assertEquals(10,settings.getAruRetryMax()); + assertEquals(200, settings.getAruRetryInterval()); + // value not set, should return default value + assertNull(settings.getInstallerDirectory()); + } + + @Test + void testDefaultInstallers() { + UserSettingsFile settings = getResourceFile("settings/basic_settings.yaml"); + assertEquals("8u241", settings.getInstallerSettings(InstallerType.JDK).getDefaultVersion()); + assertEquals("12.2.1.4.0", settings.getInstallerSettings(InstallerType.WLS).getDefaultVersion()); + } + + @Test + void testInvalidSettings() { + UserSettingsFile settings = getResourceFile("settings/invalid_settings.yaml"); + assertEquals("/home/user/patches", settings.getPatchDirectory()); + assertNull(settings.getBuildContextDirectory()); + } + + @Test + void testOutput() { + + UserSettingsFile settings = getResourceFile("settings/basic_settings.yaml"); + assertEquals(settings.getPatchDirectory(), "/home/user/patches"); + assertEquals(settings.getBuildContextDirectory(), "./builds"); + assertEquals(settings.getPatchDirectory(), "/home/user/patches"); + + } + + @Test + void testSetters() { + UserSettingsFile settings = getResourceFile("settings/basic_settings.yaml"); + ConfigAttributeName attributeName = ConfigAttributeName.patchDirectory; + attributeName.set(settings, "./cache/paches"); + assertEquals("./builds", settings.getBuildContextDirectory()); + assertEquals("./cache/paches", settings.getPatchDirectory()); + } +} \ No newline at end of file diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/CacheConverterUtilTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/CacheConverterUtilTest.java new file mode 100644 index 000000000..5dad8e087 --- /dev/null +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/CacheConverterUtilTest.java @@ -0,0 +1,228 @@ +// 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 com.oracle.weblogic.imagetool.util; + + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.regex.Pattern; + +import com.oracle.weblogic.imagetool.installer.InstallerType; +import com.oracle.weblogic.imagetool.logging.LoggingFacade; +import com.oracle.weblogic.imagetool.logging.LoggingFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Tag("unit") +class CacheConverterUtilTest { + + static final List fileContents = Arrays.asList("A", "B", "C"); + static Path path12213; + static Path patch; + static UnitTestLogHandler logHandler = new UnitTestLogHandler(); + static Pattern installerPattern; + + @BeforeAll + static void setup(@TempDir Path tempDir) throws IOException { + path12213 = tempDir.resolve("installer.file.122130.jar"); + Files.write(path12213, fileContents); + patch = tempDir.resolve("patch.file.122130.jar"); + Files.write(patch, fileContents); + + TestSetup.setup(tempDir); + + installerPattern = Pattern.compile(CacheConverterUtil.INSTALLER_PATTERN); + } + + @BeforeEach + void setupLogger() { + LoggingFacade logger = LoggingFactory.getLogger(CacheConverterUtil.class); + + logger.getUnderlyingLogger().setUseParentHandlers(false); + logger.getUnderlyingLogger().addHandler(logHandler); + logger.setLevel(Level.ALL); + logHandler.clear(); + } + + @AfterEach + void tearDownLogger() { + LoggingFacade logger = LoggingFactory.getLogger(CacheConverterUtil.class); + logger.getUnderlyingLogger().removeHandler(logHandler); + logger.getUnderlyingLogger().setUseParentHandlers(true); + } + + @Test + void testConvertVersionString() { + assertEquals("12.2.1.4.0", CacheConverterUtil.convertVersionString("122140")); + assertEquals("14.1.1.0.0", CacheConverterUtil.convertVersionString("141100")); + assertEquals("12.2.1.4.0", CacheConverterUtil.convertVersionString("12.2.1.4.0")); + } + + @Test + void testParseInstallerValidWithoutArch() { + String line = "wls_12.2.1.3.0=" + path12213.toString(); + + CacheConverterUtil.ParsedInfo result = CacheConverterUtil.convertInstallerEntry( + installerPattern, line); + assertNotNull(result); + + assertEquals(InstallerType.WLS.toString().toLowerCase(), result.getKey().toLowerCase()); + assertEquals("12.2.1.3.0", result.getVersion()); + assertEquals(Utils.standardPlatform(Architecture.getLocalArchitecture().toString()), result.getArchitecture()); + assertEquals(path12213.toString(), result.getFilePath()); + assertEquals(LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE), result.getFileDate()); + + } + + @Test + void testParseInstallerValidWithArch() { + String line = "wls_12.2.1.3.0_amd64=" + path12213.toString(); + + CacheConverterUtil.ParsedInfo result = CacheConverterUtil.convertInstallerEntry( + installerPattern, line); + assertNotNull(result); + + assertEquals(InstallerType.WLS.toString().toLowerCase(), result.getKey().toLowerCase()); + assertEquals("12.2.1.3.0", result.getVersion()); + assertEquals("amd64", result.getArchitecture()); + assertEquals(path12213.toString(), result.getFilePath()); + assertEquals(LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE), result.getFileDate()); + + } + + @Test + void testParseInstallerWithFakeFilePath() { + + String line = "wls_12.2.1.3.0_amd64=/tmp/nosuchfile.zip"; + + CacheConverterUtil.ParsedInfo result = CacheConverterUtil.convertInstallerEntry( + installerPattern, line); + assertNull(result); + List recs = logHandler.getRecords(); + boolean found = false; + for (LogRecord rec : recs) { + if (rec.getMessage().equals("IMG-0131")) { + assertEquals("/tmp/nosuchfile.zip", rec.getParameters()[0]); + found = true; + } + } + assertTrue(found); + } + + @Test + void testParseInstallerInvalidKey() { + String line = "invalid_122140=/fake/path.jar"; + CacheConverterUtil.ParsedInfo result = CacheConverterUtil.convertInstallerEntry( + installerPattern, line); + assertNull(result); + List recs = logHandler.getRecords(); + boolean found = false; + for (LogRecord rec : recs) { + //IMG-0129=Cannot parse image tool version 1 metadata file line: unrecognized product {0} in line {1}. + if (rec.getMessage().equals("IMG-0129")) { + assertEquals(line, rec.getParameters()[1]); + found = true; + } + } + assertTrue(found); + } + + @Test + void testParseInstallerBadFormat() { + String line = "wls_12.2.1.4.0"; + CacheConverterUtil.ParsedInfo result = CacheConverterUtil.convertInstallerEntry( + installerPattern, line); + assertNull(result); + List recs = logHandler.getRecords(); + boolean found = false; + for (LogRecord rec : recs) { + //IMG-0128=Cannot parse image tool version 1 metadata file line: {0}, skipping. + if (rec.getMessage().equals("IMG-0128")) { + assertEquals(line, rec.getParameters()[0]); + found = true; + } + } + assertTrue(found); + } + + + @Test + void testParsePatchValid() { + String line = "12345678_12.2.1.4.0=" + patch.toString(); + CacheConverterUtil.ParsedInfo result = CacheConverterUtil.convertPatchEntry(line); + assertNotNull(result); + + assertEquals("12345678", result.getKey()); + assertEquals("12.2.1.4.0", result.getVersion()); + assertEquals(patch.toString(), result.getFilePath()); + assertEquals(LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE), result.getFileDate()); + assertEquals(Utils.standardPlatform(Architecture.getLocalArchitecture().toString()), + result.getArchitecture()); + } + + @Test + void testParsePatchWithShortVersionAndArch() { + String line = "12345678_122140_amd64=" + patch.toString(); + + CacheConverterUtil.ParsedInfo result = CacheConverterUtil.convertPatchEntry(line); + assertNotNull(result); + + assertEquals("12345678", result.getKey()); + assertEquals("12.2.1.4.0", result.getVersion()); + assertEquals(patch.toString(), result.getFilePath()); + assertEquals(LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE), result.getFileDate()); + assertEquals("amd64", result.getArchitecture()); + } + + @Test + void testParsePatchBadFormat() { + String line = "12345678"; + CacheConverterUtil.ParsedInfo result = CacheConverterUtil.convertPatchEntry(line); + assertNull(result); + List recs = logHandler.getRecords(); + boolean found = false; + for (LogRecord rec : recs) { + //IMG-0128=Cannot parse image tool version 1 metadata file line: {0}, skipping. + if (rec.getMessage().equals("IMG-0128")) { + assertEquals(line, rec.getParameters()[0]); + found = true; + } + } + assertTrue(found); + } + + + @Test + void testParsePatchFileNotExist() { + String line = "12345678_12.2.1.4.0=/nonexistent.zip"; + CacheConverterUtil.ParsedInfo result = CacheConverterUtil.convertPatchEntry(line); + assertNull(result); + List recs = logHandler.getRecords(); + boolean found = false; + for (LogRecord rec : recs) { + if (rec.getMessage().equals("IMG-0131")) { + assertEquals("/nonexistent.zip", rec.getParameters()[0]); + found = true; + } + } + assertTrue(found); + } + +} diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/DockerfileBuilderTest.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/DockerfileBuilderTest.java index df15df3cf..e15d1a9e6 100644 --- a/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/DockerfileBuilderTest.java +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/DockerfileBuilderTest.java @@ -6,17 +6,26 @@ import java.io.File; import java.io.IOException; import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; +import java.util.List; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; +import com.oracle.weblogic.imagetool.ResourceUtils; import com.oracle.weblogic.imagetool.cli.menu.PackageManagerType; import com.oracle.weblogic.imagetool.installer.FmwInstallerType; +import com.oracle.weblogic.imagetool.installer.InstallerMetaData; +import com.oracle.weblogic.imagetool.installer.InstallerType; import com.oracle.weblogic.imagetool.installer.MiddlewareInstall; +import com.oracle.weblogic.imagetool.settings.ConfigManager; import com.oracle.weblogic.imagetool.test.annotations.ReduceTestLogging; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -24,13 +33,41 @@ @Tag("unit") class DockerfileBuilderTest { + @BeforeAll + static void setup(@TempDir Path cacheDir) throws IOException { + + Path path12214 = ResourceUtils.resourcePath("/dummyInstallers/test-installer.zip"); + + Path settingsFileName = cacheDir.resolve("settings.yaml"); + Path installerFile = cacheDir.resolve("installers.yaml"); + Path patchFile = cacheDir.resolve("patches.yaml"); + Files.createFile(settingsFileName); + Files.createFile(installerFile); + Files.createFile(patchFile); + + List lines = Arrays.asList( + "installerSettingsFile: " + installerFile.toAbsolutePath().toString(), + "patchSettingsFile: " + patchFile.toAbsolutePath().toString(), + "installerDirectory: " + cacheDir.toAbsolutePath().toString(), + "patchDirectory: " + cacheDir.toAbsolutePath().toString() + ); + Files.write(settingsFileName, lines); + ConfigManager configManager = ConfigManager.getInstance(settingsFileName); + InstallerMetaData installer2 = new InstallerMetaData("Generic", + path12214.toString(), + "12.2.1.4.0", "12.2.1.4.0"); + + configManager.addInstaller(InstallerType.WLS, "12.2.1.4.0", installer2); + } + /** * Catch mismatched start/end tags and missing mustache braces. * @throws IOException if file read fails for mustache file. */ @Test void validateMustacheAliases() throws IOException { - MiddlewareInstall install = new MiddlewareInstall(FmwInstallerType.WLS, "12.2.1.3", null, null); + MiddlewareInstall install = new MiddlewareInstall(FmwInstallerType.WLS, "12.2.1.4.0", + null, null, "docker", null); DockerfileOptions dockerfileOptions = new DockerfileOptions("123") .setPatchingEnabled() diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/TestSetup.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/TestSetup.java new file mode 100644 index 000000000..45ff1401d --- /dev/null +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/TestSetup.java @@ -0,0 +1,35 @@ +// 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 com.oracle.weblogic.imagetool.util; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +import com.oracle.weblogic.imagetool.settings.ConfigManager; +import org.junit.jupiter.api.io.TempDir; + +public class TestSetup { + public static void setup(@TempDir Path tempDir) throws IOException { + Path settingsFileName = tempDir.resolve("settings.yaml"); + Path installerFile = tempDir.resolve("installers.yaml"); + Path patchFile = tempDir.resolve("patches.yaml"); + Files.createFile(settingsFileName); + Files.createFile(installerFile); + Files.createFile(patchFile); + + + List lines = Arrays.asList( + "installerSettingsFile: " + installerFile.toAbsolutePath().toString(), + "patchSettingsFile: " + patchFile.toAbsolutePath().toString(), + "installerDirectory: " + tempDir.toAbsolutePath().toString(), + "patchDirectory: " + tempDir.toAbsolutePath().toString() + ); + Files.write(settingsFileName, lines); + ConfigManager.getInstance(settingsFileName); + + } +} diff --git a/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/UnitTestLogHandler.java b/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/UnitTestLogHandler.java new file mode 100644 index 000000000..37673c2b4 --- /dev/null +++ b/imagetool/src/test/java/com/oracle/weblogic/imagetool/util/UnitTestLogHandler.java @@ -0,0 +1,35 @@ +// 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 com.oracle.weblogic.imagetool.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Handler; +import java.util.logging.LogRecord; + +public class UnitTestLogHandler extends Handler { + private final List records = new ArrayList<>(); + + @Override + public void publish(LogRecord rec) { + records.add(rec); + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + } + + public List getRecords() { + return records; + } + + public void clear() { + records.clear(); + } +} + diff --git a/imagetool/src/test/resources/patches/patch-28186730.xml b/imagetool/src/test/resources/patches/patch-28186730.xml index 57a040ab1..9d51fed01 100644 --- a/imagetool/src/test/resources/patches/patch-28186730.xml +++ b/imagetool/src/test/resources/patches/patch-28186730.xml @@ -45,7 +45,7 @@ p28186730_139425_Generic.zip 45855234 - 8BFC99E439DA1D1663F88D6859EC19E9926A473C96B95889ABE5F45C055E1BB3 + 706204F15CE1834AD298C8E8D270315652BBD6E40CEC489F65802DB2FDD03167 41E3536305E6A71776717D700FA41B1A1AB0A493 @@ -99,7 +99,7 @@ 45731967 1C03A8D8C54236E52FB1F4B6C49C9D161DA17D84 - 0CB78A061B47B3466656BBB08EF850EF1206ECF4A350BD7E116D77E439081664 + 706204F15CE1834AD298C8E8D270315652BBD6E40CEC489F65802DB2FDD03167 2020-07-11 09:14:22 @@ -145,7 +145,7 @@ p28186730_139420_Generic.zip 46242780 - 514233AA5C7B392798B787416F75B339E3C0FA60430C5D962DC30C7BAB68A6F3 + 706204F15CE1834AD298C8E8D270315652BBD6E40CEC489F65802DB2FDD03167 8D0F09675D4D7531BDB691D9DCAB5DA4387E5E8C @@ -192,7 +192,7 @@ p28186730_139422_Generic.zip 46620588 - 6167A23A45A89D48A7F638AD6E5919E24BB9202D2D821216D7E41AF3ACBA0376 + 706204F15CE1834AD298C8E8D270315652BBD6E40CEC489F65802DB2FDD03167 724F228313383CCD84885CB057A5E2B5E80E7132 @@ -240,7 +240,7 @@ 46518865 F7D2E3C781A6E73B3359B9246253CDBA956F29B6 - E092400E722E52627E143B6D4CB055C49815D4C987642C183DCE9F2DAFD82B2A + 706204F15CE1834AD298C8E8D270315652BBD6E40CEC489F65802DB2FDD03167 2020-01-21 07:36:59 diff --git a/imagetool/src/test/resources/patches/patch-2818673x.xml b/imagetool/src/test/resources/patches/patch-2818673x.xml index 695483759..be5b1937b 100644 --- a/imagetool/src/test/resources/patches/patch-2818673x.xml +++ b/imagetool/src/test/resources/patches/patch-2818673x.xml @@ -44,7 +44,7 @@ p28186730_139425_Generic.zip 45855234 - 8BFC99E439DA1D1663F88D6859EC19E9926A473C96B95889ABE5F45C055E1BB3 + 706204F15CE1834AD298C8E8D270315652BBD6E40CEC489F65802DB2FDD03167 41E3536305E6A71776717D700FA41B1A1AB0A493 @@ -90,7 +90,7 @@ p28186730_139425_Generic.zip 45855234 - 8BFC99E439DA1D1663F88D6859EC19E9926A473C96B95889ABE5F45C055E1BB3 + 706204F15CE1834AD298C8E8D270315652BBD6E40CEC489F65802DB2FDD03167 41E3536305E6A71776717D700FA41B1A1AB0A493 diff --git a/imagetool/src/test/resources/settings/basic_settings.yaml b/imagetool/src/test/resources/settings/basic_settings.yaml new file mode 100644 index 000000000..f717c8a06 --- /dev/null +++ b/imagetool/src/test/resources/settings/basic_settings.yaml @@ -0,0 +1,11 @@ +aruRetryInterval: 200 +aruRetryMax: 10 +buildContextDirectory: ./builds +patchDirectory: /home/user/patches +installerSettings: + JDK: + defaultVersion: 8u241 + WLS: + defaultVersion: 12.2.1.4.0 + WDT: + defaultVersion: latest diff --git a/imagetool/src/test/resources/settings/installers.yaml b/imagetool/src/test/resources/settings/installers.yaml new file mode 100644 index 000000000..59b7f01ea --- /dev/null +++ b/imagetool/src/test/resources/settings/installers.yaml @@ -0,0 +1,31 @@ +JDK: + 17u11: + - {architecture: linux/arm64, dateAdded: '2025-03-20', digest: 7F1E921F932BD7E372CBBD277807BBBA774022558F2AA979FAFCDCED5D24619F, + location: /home/acme/Downloads/jdk-17_linux-aarch64_11bin.tar.gz, productVersion: 17u11} + 8u401: + - {architecture: linux/amd64, dateAdded: '2025-05-01', digest: 19684FCCD7FF32A8400E952A643F0049449A772EF63B8037D5B917CBD137D173, + location: /home/acme/Downloads/jdk-8u401-linux-x64.tar.gz, productVersion: 8u401} + - {architecture: linux/arm64, dateAdded: '2025-05-02', digest: 811AF9AA1CE2EAA902C7923EA1A5B7012BDDB787DF0805ADED5F3663B210AA47, + location: /home/acme/Downloads/jdk-8u401-fcs-bin-b10-linux-aarch64-19_dec_2023.tar.gz, + productVersion: 8u401} + 8u241: + - {architecture: linux/amd64, dateAdded: '2025-05-01', digest: 19784FCCD7FF32A8400E952A643F0049449A772EF63B8037D5B917CBD137D173, + location: /home/acme/Downloads/jdk-8u241-linux-x64.tar.gz, productVersion: 8u241} +FMW: + 14.1.2.0.0: + - {architecture: Generic, dateAdded: '2025-03-20', digest: 8D3125A358F5429B7EABE7AB28739A6053C4439DFD54EC351586875A4DE68BB0, + location: /home/acme/Downloads/V1045350-01.zip, productVersion: 14.1.2.0.0} +WDT: + latest: + - {architecture: Generic, dateAdded: '2025-04-18', digest: 88DA46CE489A5FB506F9DE45D8E9D242810FE3ED270AFB11EE67760729F7BBEE, + location: /home/acme/dev/oracle/weblogic-deploy-tooling/installer/target/weblogic-deploy.zip, + productVersion: latest} +WLS: + 12.2.1.4.0: + - {architecture: linux/arm64, dateAdded: '2025-03-20', digest: 2630E4E3D6C8998DA8AA97FF6FF4A4A44F95A568DE8CF9DE01DCD47B753FF324, + location: /home/acme/Downloads/fmw_12.2.1.4.0_wls_generic_ARM_OCI.zip, productVersion: 12.2.1.4.0} + - {architecture: linux/amd64, dateAdded: '2025-05-01', digest: 4B3A2264875CE4D56CF6C4C70FC2E5895DB1F5CBC39EB5D4E28E46BFA65D2671, + location: /home/acme/Downloads/fmw_12.2.1.4.0_wls_lite_Disk1_1of1.zip, productVersion: 12.2.1.4.0} + 14.1.2.0.0: + - {architecture: linux/arm64, dateAdded: '2025-04-24', digest: E0323E52FF26787E8397F813ABD44E1EB73814E9174146E852889127DB04F655, + location: /home/acme/Downloads/fmw_14.1.2.0.0_wls_generic._Disk1_1of1.zip, productVersion: 14.1.2.0.0} diff --git a/imagetool/src/test/resources/settings/invalid_settings.yaml b/imagetool/src/test/resources/settings/invalid_settings.yaml new file mode 100644 index 000000000..ba81dffd7 --- /dev/null +++ b/imagetool/src/test/resources/settings/invalid_settings.yaml @@ -0,0 +1,3 @@ +patchDirectory: /home/user/patches +imageBuildDirectory: 1 +someField: someValue diff --git a/imagetool/src/test/resources/settings/patches.yaml b/imagetool/src/test/resources/settings/patches.yaml new file mode 100644 index 000000000..c4f31679d --- /dev/null +++ b/imagetool/src/test/resources/settings/patches.yaml @@ -0,0 +1,10 @@ +'37297691': + - {architecture: linux/arm64, dateAdded: '2024-12-23', description: OSS 19C BUNDLE + PATCH 12.2.1.4.241119, digest: 2F81E3FAF5832234B57BA845F2E869585E35B742996BA90F377AFB1D888C9D0E, + location: /home/acme/.imagetool/downloaded_patches/p37297691_122140_Linux-ARM-64.zip, + patchVersion: 12.2.1.4.0} +'37258699': + - {architecture: Generic, dateAdded: '2024-11-18', description: JDBC19.25 BUNDLE PATCH + 12.2.1.4.241107, digest: 5B40AEB1D642CB560AE9FB335802A4D9382B78D1875AD2A5FB19D4C1E0FA148E, + location: /home/acme/.imagetool/downloaded_patches/p37258699_122140_Generic.zip, + patchVersion: 12.2.1.4.0} diff --git a/imagetool/src/test/resources/settings/settings.yaml b/imagetool/src/test/resources/settings/settings.yaml new file mode 100644 index 000000000..426dd3c3e --- /dev/null +++ b/imagetool/src/test/resources/settings/settings.yaml @@ -0,0 +1,11 @@ +installerDirectory: settings/installers +patchDirectory: settings/oraclePatches +installerSettingsFile: src/test/resources/settings/installers.yaml +patchSettingsFile: src/test/resources/settings/patches.yaml +installers: + jdk: + defaultVersion: 8u401 + wls: + defaultVersion: 12.2.1.4.0 + wdt: + defaultVersion: latest diff --git a/installer/pom.xml b/installer/pom.xml index 3ab4bc7f7..c784ad761 100644 --- a/installer/pom.xml +++ b/installer/pom.xml @@ -35,6 +35,10 @@ com.github.spullara.mustache.java compiler + + org.yaml + snakeyaml + diff --git a/pom.xml b/pom.xml index aedf87f7f..3f418eefd 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,11 @@ annotations 24.1.0 + + org.yaml + snakeyaml + 1.30 + diff --git a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/ITImagetool.java b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/ITImagetool.java index 4543ba56d..4ff11e5be 100644 --- a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/ITImagetool.java +++ b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/ITImagetool.java @@ -13,9 +13,12 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; import com.oracle.weblogic.imagetool.cli.menu.KubernetesTarget; +import com.oracle.weblogic.imagetool.installer.InstallerType; import com.oracle.weblogic.imagetool.logging.LoggingFacade; import com.oracle.weblogic.imagetool.logging.LoggingFactory; import com.oracle.weblogic.imagetool.tests.annotations.IntegrationTest; @@ -24,7 +27,6 @@ import com.oracle.weblogic.imagetool.tests.utils.CommandResult; import com.oracle.weblogic.imagetool.tests.utils.CreateAuxCommand; import com.oracle.weblogic.imagetool.tests.utils.CreateCommand; -import com.oracle.weblogic.imagetool.tests.utils.RebaseCommand; import com.oracle.weblogic.imagetool.tests.utils.Runner; import com.oracle.weblogic.imagetool.tests.utils.UpdateCommand; import com.oracle.weblogic.imagetool.util.Utils; @@ -38,6 +40,7 @@ import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.TestMethodOrder; +import static com.oracle.weblogic.imagetool.cachestore.CacheStore.CACHE_DIR_ENV; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -54,6 +57,11 @@ class ITImagetool { private static final String STAGING_DIR = System.getProperty("STAGING_DIR"); private static final String BLDDIR_ENV = System.getProperty("WLSIMG_BLDDIR"); private static final String CACHEDIR_ENV = System.getProperty("WLSIMG_CACHEDIR"); + private static final String AMD64 = "amd64"; + private static final String ARM64 = "arm64"; + private static final String GENERIC = "Generic"; + private static final String PLATFORM_AMD64 = "linux/amd64"; + private static final String PLATFORM_ARN64 = "linux/arm64"; // Docker images private static String DB_IMAGE = System.getProperty("DB_IMAGE"); @@ -64,7 +72,7 @@ class ITImagetool { private static final String JDK_INSTALLER_NEWER = "jdk-8u231-linux-x64.tar.gz"; private static final String WLS_INSTALLER = "fmw_12.2.1.3.0_wls_Disk1_1of1.zip"; private static final String P27342434_INSTALLER = "p27342434_122130_Generic.zip"; - private static final String P28186730_INSTALLER = "p28186730_139422_Generic.zip"; + private static final String P28186730_INSTALLER = "p28186730_1394217_Generic.zip"; private static final String WDT_INSTALLER = "weblogic-deploy.zip"; private static final String FMW_INSTALLER = "fmw_12.2.1.3.0_infrastructure_Disk1_1of1.zip"; @@ -72,7 +80,8 @@ class ITImagetool { private static final String P27342434_ID = "27342434"; private static final String P28186730_ID = "28186730"; private static final String WLS_VERSION = "12.2.1.3.0"; - private static final String OPATCH_VERSION = "13.9.4.2.2"; + private static final String CUSTOM_WLS_VERSION = "custom122130"; + private static final String OPATCH_VERSION = "13.9.4.2.17"; private static final String JDK_VERSION = "8u202"; private static final String JDK_VERSION_212 = "8u212"; private static final String WDT_VERSION = "1.1.2"; @@ -202,8 +211,41 @@ private static void cleanup() throws Exception { static void staticPrepare() throws Exception { logger.info("prepare for image tool test ..."); // verify that all the prerequisites are set and exist + + validateEnvironmentSettings(); // clean up Docker instances leftover from a previous run + String baseDir = System.getProperty(CACHE_DIR_ENV); + + Path tempDir = Paths.get(baseDir); + if (Files.exists(tempDir)) { + Files.walk(tempDir) + .sorted(Comparator.reverseOrder()) + .forEach(path -> { + try { + Files.delete(path); + } catch (IOException ignore) { + ignore.printStackTrace(); + } + }); + } + Files.createDirectories(tempDir); + Path settingsFileName = tempDir.resolve("settings.yaml"); + Path installerFile = tempDir.resolve("installers.yaml"); + Path patchFile = tempDir.resolve("patches.yaml"); + Files.createFile(settingsFileName); + Files.createFile(installerFile); + Files.createFile(patchFile); + + List lines = Arrays.asList( + "installerSettingsFile: " + installerFile.toAbsolutePath().toString(), + "patchSettingsFile: " + patchFile.toAbsolutePath().toString(), + "installerDirectory: " + tempDir.toAbsolutePath().toString(), + "patchDirectory: " + tempDir.toAbsolutePath().toString() + ); + Files.write(settingsFileName, lines); + logger.info("Test settings file initialized : " + settingsFileName); + cleanup(); logger.info("Setting up the test environment ..."); @@ -215,6 +257,8 @@ static void staticPrepare() throws Exception { } } + setupDockerMultiPlatform(); + // verify that required files/installers are available verifyStagedFiles(JDK_INSTALLER, WLS_INSTALLER, WDT_INSTALLER, P27342434_INSTALLER, P28186730_INSTALLER, FMW_INSTALLER, JDK_INSTALLER_NEWER); @@ -306,6 +350,16 @@ private void verifyFileInImage(String imagename, String filename, String expecte } } + private static void setupDockerMultiPlatform() throws Exception { + logger.info("setup docker multiplatform build"); + String command = "docker run --privileged --rm tonistiigi/binfmt --install all"; + logger.info("executing command: " + command); + CommandResult result = Runner.run(command); + if (!result.stdout().contains("emulators")) { + throw new Exception("Failed to run command: " + command); + } + } + private void createDBContainer() throws Exception { logger.info("Creating an Oracle db docker container ..."); String command = "docker rm -f " + dbContainerName; @@ -337,6 +391,7 @@ void cacheAddInstallerJdk(TestInfo testInfo) throws Exception { .type("jdk") .version(JDK_VERSION) .path(jdkPath) + .architecture(AMD64) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { @@ -345,12 +400,14 @@ void cacheAddInstallerJdk(TestInfo testInfo) throws Exception { assertEquals(0, result.exitValue(), "for command: " + command); // verify the result - String listCommand = new CacheCommand().listItems(true).build(); + String listCommand = new CacheCommand().listInstallers(true).type("jdk") + .commonName(JDK_VERSION).build(); CommandResult listResult = Runner.run(listCommand, out, logger); // the process return code for listItems should be 0 assertEquals(0, listResult.exitValue(), "for command: " + listCommand); // output should show newly added JDK installer - assertTrue(listResult.stdout().contains("jdk_" + JDK_VERSION + "=" + jdkPath)); + assertTrue(listResult.stdout().contains(JDK_VERSION + ":")); + assertTrue(listResult.stdout().contains(jdkPath.toString())); } } @@ -371,6 +428,7 @@ void cacheAddInstallerWls(TestInfo testInfo) throws Exception { .type("wls") .version(WLS_VERSION) .path(wlsPath) + .architecture(AMD64) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { @@ -379,12 +437,14 @@ void cacheAddInstallerWls(TestInfo testInfo) throws Exception { assertEquals(0, result.exitValue(), "for command: " + command); // verify the result - String listCommand = new CacheCommand().listItems(true).build(); + String listCommand = new CacheCommand().listInstallers(true).type("wls") + .commonName(WLS_VERSION).version(WLS_VERSION).build(); CommandResult listResult = Runner.run(listCommand, out, logger); // the process return code for listItems should be 0 assertEquals(0, listResult.exitValue(), "for command: " + listCommand); // output should show newly added WLS installer - assertTrue(listResult.stdout().contains("wls_" + WLS_VERSION + "=" + wlsPath)); + assertTrue(listResult.stdout().contains(wlsPath.toString())); + assertTrue(listResult.stdout().contains(WLS_VERSION + ":")); } } @@ -403,7 +463,9 @@ void cacheAddPatch(TestInfo testInfo) throws Exception { String command = new CacheCommand() .addPatch(true) .path(patchPath) - .patchId(P27342434_ID, WLS_VERSION) + .patchId(P27342434_ID) + .version(WLS_VERSION) + .architecture(GENERIC) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { @@ -412,74 +474,121 @@ void cacheAddPatch(TestInfo testInfo) throws Exception { assertEquals(0, result.exitValue(), "for command: " + command); // verify the result - String listCommand = new CacheCommand().listItems(true).build(); + String listCommand = new CacheCommand().listPatches(true).patchId(P27342434_ID).build(); CommandResult listResult = Runner.run(listCommand, out, logger); // the process return code for listItems should be 0 assertEquals(0, listResult.exitValue(), "for command: " + listCommand); // output should show newly added patch - assertTrue(listResult.stdout().contains(P27342434_ID + "_" + WLS_VERSION + "=" + patchPath)); + assertTrue(listResult.stdout().contains(P27342434_ID)); + assertTrue(listResult.stdout().contains(WLS_VERSION)); + assertTrue(listResult.stdout().contains(patchPath.toString())); } } /** - * add an entry to the cache. + * delete a patch from the cache. * * @throws Exception - if any error occurs */ @Test @Order(4) @Tag("gate") - @DisplayName("Add manual entry to cache") - void cacheAddTestEntry(TestInfo testInfo) throws Exception { - Path testEntryValue = Paths.get(STAGING_DIR, P27342434_INSTALLER); + @DisplayName("Delete a patch from cache") + void deletePatchTest(TestInfo testInfo) throws Exception { + Path patchPath = Paths.get(STAGING_DIR, P27342434_INSTALLER); + + String testPatchID = "27342430"; String command = new CacheCommand() - .addEntry(true) - .key(TEST_ENTRY_KEY) - .value(testEntryValue) + .addPatch(true) + .path(patchPath) + .patchId(testPatchID) + .version(WLS_VERSION) + .architecture(GENERIC) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { - CommandResult addEntryResult = Runner.run(command, out, logger); - assertEquals(0, addEntryResult.exitValue(), "for command: " + command); + CommandResult result = Runner.run(command, out, logger); + // the process return code for addPatch should be 0 + assertEquals(0, result.exitValue(), "for command: " + command); // verify the result - String listCommand = new CacheCommand().listItems(true).build(); + String listCommand = new CacheCommand().listPatches(true).patchId(testPatchID).build(); CommandResult listResult = Runner.run(listCommand, out, logger); // the process return code for listItems should be 0 assertEquals(0, listResult.exitValue(), "for command: " + listCommand); // output should show newly added patch - assertTrue(listResult.stdout().contains(TEST_ENTRY_KEY.toLowerCase() + "=" + testEntryValue)); - // cache should also contain the installer that was added in the previous test (persistent cache) - assertTrue(listResult.stdout().contains(P27342434_ID + "_" + WLS_VERSION + "=")); + assertTrue(listResult.stdout().contains(testPatchID)); + assertTrue(listResult.stdout().contains(WLS_VERSION)); + assertTrue(listResult.stdout().contains(patchPath.toString())); + } + + command = new CacheCommand() + .deletePatch(true) + .patchId(testPatchID) + .version(WLS_VERSION) + .architecture(GENERIC) + .build(); + + try (PrintWriter out = getTestMethodWriter(testInfo)) { + CommandResult result = Runner.run(command, out, logger); + assertEquals(0, result.exitValue(), "for command: " + command); + + // verify the result + String listCommand = new CacheCommand().listPatches(true).patchId(testPatchID).build(); + CommandResult listResult = Runner.run(listCommand, out, logger); + // the process return code for listItems should be 0 + assertEquals(0, listResult.exitValue(), "for command: " + listCommand); + // output should show newly added patch + String errorMessage = Utils.getMessage("IMG-0160", testPatchID); + assertTrue(listResult.stdout().contains(errorMessage)); } } /** - * test delete an entry from the cache. + * test delete an installer. * * @throws Exception - if any error occurs */ @Test @Order(5) @Tag("gate") - @DisplayName("Delete cache entry") - void cacheDeleteTestEntry(TestInfo testInfo) throws Exception { - String command = new CacheCommand() - .deleteEntry(true) - .key(TEST_ENTRY_KEY) + @DisplayName("Delete installer") + void deleteInstaller(TestInfo testInfo) throws Exception { + + String wdtVersion = "testonly"; + Path wdtPath = Paths.get(STAGING_DIR, WDT_INSTALLER); + String addCommand = new CacheCommand() + .addInstaller(true) + .type("WDT") + .version(wdtVersion) + .path(wdtPath) + .architecture(GENERIC) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { - CommandResult result = Runner.run(command, out, logger); - assertEquals(0, result.exitValue(), "for command: " + command); + CommandResult addResult = Runner.run(addCommand, out, logger); + // the process return code for addInstaller should be 0 + assertEquals(0, addResult.exitValue(), "for command: " + addCommand); + } + + String deleteCommand = new CacheCommand() + .deleteInstaller(true) + .architecture(GENERIC) + .type("WDT") + .version(wdtVersion) + .build(); + + try (PrintWriter out = getTestMethodWriter(testInfo)) { + CommandResult result = Runner.run(deleteCommand, out, logger); + assertEquals(0, result.exitValue(), "for command: " + deleteCommand); // verify the result - String listCommand = new CacheCommand().listItems(true).build(); + String listCommand = new CacheCommand().listInstallers(true).type("WDT").build(); CommandResult listResult = Runner.run(listCommand, out, logger); // the process return code for listItems should be 0 assertEquals(0, listResult.exitValue(), "for command: " + listCommand); - // output should NOT show deleted patch - assertFalse(listResult.stdout().contains(TEST_ENTRY_KEY.toLowerCase())); + // output should NOT show deleted installer + assertFalse(listResult.stdout().contains(wdtVersion.toLowerCase())); } } @@ -498,7 +607,9 @@ void cacheOpatch(TestInfo testInfo) throws Exception { String command = new CacheCommand() .addPatch(true) .path(patchPath) - .patchId(P28186730_ID, OPATCH_VERSION) + .architecture(GENERIC) + .patchId(P28186730_ID) + .version(OPATCH_VERSION) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { @@ -507,12 +618,15 @@ void cacheOpatch(TestInfo testInfo) throws Exception { assertEquals(0, result.exitValue(), "for command: " + command); // verify the result - String listCommand = new CacheCommand().listItems(true).build(); + String listCommand = new CacheCommand().listPatches(true).patchId(P28186730_ID) + .version(OPATCH_VERSION).build(); CommandResult listResult = Runner.run(listCommand, out, logger); // the process return code for listItems should be 0 assertEquals(0, listResult.exitValue(), "for command: " + listCommand); // output should show newly added patch - assertTrue(listResult.stdout().contains(P28186730_ID + "_" + OPATCH_VERSION + "=" + patchPath)); + assertTrue(listResult.stdout().contains(P28186730_ID + ":")); + assertTrue(listResult.stdout().contains(OPATCH_VERSION)); + assertTrue(listResult.stdout().contains(patchPath.toString())); } } @@ -534,6 +648,7 @@ void cacheAddInstallerWdt(TestInfo testInfo) throws IOException, InterruptedExce .type("wdt") .version(WDT_VERSION) .path(wdtPath) + .architecture(GENERIC) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { @@ -555,6 +670,7 @@ void cacheAddInstallerFmw(TestInfo testInfo) throws Exception { .type("fmw") .version(WLS_VERSION) .path(Paths.get(STAGING_DIR, FMW_INSTALLER)) + .architecture(AMD64) .build(); @@ -564,12 +680,15 @@ void cacheAddInstallerFmw(TestInfo testInfo) throws Exception { assertEquals(0, addResult.exitValue(), "for command: " + addCommand); // verify the result - String listCommand = new CacheCommand().listItems(true).build(); + String listCommand = new CacheCommand().listInstallers(true).type("fmw") + .commonName(WLS_VERSION).build(); CommandResult listResult = Runner.run(listCommand, out, logger); // the process return code for listItems should be 0 assertEquals(0, listResult.exitValue(), "for command: " + listCommand); // output should show newly added WLS installer - assertTrue(listResult.stdout().contains("fmw_" + WLS_VERSION + "=")); + assertTrue(listResult.stdout().contains(Paths.get(STAGING_DIR, FMW_INSTALLER).toString())); + assertTrue(listResult.stdout().contains(WLS_VERSION + ":")); + } } @@ -584,7 +703,7 @@ void cacheAddInstallerFmw(TestInfo testInfo) throws Exception { @DisplayName("Create default WebLogic Server image") void createWlsImg(TestInfo testInfo) throws Exception { String tagName = build_tag + ":" + getMethodName(testInfo); - String command = new CreateCommand().tag(tagName).build(); + String command = new CreateCommand().platform(PLATFORM_AMD64).tag(tagName).build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { CommandResult result = Runner.run(command, out, logger); @@ -613,6 +732,7 @@ void updateWlsImg(TestInfo testInfo) throws Exception { String command = new UpdateCommand() .fromImage(build_tag + ":createWlsImg") .tag(tagName) + .platform(PLATFORM_AMD64) .patches(P27342434_ID) .build(); @@ -650,6 +770,7 @@ void createWlsImgUsingWdt(TestInfo testInfo) throws Exception { .wdtArchive(WDT_ARCHIVE) .wdtDomainHome("/u01/domains/simple_domain") .wdtVariables(WDT_VARIABLES) + .platform(PLATFORM_AMD64) .build(); CommandResult result = Runner.run(command, out, logger); @@ -663,30 +784,31 @@ void createWlsImgUsingWdt(TestInfo testInfo) throws Exception { } /** - * Use the Rebase function to move a domain to a new image. + * Create image with non existent installer. * * @throws Exception - if any error occurs */ @Test @Order(13) @Tag("gate") - @DisplayName("Rebase the WLS domain") - void rebaseWlsImg(TestInfo testInfo) throws Exception { - assumeTrue(wlsImgBuilt); - assumeTrue(domainImgBuilt); - String tagName = build_tag + ":" + getMethodName(testInfo); - String command = new RebaseCommand() - .sourceImage(build_tag, "createWlsImgUsingWdt") - .targetImage(build_tag, "updateWlsImg") - .tag(tagName) - .build(); + @DisplayName("Creat the WLS domain with non existent installer") + void createImagewithWrongVersion(TestInfo testInfo) throws Exception { try (PrintWriter out = getTestMethodWriter(testInfo)) { - CommandResult result = Runner.run(command, out, logger); - assertEquals(0, result.exitValue(), "for command: " + command); - // verify the docker image is created - assertTrue(imageExists(tagName), "Image was not created: " + tagName); + String tagName = build_tag + ":" + getMethodName(testInfo); + // create a WLS image with a domain + String command = new CreateCommand() + .tag(tagName) + .version("NOSUCHTHING") + .platform(PLATFORM_AMD64) + .build(); + + CommandResult result = Runner.run(command, out, logger); + assertEquals(1, result.exitValue(), "for command: " + command); + String errorMessage = Utils.getMessage("IMG-0145", InstallerType.WLS.toString(), + PLATFORM_AMD64, "NOSUCHTHING"); + assertTrue(result.stdout().contains(errorMessage)); } } @@ -721,6 +843,7 @@ void createMiiOl8slim(TestInfo testInfo) throws Exception { .wdtArchive(WDT_ARCHIVE) .wdtModel(tmpWdtModel) .wdtModelOnly(true) + .platform(PLATFORM_AMD64) .type("wls") .build(); @@ -756,6 +879,7 @@ void createAuxImage(TestInfo testInfo) throws Exception { .wdtModel(WDT_MODEL) .wdtArchive(WDT_ARCHIVE) .wdtVersion(WDT_VERSION) + .platform(PLATFORM_AMD64) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { @@ -773,6 +897,72 @@ void createAuxImage(TestInfo testInfo) throws Exception { } } + /** + * Test caching of an installer of type WLS. + * + * @throws Exception - if any error occurs + */ + @Test + @Order(16) + @Tag("gate") + @Tag("cache") + @DisplayName("Add WLS installer to cache using custom name") + void cacheAddInstallerWlsUsingCommonName(TestInfo testInfo) throws Exception { + Path wlsPath = Paths.get(STAGING_DIR, WLS_INSTALLER); + String command = new CacheCommand() + .addInstaller(true) + .type("wls") + .version(WLS_VERSION) + .commonName(CUSTOM_WLS_VERSION) + .path(wlsPath) + .architecture(AMD64) + .build(); + + try (PrintWriter out = getTestMethodWriter(testInfo)) { + CommandResult result = Runner.run(command, out, logger); + // the process return code for addInstaller should be 0 + System.out.println("Result: " + result.stdout()); + assertEquals(0, result.exitValue(), "for command: " + command); + // verify the result + String listCommand = new CacheCommand().listInstallers(true).type("wls") + .commonName(CUSTOM_WLS_VERSION).version(WLS_VERSION).build(); + CommandResult listResult = Runner.run(listCommand, out, logger); + // the process return code for listItems should be 0 + System.out.println("Result: " + listResult.stdout()); + assertEquals(0, listResult.exitValue(), "for command: " + listCommand); + // output should show newly added WLS installer + assertTrue(listResult.stdout().contains(wlsPath.toString())); + assertTrue(listResult.stdout().contains(CUSTOM_WLS_VERSION + ":")); + } + } + + /** + * create a WLS image with custom name. + * + * @throws Exception - if any error occurs + */ + @Test + @Order(17) + @Tag("gate") + @DisplayName("Create custom WebLogic Server image") + void createCustomWlsImg(TestInfo testInfo) throws Exception { + String tagName = build_tag + ":" + getMethodName(testInfo); + String command = new CreateCommand().platform(PLATFORM_AMD64).tag(tagName) + .commonName(CUSTOM_WLS_VERSION).version(WLS_VERSION).build(); + + try (PrintWriter out = getTestMethodWriter(testInfo)) { + CommandResult result = Runner.run(command, out, logger); + assertEquals(0, result.exitValue(), "for command: " + command); + + // verify the docker image is created + assertTrue(imageExists(tagName), "Image was not created: " + tagName); + + wlsImgBuilt = true; + } + } + + + /** * Create a FMW image with internet access to download PSU. * Oracle Support credentials must be provided to download the patches. @@ -789,6 +979,7 @@ void createFmwImgFullInternetAccess(TestInfo testInfo) throws Exception { String addNewJdkCmd = new CacheCommand().addInstaller(true) .type("jdk") .version(JDK_VERSION_212) + .architecture(AMD64) .path(Paths.get(STAGING_DIR, JDK_INSTALLER_NEWER)) .build(); @@ -804,6 +995,7 @@ void createFmwImgFullInternetAccess(TestInfo testInfo) throws Exception { .jdkVersion(JDK_VERSION_212) .type("fmw") .user(oracleSupportUsername) + .platform(PLATFORM_AMD64) .passwordEnv("ORACLE_SUPPORT_PASSWORD") .latestPsu(true) .build(); @@ -853,6 +1045,7 @@ void createJrfDomainImgUsingWdt(TestInfo testInfo) throws Exception { String command = new CreateCommand() .tag(tagName) .version(WLS_VERSION) + .jdkVersion(JDK_VERSION_212) .wdtVersion(WDT_VERSION) .wdtArchive(WDT_ARCHIVE) .wdtDomainHome("/u01/domains/simple_domain") @@ -860,6 +1053,7 @@ void createJrfDomainImgUsingWdt(TestInfo testInfo) throws Exception { .wdtDomainType("JRF") .wdtRunRcu(true) .type("fmw") + .platform(PLATFORM_AMD64) .build(); CommandResult result = Runner.run(command, out, logger); @@ -888,6 +1082,7 @@ void createRestrictedJrfDomainImgUsingWdt(TestInfo testInfo) throws Exception { String command = new CreateCommand() .tag(tagName) .version(WLS_VERSION) + .jdkVersion(JDK_VERSION_212) .latestPsu(true) .user(oracleSupportUsername) .passwordEnv("ORACLE_SUPPORT_PASSWORD") @@ -897,6 +1092,7 @@ void createRestrictedJrfDomainImgUsingWdt(TestInfo testInfo) throws Exception { .wdtVariables(WDT_VARIABLES) .wdtDomainHome("/u01/domains/simple_domain") .wdtDomainType("RestrictedJRF") + .platform(PLATFORM_AMD64) .type("fmw") .build(); @@ -929,11 +1125,13 @@ void createWlsImgUsingMultiModels(TestInfo testInfo) throws Exception { String command = new CreateCommand() .tag(tagName) .version(WLS_VERSION) + .jdkVersion(JDK_VERSION_212) .wdtVersion(WDT_VERSION) .wdtArchive(WDT_ARCHIVE) .wdtDomainHome("/u01/domains/simple_domain") .wdtModel(WDT_MODEL, WDT_MODEL2) .wdtVariables(WDT_VARIABLES) + .platform(PLATFORM_AMD64) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { @@ -963,6 +1161,7 @@ void createWlsImgWithAdditionalBuildCommands(TestInfo testInfo) throws Exception .user(oracleSupportUsername) .passwordEnv("ORACLE_SUPPORT_PASSWORD") .additionalBuildCommands(WDT_RESOURCES.resolve("multi-sections.txt")) + .platform(PLATFORM_AMD64) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { @@ -1002,6 +1201,7 @@ void createImageWithServerJRE(TestInfo testInfo) throws Exception { String command = new CreateCommand() .tag(tagName) .fromImage(JRE_IMAGE) + .platform(PLATFORM_AMD64) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { @@ -1137,7 +1337,7 @@ void updateAddSecondModel(TestInfo testInfo) throws Exception { void createWlsImgWithOpenShiftSettings(TestInfo testInfo) throws Exception { String tagName = build_tag + ":" + getMethodName(testInfo); String command = new CreateCommand() - .jdkVersion(JDK_VERSION) + .jdkVersion(JDK_VERSION_212) .tag(tagName) .wdtVersion(WDT_VERSION) .wdtArchive(WDT_ARCHIVE) @@ -1145,6 +1345,7 @@ void createWlsImgWithOpenShiftSettings(TestInfo testInfo) throws Exception { .wdtModel(WDT_MODEL, WDT_MODEL2) .wdtVariables(WDT_VARIABLES) .target(KubernetesTarget.OPENSHIFT) + .platform(PLATFORM_AMD64) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { @@ -1175,6 +1376,7 @@ void createWlsImgFromJar(TestInfo testInfo) throws Exception { .addInstaller(true) .type("wls") .version("12.2.1.4.0") + .architecture(AMD64) .path(Paths.get(STAGING_DIR, "fmw_12.2.1.4.0_wls_lite_generic.jar")) .build(); @@ -1183,6 +1385,7 @@ void createWlsImgFromJar(TestInfo testInfo) throws Exception { String buildCommand = new CreateCommand() .tag(tagName) .version("12.2.1.4.0") + .platform(PLATFORM_AMD64) .build(); try (PrintWriter out = getTestMethodWriter(testInfo)) { diff --git a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CacheCommand.java b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CacheCommand.java index 5b8c29fdb..4f25b1100 100644 --- a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CacheCommand.java +++ b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CacheCommand.java @@ -6,30 +6,25 @@ import java.nio.file.Path; public class CacheCommand extends ImageToolCommand { + private String commonName; private String version; private String type; // cache flags - private boolean listItems; private boolean addInstaller; private boolean addPatch; - private boolean addEntry; - private boolean deleteEntry; + private boolean listInstallers; + private boolean listPatches; + private boolean deletePatch; + private boolean deleteInstaller; private String path; private String patchId; - private String key; - private String value; - + private String architecture; public CacheCommand() { super("cache"); } - public CacheCommand listItems(boolean value) { - listItems = value; - return this; - } - public CacheCommand addInstaller(boolean value) { addInstaller = value; return this; @@ -40,33 +35,32 @@ public CacheCommand addPatch(boolean value) { return this; } - - public CacheCommand addEntry(boolean value) { - addEntry = value; + public CacheCommand listInstallers(boolean value) { + listInstallers = value; return this; } - - public CacheCommand deleteEntry(boolean value) { - deleteEntry = value; + public CacheCommand listPatches(boolean value) { + listPatches = value; return this; } - public CacheCommand path(Path value) { - path = value.toString(); + public CacheCommand deletePatch(boolean value) { + deletePatch = value; return this; } - public CacheCommand key(String value) { - key = value; + + public CacheCommand deleteInstaller(boolean value) { + deleteInstaller = value; return this; } - public CacheCommand value(Path value) { - this.value = value.toString(); + public CacheCommand path(Path value) { + path = value.toString(); return this; } - + public CacheCommand patchId(String value) { patchId = value; return this; @@ -86,6 +80,16 @@ public CacheCommand type(String value) { return this; } + public CacheCommand architecture(String value) { + architecture = value; + return this; + } + + public CacheCommand commonName(String value) { + commonName = value; + return this; + } + /** * Generate the command using the provided command line options. * @return the imagetool command as a string suitable for running in ProcessBuilder @@ -93,17 +97,18 @@ public CacheCommand type(String value) { @Override public String build() { return super.build() - + field("listItems", listItems) + field("addInstaller", addInstaller) + field("addPatch", addPatch) - + field("addEntry", addEntry) - + field("deleteEntry", deleteEntry) - + field("--key", key) - + field("--value", value) + + field("deleteInstaller", deleteInstaller) + + field("deletePatch", deletePatch) + + field("listPatches", listPatches) + + field("listInstallers", listInstallers) + + field("--commonName", commonName) + field("--version", version) + field("--type", type) + field("--path", path) - + field("--patchId", patchId); + + field("--patchId", patchId) + + field("--architecture", architecture); } } diff --git a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CreateAuxCommand.java b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CreateAuxCommand.java index 5180603ff..b1f742128 100644 --- a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CreateAuxCommand.java +++ b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CreateAuxCommand.java @@ -18,6 +18,7 @@ public class CreateAuxCommand extends ImageToolCommand { private String wdtVariables; private String wdtDomainHome; private boolean wdtModelOnly; + private String platform; public CreateAuxCommand() { super("createAuxImage"); @@ -67,6 +68,11 @@ public CreateAuxCommand wdtModelOnly(boolean value) { return this; } + public CreateAuxCommand platform(String value) { + platform = value; + return this; + } + /** * Generate the command using the provided command line options. * @return the imagetool command as a string suitable for running in ProcessBuilder @@ -81,6 +87,7 @@ public String build() { + field("--wdtArchive", wdtArchive) + field("--wdtVariables", wdtVariables) + field("--wdtDomainHome", wdtDomainHome) + + field("--platform", platform) + field("--wdtModelOnly", wdtModelOnly); } } diff --git a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CreateCommand.java b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CreateCommand.java index 190071d4a..879ca1171 100644 --- a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CreateCommand.java +++ b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/CreateCommand.java @@ -33,6 +33,8 @@ public class CreateCommand extends ImageToolCommand { private String wdtDomainType; private boolean wdtRunRcu; private boolean wdtModelOnly; + private String platform; + private String commonName; public CreateCommand() { super("create"); @@ -142,6 +144,17 @@ public CreateCommand wdtModelOnly(boolean value) { return this; } + public CreateCommand platform(String value) { + platform = value; + return this; + } + + public CreateCommand commonName(String value) { + commonName = value; + return this; + } + + /** * Generate the command using the provided command line options. * @return the imagetool command as a string suitable for running in ProcessBuilder @@ -168,6 +181,9 @@ public String build() { + field("--wdtDomainHome", wdtDomainHome) + field("--wdtDomainType", wdtDomainType) + field("--wdtRunRCU", wdtRunRcu) + + field("--platform", platform) + + field("--commonName", commonName) + field("--wdtModelOnly", wdtModelOnly); + } } diff --git a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/RebaseCommand.java b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/RebaseCommand.java index 14ab329cf..7f359a393 100644 --- a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/RebaseCommand.java +++ b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/RebaseCommand.java @@ -7,6 +7,7 @@ public class RebaseCommand extends ImageToolCommand { private String targetImage; private String sourceImage; private String tag; + private String platform; public RebaseCommand() { super("rebase"); @@ -35,6 +36,11 @@ public RebaseCommand tag(String value) { return this; } + public RebaseCommand platform(String value) { + platform = value; + return this; + } + /** * Generate the command using the provided command line options. * @return the imagetool command as a string suitable for running in ProcessBuilder @@ -44,6 +50,7 @@ public String build() { return super.build() + field("--targetImage", targetImage) + field("--sourceImage", sourceImage) + + field("--platform", platform) + field("--tag", tag); } } diff --git a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/UpdateCommand.java b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/UpdateCommand.java index 89f9574ed..6ee38ce7c 100644 --- a/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/UpdateCommand.java +++ b/tests/src/test/java/com/oracle/weblogic/imagetool/tests/utils/UpdateCommand.java @@ -19,6 +19,8 @@ public class UpdateCommand extends ImageToolCommand { private String wdtArchive; private String wdtVariables; private boolean wdtModelOnly; + private String platform; + public UpdateCommand() { super("update"); @@ -39,6 +41,11 @@ public UpdateCommand patches(String... values) { return this; } + public UpdateCommand platform(String value) { + platform = value; + return this; + } + public UpdateCommand wdtVersion(String value) { wdtVersion = value; return this; @@ -78,6 +85,7 @@ public String build() { + field("--wdtModel", wdtModel) + field("--wdtArchive", wdtArchive) + field("--wdtVariables", wdtVariables) + + field("--platform", platform) + field("--wdtModelOnly", wdtModelOnly); } } diff --git a/tests/src/test/resources/wdt/simple-topology1.yaml b/tests/src/test/resources/wdt/simple-topology1.yaml index e8f63e913..a64ded613 100644 --- a/tests/src/test/resources/wdt/simple-topology1.yaml +++ b/tests/src/test/resources/wdt/simple-topology1.yaml @@ -13,6 +13,7 @@ topology: ProductionModeEnabled: true Cluster: cluster1: + FrontendHost: localhost ClientCertProxyEnabled: true DynamicServers: ServerTemplate: template1