From 4d37e601b2a5be7de6f2650487762192bbeb42a2 Mon Sep 17 00:00:00 2001 From: Roy Golan Date: Tue, 21 Apr 2026 15:08:53 +0300 Subject: [PATCH 1/2] feat: add STORAGE_SKIP_SSL_VERIFICATION support for copy-offload storage secrets Allow skipping SSL certificate verification for storage array connections in copy-offload tests, configurable via providers.json or env var. Co-Authored-By: Claude Opus 4.6 --- .providers.json.example | 1 + cli/mtv_api_tests/common.py | 9 +++++---- cli/mtv_api_tests/generate.py | 4 +++- docs/container-and-openshift-job-execution.md | 1 + docs/copy-offload-migrations.md | 1 + docs/installation-and-local-setup.md | 1 + docs/optional-integrations-and-secrets.md | 1 + tests/copyoffload/conftest.py | 5 +++++ 8 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.providers.json.example b/.providers.json.example index 24a079f7..4fc5b07f 100644 --- a/.providers.json.example +++ b/.providers.json.example @@ -57,6 +57,7 @@ "storage_hostname": "storage.example.com", "storage_username": "admin", "storage_password": "your-password-here", # pragma: allowlist secret + # "storage_skip_ssl_verification": "true", # Optional: skip SSL cert verification for storage array # Vendor-specific fields (configure based on your storage_vendor_product): # IMPORTANT: Only configure the fields for your selected storage_vendor_product. diff --git a/cli/mtv_api_tests/common.py b/cli/mtv_api_tests/common.py index 63fe4f26..be53c305 100644 --- a/cli/mtv_api_tests/common.py +++ b/cli/mtv_api_tests/common.py @@ -325,18 +325,19 @@ def get_vsphere_credentials() -> tuple[str, str, str, dict[str, str]]: return host, username, password, ssl_config -def get_storage_credentials() -> tuple[str, str, str]: - """Gather storage array credentials from env vars or interactive prompts. +def get_storage_credentials() -> tuple[str, str, str, dict[str, str]]: + """Gather storage array credentials and SSL config from env vars or interactive prompts. Returns: - Tuple of (hostname, username, password). + Tuple of (hostname, username, password, ssl_config). """ console.print("\n[bold]Storage Array Credentials[/bold]") console.print(" [dim]Tip: set STORAGE_HOSTNAME, STORAGE_USERNAME, STORAGE_PASSWORD env vars to skip prompts[/dim]") hostname = _env_or_prompt("STORAGE_HOSTNAME", "Storage hostname/IP") username = _env_or_prompt("STORAGE_USERNAME", "Storage username") password = _env_or_prompt("STORAGE_PASSWORD", "Storage password", password=True) - return hostname, username, password + ssl_config = _get_ssl_config("STORAGE") + return hostname, username, password, ssl_config def get_ocp_credentials() -> dict[str, str]: diff --git a/cli/mtv_api_tests/generate.py b/cli/mtv_api_tests/generate.py index 28693e80..fbdf73af 100644 --- a/cli/mtv_api_tests/generate.py +++ b/cli/mtv_api_tests/generate.py @@ -158,7 +158,7 @@ def _gather_copyoffload_config( vendor_fields = gather_vendor_fields(vendor) # Storage credentials - storage_host, storage_user, storage_pass = get_storage_credentials() + storage_host, storage_user, storage_pass, storage_ssl = get_storage_credentials() # Guest VM credentials console.print("\n[bold]Guest VM Credentials[/bold]") @@ -186,6 +186,8 @@ def _gather_copyoffload_config( **esxi_config, **vendor_fields, } + if storage_ssl.get("verify_ssl") == "false": + config["storage_skip_ssl_verification"] = "true" if vsphere_resources["secondary_ds"]: config["secondary_datastore_id"] = vsphere_resources["secondary_ds"]["id"] if rdm_lun_uuid: diff --git a/docs/container-and-openshift-job-execution.md b/docs/container-and-openshift-job-execution.md index 59097ebf..554442d9 100644 --- a/docs/container-and-openshift-job-execution.md +++ b/docs/container-and-openshift-job-execution.md @@ -274,6 +274,7 @@ Common environment variable names come straight from the fixtures: - `COPYOFFLOAD_STORAGE_HOSTNAME` - `COPYOFFLOAD_STORAGE_USERNAME` - `COPYOFFLOAD_STORAGE_PASSWORD` +- `COPYOFFLOAD_STORAGE_SKIP_SSL_VERIFICATION` - vendor-specific names such as `COPYOFFLOAD_ONTAP_SVM` - ESXi SSH values such as `COPYOFFLOAD_ESXI_HOST`, `COPYOFFLOAD_ESXI_USER`, and `COPYOFFLOAD_ESXI_PASSWORD` diff --git a/docs/copy-offload-migrations.md b/docs/copy-offload-migrations.md index e15c1467..45927d6f 100644 --- a/docs/copy-offload-migrations.md +++ b/docs/copy-offload-migrations.md @@ -139,6 +139,7 @@ That pattern works for the common storage credentials: - `COPYOFFLOAD_STORAGE_HOSTNAME` - `COPYOFFLOAD_STORAGE_USERNAME` - `COPYOFFLOAD_STORAGE_PASSWORD` +- `COPYOFFLOAD_STORAGE_SKIP_SSL_VERIFICATION` It also works for vendor-specific and ESXi-specific values such as: diff --git a/docs/installation-and-local-setup.md b/docs/installation-and-local-setup.md index a703a511..ea29907e 100644 --- a/docs/installation-and-local-setup.md +++ b/docs/installation-and-local-setup.md @@ -173,6 +173,7 @@ That means variables such as these override file values when present: - `COPYOFFLOAD_STORAGE_HOSTNAME` - `COPYOFFLOAD_STORAGE_USERNAME` - `COPYOFFLOAD_STORAGE_PASSWORD` +- `COPYOFFLOAD_STORAGE_SKIP_SSL_VERIFICATION` - `COPYOFFLOAD_ESXI_HOST` - `COPYOFFLOAD_ESXI_USER` - `COPYOFFLOAD_ESXI_PASSWORD` diff --git a/docs/optional-integrations-and-secrets.md b/docs/optional-integrations-and-secrets.md index 653182ed..19f2a39d 100644 --- a/docs/optional-integrations-and-secrets.md +++ b/docs/optional-integrations-and-secrets.md @@ -193,6 +193,7 @@ That helper is used for the credential-like copy-offload inputs. In the current | `storage_hostname` | `COPYOFFLOAD_STORAGE_HOSTNAME` | | `storage_username` | `COPYOFFLOAD_STORAGE_USERNAME` | | `storage_password` | `COPYOFFLOAD_STORAGE_PASSWORD` | +| `storage_skip_ssl_verification` | `COPYOFFLOAD_STORAGE_SKIP_SSL_VERIFICATION` | | `ontap_svm` | `COPYOFFLOAD_ONTAP_SVM` | | `vantara_storage_id` | `COPYOFFLOAD_VANTARA_STORAGE_ID` | | `vantara_storage_port` | `COPYOFFLOAD_VANTARA_STORAGE_PORT` | diff --git a/tests/copyoffload/conftest.py b/tests/copyoffload/conftest.py index c8665658..c4e277cb 100644 --- a/tests/copyoffload/conftest.py +++ b/tests/copyoffload/conftest.py @@ -180,6 +180,11 @@ def copyoffload_storage_secret( "STORAGE_PASSWORD": storage_password, } + # Optional SSL verification skip + skip_ssl = get_copyoffload_credential("storage_skip_ssl_verification", copyoffload_cfg) + if skip_ssl and skip_ssl.lower() in ("true", "1", "yes"): + secret_data["STORAGE_SKIP_SSL_VERIFICATION"] = "true" + # Vendor-specific configuration mapping # Maps vendor name to list of (config_key, secret_key, required) tuples # Based on forklift vsphere-xcopy-volume-populator code and README From 7caa3a5139d81a7927d7519b55ed93ea9b8e3efb Mon Sep 17 00:00:00 2001 From: Roy Golan Date: Sun, 3 May 2026 14:27:10 +0300 Subject: [PATCH 2/2] fix: guard skip_ssl against non-string types and add strip() normalization Coerce skip_ssl via str() before calling .lower() to handle JSON booleans from dict[str, Any], and add .strip() for parity with _get_ssl_config. Co-Authored-By: Claude Opus 4.6 --- tests/copyoffload/conftest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/copyoffload/conftest.py b/tests/copyoffload/conftest.py index c4e277cb..0c253773 100644 --- a/tests/copyoffload/conftest.py +++ b/tests/copyoffload/conftest.py @@ -182,7 +182,8 @@ def copyoffload_storage_secret( # Optional SSL verification skip skip_ssl = get_copyoffload_credential("storage_skip_ssl_verification", copyoffload_cfg) - if skip_ssl and skip_ssl.lower() in ("true", "1", "yes"): + normalized_skip_ssl = str(skip_ssl).strip().lower() if skip_ssl is not None else "" + if normalized_skip_ssl in ("true", "1", "yes"): secret_data["STORAGE_SKIP_SSL_VERIFICATION"] = "true" # Vendor-specific configuration mapping