Skip to content

Commit cabd7d0

Browse files
Install CRDs in operator upgrade tests (#127)
# Summary #63 rebased on `master` branch. ## Proof of Work <!-- Enter your proof that it works here.--> ## 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? - [ ] Have you checked for release_note changes? ## Reminder (Please remove this when merging) - Please try to Approve or Reject Changes the PR, keep PRs in review as short as possible - Our Short Guide for PRs: [Link](https://docs.google.com/document/d/1T93KUtdvONq43vfTfUt8l92uo4e4SEEvFbIEKOxGr44/edit?tab=t.0) - Remember the following Communication Standards - use comment prefixes for clarity: * **blocking**: Must be addressed before approval. * **follow-up**: Can be addressed in a later PR or ticket. * **q**: Clarifying question. * **nit**: Non-blocking suggestions. * **note**: Side-note, non-actionable. Example: Praise * --> no prefix is considered a question --------- Co-authored-by: Łukasz Sierant <lukasz.sierant@mongodb.com>
1 parent 7ecd700 commit cabd7d0

File tree

9 files changed

+118
-51
lines changed

9 files changed

+118
-51
lines changed

docker/mongodb-kubernetes-tests/Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ RUN mkdir -p /tmp/mongodb-tools && \
4343
cp /tmp/mongodb-tools/*/bin/* /usr/local/bin/ && \
4444
rm -rf /tmp/mongodb-tools /tmp/mongodb-tools.tgz
4545

46+
RUN curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" \
47+
&& chmod +x ./kubectl \
48+
&& mv ./kubectl /usr/local/bin/kubectl
49+
4650
COPY --from=builder /venv /venv
4751

4852
ENV PATH="/venv/bin:${PATH}"

docker/mongodb-kubernetes-tests/kubetester/helm.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import glob
12
import logging
23
import os
34
import re
@@ -120,8 +121,16 @@ def helm_repo_add(repo_name: str, url: str):
120121

121122
def process_run_and_check(args, **kwargs):
122123
try:
124+
logger.debug(f"subprocess.run: {args}")
123125
completed_process = subprocess.run(args, **kwargs)
124-
completed_process.check_returncode()
126+
# always print process output
127+
if completed_process.stdout is not None:
128+
stdout = completed_process.stdout.decode("utf-8")
129+
logger.debug(f"stdout: {stdout}")
130+
if completed_process.stderr is not None:
131+
stderr = completed_process.stderr.decode("utf-8")
132+
logger.debug(f"stderr: {stderr}")
133+
completed_process.check_returncode()
125134
except subprocess.CalledProcessError as exc:
126135
if exc.stdout is not None:
127136
stdout = exc.stdout.decode("utf-8")
@@ -141,10 +150,17 @@ def helm_upgrade(
141150
helm_options: Optional[List[str]] = None,
142151
helm_override_path: Optional[bool] = False,
143152
custom_operator_version: Optional[str] = None,
153+
apply_crds_first: bool = False,
144154
):
145155
if not helm_chart_path:
146156
logger.warning("Helm chart path is empty, defaulting to 'helm_chart'")
147157
helm_chart_path = "helm_chart"
158+
159+
chart_dir = helm_chart_path if helm_override_path else _helm_chart_dir(helm_chart_path)
160+
161+
if apply_crds_first:
162+
apply_crds_from_chart(chart_dir)
163+
148164
command_args = _create_helm_args(helm_args, helm_options)
149165
args = [
150166
"helm",
@@ -154,19 +170,30 @@ def helm_upgrade(
154170
*command_args,
155171
name,
156172
]
173+
157174
if custom_operator_version:
158175
args.append(f"--version={custom_operator_version}")
159-
if helm_override_path:
160-
args.append(helm_chart_path)
161-
else:
162-
args.append(_helm_chart_dir(helm_chart_path))
176+
177+
args.append(chart_dir)
163178

164179
command = " ".join(args)
165-
logger.debug("Running helm upgrade command:")
166-
logger.debug(command)
167180
process_run_and_check(command, check=True, capture_output=True, shell=True)
168181

169182

183+
def apply_crds_from_chart(chart_dir: str):
184+
crd_files = glob.glob(os.path.join(chart_dir, "crds", "*.yaml"))
185+
186+
if not crd_files:
187+
raise Exception(f"No CRD files found in chart directory: {chart_dir}")
188+
189+
logger.info(f"Found {len(crd_files)} CRD files to apply:")
190+
191+
for crd_file in crd_files:
192+
logger.info(f"Applying CRD from file: {crd_file}")
193+
args = ["kubectl", "apply", "-f", crd_file]
194+
process_run_and_check(" ".join(args), check=True, capture_output=True, shell=True)
195+
196+
170197
def helm_uninstall(name):
171198
args = ("helm", "uninstall", name)
172199
logger.info(args)

docker/mongodb-kubernetes-tests/kubetester/operator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ def install(self, custom_operator_version: Optional[str] = None) -> Operator:
9595

9696
return self
9797

98-
def upgrade(self, multi_cluster: bool = False, custom_operator_version: Optional[str] = None) -> Operator:
98+
def upgrade(
99+
self, multi_cluster: bool = False, custom_operator_version: Optional[str] = None, apply_crds_first: bool = False
100+
) -> Operator:
99101
"""Upgrades the Operator in Kubernetes cluster using 'helm upgrade', waits until it's running"""
100102
helm_upgrade(
101103
self.name,
@@ -104,6 +106,7 @@ def upgrade(self, multi_cluster: bool = False, custom_operator_version: Optional
104106
helm_chart_path=self.helm_chart_path,
105107
helm_options=self.helm_options,
106108
custom_operator_version=custom_operator_version,
109+
apply_crds_first=apply_crds_first,
107110
)
108111
self._wait_for_operator_ready()
109112
self._wait_operator_webhook_is_ready(multi_cluster=multi_cluster)

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

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def get_version_id():
107107

108108

109109
@fixture(scope="module")
110-
def operator_installation_config(namespace: str) -> Dict[str, str]:
110+
def operator_installation_config(namespace: str) -> dict[str, str]:
111111
return get_operator_installation_config(namespace)
112112

113113

@@ -126,7 +126,7 @@ def get_operator_installation_config(namespace):
126126

127127

128128
@fixture(scope="module")
129-
def monitored_appdb_operator_installation_config(operator_installation_config: Dict[str, str]) -> Dict[str, str]:
129+
def monitored_appdb_operator_installation_config(operator_installation_config: dict[str, str]) -> dict[str, str]:
130130
"""Returns the ConfigMap containing configuration data for the Operator to be created
131131
and for the AppDB to be monitored.
132132
Created in the single_e2e.sh"""
@@ -135,7 +135,7 @@ def monitored_appdb_operator_installation_config(operator_installation_config: D
135135
return config
136136

137137

138-
def get_multi_cluster_operator_installation_config(namespace: str) -> Dict[str, str]:
138+
def get_multi_cluster_operator_installation_config(namespace: str) -> dict[str, str]:
139139
"""Returns the ConfigMap containing configuration data for the Operator to be created.
140140
Created in the single_e2e.sh"""
141141
config = KubernetesTester.read_configmap(
@@ -150,7 +150,7 @@ def get_multi_cluster_operator_installation_config(namespace: str) -> Dict[str,
150150
@fixture(scope="module")
151151
def multi_cluster_operator_installation_config(
152152
central_cluster_client: kubernetes.client.ApiClient, namespace: str
153-
) -> Dict[str, str]:
153+
) -> dict[str, str]:
154154
return get_multi_cluster_operator_installation_config(namespace)
155155

156156

@@ -159,15 +159,15 @@ def multi_cluster_monitored_appdb_operator_installation_config(
159159
central_cluster_client: kubernetes.client.ApiClient,
160160
namespace: str,
161161
multi_cluster_operator_installation_config: dict[str, str],
162-
) -> Dict[str, str]:
162+
) -> dict[str, str]:
163163
multi_cluster_operator_installation_config["customEnvVars"] = f"OPS_MANAGER_MONITOR_APPDB=true"
164164
return multi_cluster_operator_installation_config
165165

166166

167167
@fixture(scope="module")
168168
def operator_clusterwide(
169169
namespace: str,
170-
operator_installation_config: Dict[str, str],
170+
operator_installation_config: dict[str, str],
171171
) -> Operator:
172172
return get_operator_clusterwide(namespace, operator_installation_config)
173173

@@ -181,7 +181,7 @@ def get_operator_clusterwide(namespace, operator_installation_config):
181181
@fixture(scope="module")
182182
def operator_vault_secret_backend(
183183
namespace: str,
184-
monitored_appdb_operator_installation_config: Dict[str, str],
184+
monitored_appdb_operator_installation_config: dict[str, str],
185185
) -> Operator:
186186
helm_args = monitored_appdb_operator_installation_config.copy()
187187
helm_args["operator.vaultSecretBackend.enabled"] = "true"
@@ -191,7 +191,7 @@ def operator_vault_secret_backend(
191191
@fixture(scope="module")
192192
def operator_vault_secret_backend_tls(
193193
namespace: str,
194-
monitored_appdb_operator_installation_config: Dict[str, str],
194+
monitored_appdb_operator_installation_config: dict[str, str],
195195
) -> Operator:
196196
helm_args = monitored_appdb_operator_installation_config.copy()
197197
helm_args["operator.vaultSecretBackend.enabled"] = "true"
@@ -200,7 +200,7 @@ def operator_vault_secret_backend_tls(
200200

201201

202202
@fixture(scope="module")
203-
def operator_installation_config_quick_recovery(operator_installation_config: Dict[str, str]) -> Dict[str, str]:
203+
def operator_installation_config_quick_recovery(operator_installation_config: dict[str, str]) -> dict[str, str]:
204204
"""
205205
This functions appends automatic recovery settings for CLOUDP-189433. In order to make the test runnable in
206206
reasonable time, we override the Recovery back off to 120 seconds. This gives enough time for the initial
@@ -480,9 +480,9 @@ def get_custom_om_version():
480480
@fixture(scope="module")
481481
def default_operator(
482482
namespace: str,
483-
operator_installation_config: Dict[str, str],
483+
operator_installation_config: dict[str, str],
484484
central_cluster_name: str,
485-
multi_cluster_operator_installation_config: Dict[str, str],
485+
multi_cluster_operator_installation_config: dict[str, str],
486486
central_cluster_client: client.ApiClient,
487487
member_cluster_clients: List[MultiClusterClient],
488488
member_cluster_names: List[str],
@@ -500,24 +500,23 @@ def default_operator(
500500

501501

502502
def get_default_operator(
503-
namespace: str,
504-
operator_installation_config: Dict[str, str],
503+
namespace: str, operator_installation_config: dict[str, str], apply_crds_first: bool = False
505504
) -> Operator:
506505
"""Installs/upgrades a default Operator used by any test not interested in some custom Operator setting.
507506
TODO we use the helm template | kubectl apply -f process so far as Helm install/upgrade needs more refactoring in
508507
the shared environment"""
509508
operator = Operator(
510509
namespace=namespace,
511510
helm_args=operator_installation_config,
512-
).upgrade()
511+
).upgrade(apply_crds_first=apply_crds_first)
513512

514513
return operator
515514

516515

517516
@fixture(scope="module")
518517
def operator_with_monitored_appdb(
519518
namespace: str,
520-
monitored_appdb_operator_installation_config: Dict[str, str],
519+
monitored_appdb_operator_installation_config: dict[str, str],
521520
) -> Operator:
522521
"""Installs/upgrades a default Operator used by any test that needs the AppDB monitoring enabled."""
523522
return Operator(
@@ -628,7 +627,7 @@ def member_cluster_clients() -> List[MultiClusterClient]:
628627
def multi_cluster_operator(
629628
namespace: str,
630629
central_cluster_name: str,
631-
multi_cluster_operator_installation_config: Dict[str, str],
630+
multi_cluster_operator_installation_config: dict[str, str],
632631
central_cluster_client: client.ApiClient,
633632
member_cluster_clients: List[MultiClusterClient],
634633
member_cluster_names: List[str],
@@ -646,10 +645,11 @@ def multi_cluster_operator(
646645
def get_multi_cluster_operator(
647646
namespace: str,
648647
central_cluster_name: str,
649-
multi_cluster_operator_installation_config: Dict[str, str],
648+
multi_cluster_operator_installation_config: dict[str, str],
650649
central_cluster_client: client.ApiClient,
651650
member_cluster_clients: List[MultiClusterClient],
652651
member_cluster_names: List[str],
652+
apply_crds_first: bool = False,
653653
) -> Operator:
654654
os.environ["HELM_KUBECONTEXT"] = central_cluster_name
655655

@@ -667,14 +667,15 @@ def get_multi_cluster_operator(
667667
"operator.createOperatorServiceAccount": "false",
668668
},
669669
central_cluster_name,
670+
apply_crds_first=apply_crds_first,
670671
)
671672

672673

673674
@fixture(scope="module")
674675
def multi_cluster_operator_with_monitored_appdb(
675676
namespace: str,
676677
central_cluster_name: str,
677-
multi_cluster_monitored_appdb_operator_installation_config: Dict[str, str],
678+
multi_cluster_monitored_appdb_operator_installation_config: dict[str, str],
678679
central_cluster_client: client.ApiClient,
679680
member_cluster_clients: List[MultiClusterClient],
680681
member_cluster_names: List[str],
@@ -703,7 +704,7 @@ def multi_cluster_operator_with_monitored_appdb(
703704
def multi_cluster_operator_manual_remediation(
704705
namespace: str,
705706
central_cluster_name: str,
706-
multi_cluster_operator_installation_config: Dict[str, str],
707+
multi_cluster_operator_installation_config: dict[str, str],
707708
central_cluster_client: client.ApiClient,
708709
member_cluster_clients: List[MultiClusterClient],
709710
member_cluster_names: List[str],
@@ -754,7 +755,7 @@ def get_multi_cluster_operator_clustermode(namespace: str) -> Operator:
754755
def multi_cluster_operator_clustermode(
755756
namespace: str,
756757
central_cluster_name: str,
757-
multi_cluster_operator_installation_config: Dict[str, str],
758+
multi_cluster_operator_installation_config: dict[str, str],
758759
central_cluster_client: client.ApiClient,
759760
member_cluster_clients: List[MultiClusterClient],
760761
member_cluster_names: List[str],
@@ -767,7 +768,7 @@ def multi_cluster_operator_clustermode(
767768
def install_multi_cluster_operator_set_members_fn(
768769
namespace: str,
769770
central_cluster_name: str,
770-
multi_cluster_operator_installation_config: Dict[str, str],
771+
multi_cluster_operator_installation_config: dict[str, str],
771772
central_cluster_client: client.ApiClient,
772773
member_cluster_clients: List[MultiClusterClient],
773774
) -> Callable[[List[str]], Operator]:
@@ -793,14 +794,15 @@ def _fn(member_cluster_names: List[str]) -> Operator:
793794

794795
def _install_multi_cluster_operator(
795796
namespace: str,
796-
multi_cluster_operator_installation_config: Dict[str, str],
797+
multi_cluster_operator_installation_config: dict[str, str],
797798
central_cluster_client: client.ApiClient,
798799
member_cluster_clients: List[MultiClusterClient],
799-
helm_opts: Dict[str, str],
800+
helm_opts: dict[str, str],
800801
central_cluster_name: str,
801802
operator_name: Optional[str] = MULTI_CLUSTER_OPERATOR_NAME,
802803
helm_chart_path: Optional[str] = LOCAL_HELM_CHART_DIR,
803804
custom_operator_version: Optional[str] = None,
805+
apply_crds_first: bool = False,
804806
) -> Operator:
805807
multi_cluster_operator_installation_config.update(helm_opts)
806808

@@ -822,7 +824,7 @@ def _install_multi_cluster_operator(
822824
helm_args=multi_cluster_operator_installation_config,
823825
api_client=central_cluster_client,
824826
helm_chart_path=helm_chart_path,
825-
).upgrade(multi_cluster=True, custom_operator_version=custom_operator_version)
827+
).upgrade(multi_cluster=True, custom_operator_version=custom_operator_version, apply_crds_first=apply_crds_first)
826828

827829
# If we're running locally, then immediately after installing the deployment, we scale it to zero.
828830
# This way operator in POD is not interfering with locally running one.
@@ -840,7 +842,7 @@ def _install_multi_cluster_operator(
840842
def official_operator(
841843
namespace: str,
842844
managed_security_context: str,
843-
operator_installation_config: Dict[str, str],
845+
operator_installation_config: dict[str, str],
844846
central_cluster_name: str,
845847
central_cluster_client: client.ApiClient,
846848
member_cluster_clients: List[MultiClusterClient],
@@ -919,7 +921,7 @@ def install_legacy_deployment_state_meko(
919921
def install_official_operator(
920922
namespace: str,
921923
managed_security_context: str,
922-
operator_installation_config: Dict[str, str],
924+
operator_installation_config: dict[str, str],
923925
central_cluster_name: Optional[str],
924926
central_cluster_client: Optional[client.ApiClient],
925927
member_cluster_clients: Optional[List[MultiClusterClient]],
@@ -1033,7 +1035,7 @@ def log_deployment_and_images(deployments):
10331035

10341036
# Extract container images and deployments names from the nested dict returned by kubetester
10351037
# Handles any missing key gracefully
1036-
def extract_container_images_and_deployments(deployments) -> (Dict[str, str], List[str]):
1038+
def extract_container_images_and_deployments(deployments) -> (dict[str, str], List[str]):
10371039
deployment_images = {}
10381040
deployment_names = []
10391041
deployments = deployments.to_dict()
@@ -1318,7 +1320,7 @@ def get_api_servers_from_kubeconfig_secret(
13181320
return get_api_servers_from_pod_kubeconfig(kubeconfig_secret["kubeconfig"], cluster_clients)
13191321

13201322

1321-
def get_api_servers_from_test_pod_kubeconfig(namespace: str, member_cluster_names: List[str]) -> Dict[str, str]:
1323+
def get_api_servers_from_test_pod_kubeconfig(namespace: str, member_cluster_names: List[str]) -> dict[str, str]:
13221324
test_pod_cluster = get_test_pod_cluster_name()
13231325
cluster_clients = get_clients_for_clusters(member_cluster_names)
13241326

0 commit comments

Comments
 (0)