Skip to content

Commit b770203

Browse files
authored
fix(tests): create MinIO buckets from test to avoid flaky operator bu… (#882)
# Summary The `e2e_om_ops_manager_backup_restore_minio` test continued to experience intermittent failures even after PR #852 fixed the MinIO operator's CA trust issue. The remaining flakiness was caused by a race condition between the MinIO operator's bucket creation attempts and MinIO's readiness to accept HTTPS connections. **Why the MinIO operator still fails to create buckets (even with CA trust fixed):** Even though PR #852 resolved the x509 certificate trust issue by providing the test CA to the MinIO operator, the operator still occasionally fails with "connection refused" errors when attempting to create buckets. This happens because: 1. The MinIO operator's reconcile loop triggers immediately after the MinIO tenant pods become "Ready" 2. Pod readiness does not guarantee that MinIO is fully ready to accept HTTPS connections 3. The operator attempts to create buckets via HTTPS API calls before MinIO's TLS listener is fully initialized 4. This results in intermittent "connection refused" errors, causing bucket creation to fail silently The test then times out waiting for buckets that were never created. **The fix:** Instead of relying on the MinIO operator's timing-dependent bucket creation, we now create the buckets directly from the test code using boto3: 1. After the MinIO tenant pods are ready, call `_create_minio_buckets()` which uses boto3's S3 client 2. Configure boto3 to use the test CA certificate for TLS verification 3. Implement retry logic (120s timeout, 5s intervals) to handle MinIO startup timing 4. Check if buckets already exist (created by operator) or create them via boto3 5. Log which method created each bucket for observability This approach completely bypasses the operator's unreliable bucket creation and makes the test deterministic. ## Proof of Work 5 independent test patches were run to verify the fix eliminates flakiness: | Patch # | Patch ID | Status | Build URL | |---------|----------|--------|-----------| | 1 | `69b02324c32e6b00075f770b` | ✅ Success (4/4) | https://evergreen.mongodb.com/version/69b02324c32e6b00075f770b | | 2 | `69b0232e8a5d860007735fc9` | ✅ Success (4/4) | https://evergreen.mongodb.com/version/69b0232e8a5d860007735fc9 | | 3 | `69b02339c03a4d00075eac0c` | ✅ Success (4/4) | https://evergreen.mongodb.com/version/69b02339c03a4d00075eac0c | | 4 | `69b02343f4bdb00007c3f6cd` | ✅ Success (4/4) | https://evergreen.mongodb.com/version/69b02343f4bdb00007c3f6cd | | 5 | `69b0234cc1f1690007bb9f21` | ✅ Success (4/4) | https://evergreen.mongodb.com/version/69b0234cc1f1690007bb9f21 | **Result: 20/20 tests passed (100% success rate)** all were created by boto3 ## Checklist - [ ] Have you linked a jira ticket and/or is the ticket in the title? - [ ] Have you checked whether your jira ticket required DOCSP changes? - [x] Have you added changelog file? - use `skip-changelog` label if not needed - refer to [Changelog files and Release Notes](https://github.com/mongodb/mongodb-kubernetes/blob/master/CONTRIBUTING.md#changelog-files-and-release-notes) section in CONTRIBUTING.md for more details
1 parent 2e92557 commit b770203

2 files changed

Lines changed: 26 additions & 33 deletions

File tree

docker/mongodb-kubernetes-tests/tests/opsmanager/conftest.py

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import os
44
import time
55
from pathlib import Path
6-
from typing import Dict, List, Optional
6+
from typing import Dict, List, Optional, Set
77

88
import boto3
99
from botocore.exceptions import ClientError
@@ -117,47 +117,49 @@ def mino_operator_install(
117117
)
118118

119119

120-
def _wait_for_minio_buckets(
120+
def _create_minio_buckets(
121121
endpoint: str,
122122
bucket_names: List[str],
123123
access_key: str = "minio",
124124
secret_key: str = "minio123",
125-
timeout: int = 500,
126-
interval: int = 10,
125+
timeout: int = 120,
126+
interval: int = 5,
127127
issuer_ca_filepath: Optional[str] = os.getenv("MINIO_ISSUER_CA_FILEPATH", None),
128-
):
129-
"""Poll S3/MinIO until all buckets are accessible or timeout is reached.
130-
131-
Pod readiness does not guarantee bucket provisioning is complete. This
132-
function bridges the gap by probing headBucket() with retry/backoff,
133-
mirroring the exact check OpsManager performs when saving S3 store config.
134-
"""
128+
) -> None:
129+
"""Ensure MinIO buckets exist via S3 API (create if missing). Uses test CA when tenant has custom TLS."""
135130
s3 = boto3.client(
136131
"s3",
137132
endpoint_url=f"https://{endpoint}",
138133
aws_access_key_id=access_key,
139134
aws_secret_access_key=secret_key,
140135
verify=issuer_ca_filepath,
141136
)
142-
137+
target: Set[str] = set(bucket_names)
138+
ready: Set[str] = set()
143139
deadline = time.time() + timeout
144-
pending = set(bucket_names)
145140

146141
while time.time() < deadline:
147-
for bucket in list(pending):
142+
for bucket in bucket_names:
143+
if bucket in ready:
144+
continue
148145
try:
149146
s3.head_bucket(Bucket=bucket)
150-
print(f"MinIO bucket '{bucket}' is accessible")
151-
pending.discard(bucket)
147+
ready.add(bucket)
152148
except ClientError as e:
153-
code = e.response["Error"]["Code"]
154-
print(f"MinIO bucket '{bucket}' not ready (HTTP {code}), retrying in {interval}s...")
155-
if not pending:
156-
print(f"All MinIO buckets accessible: {bucket_names}")
149+
if e.response["Error"].get("Code") in ("404", "NoSuchBucket"):
150+
try:
151+
s3.create_bucket(Bucket=bucket)
152+
ready.add(bucket)
153+
except ClientError as ce:
154+
if ce.response["Error"].get("Code") == "BucketAlreadyOwnedByYou":
155+
ready.add(bucket)
156+
except Exception:
157+
pass # MinIO not ready (connection/SSL), retry
158+
if ready >= target:
157159
return
158160
time.sleep(interval)
159161

160-
raise TimeoutError(f"MinIO buckets still inaccessible after {timeout}s: {pending}")
162+
raise TimeoutError(f"Could not create MinIO buckets within {timeout}s: missing {target - ready}")
161163

162164

163165
def mino_tenant_install(
@@ -214,10 +216,9 @@ def mino_tenant_install(
214216
print(f"Minio tenant already installed, skipping helm installation!")
215217

216218
get_pod_when_ready(namespace, f"app=minio", api_client=cluster_client)
217-
# Wait for MinIO bucket provisioning (pod ready ≠ buckets ready)
218-
# MinIO creates the buckets async via a kubernetes Job after the tenant pod is running,
219-
# so we need to wait for the buckets to be accessible before proceeding with tests that depend on them.
220-
_wait_for_minio_buckets(
219+
# Ensure buckets exist from the test so we don't rely on the operator (custom TLS often
220+
# breaks operator bucket creation). Retries until all buckets are created/accessible.
221+
_create_minio_buckets(
221222
endpoint=f"minio.{namespace}.svc.cluster.local",
222223
bucket_names=["s3-store-bucket", "oplog-s3-bucket"],
223224
issuer_ca_filepath=issuer_ca_filepath,

docker/mongodb-kubernetes-tests/tests/opsmanager/fixtures/minio/values-tenant.yaml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,6 @@ tenant:
109109
features:
110110
bucketDNS: false
111111
domains: { }
112-
## List of bucket definitions to create during tenant provisioning.
113-
## Example:
114-
# - name: my-minio-bucket
115-
# objectLock: false # optional
116-
# region: us-east-1 # optional
117-
buckets:
118-
- name: oplog-s3-bucket
119-
- name: s3-store-bucket
120112

121113
## List of secret names to use for generating MinIO users during tenant provisioning
122114
users: [ ]

0 commit comments

Comments
 (0)