From ba9907dbd8fb58c6aa83aead185d29efdbb7a9d7 Mon Sep 17 00:00:00 2001 From: Tyler Gu Date: Tue, 31 Oct 2023 21:23:31 -0500 Subject: [PATCH] Feat: support feature gates for Kind Signed-off-by: Tyler Gu --- acto/engine.py | 6 ++++-- acto/kubernetes_engine/base.py | 7 +++++-- acto/kubernetes_engine/kind.py | 21 ++++++++++++------- acto/lib/operator_config.py | 10 ++++++++- acto/post_process/post_diff_test.py | 6 ++++-- acto/post_process/simple_crash_test.py | 3 ++- .../measure_performance.py | 4 ++-- 7 files changed, 40 insertions(+), 17 deletions(-) diff --git a/acto/engine.py b/acto/engine.py index eb27f8cfed..d9f91605ec 100644 --- a/acto/engine.py +++ b/acto/engine.py @@ -628,13 +628,15 @@ def __init__(self, operator_config.deploy.init).new() if cluster_runtime == "KIND": - cluster = kind.Kind(acto_namespace=acto_namespace) + cluster = kind.Kind(acto_namespace=acto_namespace, + feature_gates=operator_config.kubernetes_engine.feature_gates) elif cluster_runtime == "K3D": cluster = k3d.K3D() else: logger.warning( f"Cluster Runtime {cluster_runtime} is not supported, defaulted to use kind") - cluster = kind.Kind(acto_namespace=acto_namespace) + cluster = kind.Kind(acto_namespace=acto_namespace, + feature_gates=operator_config.kubernetes_engine.feature_gates) self.cluster = cluster self.deploy = deploy diff --git a/acto/kubernetes_engine/base.py b/acto/kubernetes_engine/base.py index e56d06d167..f72af63aa3 100644 --- a/acto/kubernetes_engine/base.py +++ b/acto/kubernetes_engine/base.py @@ -1,7 +1,7 @@ import subprocess import time from abc import ABC, abstractmethod -from typing import Callable, List +from typing import Callable, List, Tuple import kubernetes @@ -9,18 +9,21 @@ from acto.utils import get_thread_logger KubernetesEnginePostHookType = Callable[[kubernetes.client.ApiClient], None] +KeyValuePairType = Tuple[str, str] class KubernetesEngine(ABC): @abstractmethod def __init__(self, acto_namespace: int, - posthooks: List[KubernetesEnginePostHookType] = None) -> None: ... + posthooks: List[KubernetesEnginePostHookType] = None, + feature_gates: List[KeyValuePairType] = None) -> None: ... '''Constructor for KubernetesEngine Args: acto_namespace: the namespace of the acto posthooks: a list of posthooks to be executed after the cluster is created + feature_gates: a list of feature gates to be enabled ''' @abstractmethod diff --git a/acto/kubernetes_engine/kind.py b/acto/kubernetes_engine/kind.py index c2647cbb2c..5bafdd0424 100644 --- a/acto/kubernetes_engine/kind.py +++ b/acto/kubernetes_engine/kind.py @@ -16,9 +16,11 @@ class Kind(base.KubernetesEngine): def __init__( - self, acto_namespace: int, posthooks: List[base.KubernetesEnginePostHookType] = None): - self.config_path = os.path.join(CONST.CLUSTER_CONFIG_FOLDER, f'KIND-{acto_namespace}.yaml') - self.posthooks = posthooks + self, acto_namespace: int, posthooks: List[base.KubernetesEnginePostHookType] = None, + feature_gates: List[base.KeyValuePairType] = None): + self._config_path = os.path.join(CONST.CLUSTER_CONFIG_FOLDER, f'KIND-{acto_namespace}.yaml') + self._posthooks = posthooks + self._feature_gates = feature_gates def configure_cluster(self, num_nodes: int, version: str): '''Create config file for kind''' @@ -45,12 +47,17 @@ def configure_cluster(self, num_nodes: int, version: str): }] }) + if self._feature_gates: + config_dict['featureGates'] = {} + for key, value in self._feature_gates: + config_dict['featureGates'][key] = value + try: os.mkdir(CONST.CLUSTER_CONFIG_FOLDER) except FileExistsError: pass - with open(self.config_path, 'w') as config_file: + with open(self._config_path, 'w') as config_file: yaml.dump(config_dict, config_file) self._k8s_version = version @@ -82,7 +89,7 @@ def create_cluster(self, name: str, kubeconfig: str): else: raise Exception('Missing kubeconfig for kind create') - cmd.extend(['--config', self.config_path]) + cmd.extend(['--config', self._config_path]) cmd.extend(['--image', f"kindest/node:{self._k8s_version}"]) @@ -103,8 +110,8 @@ def create_cluster(self, name: str, kubeconfig: str): logging.debug(f.read()) raise e - if self.posthooks: - for posthook in self.posthooks: + if self._posthooks: + for posthook in self._posthooks: posthook(apiclient=apiclient) def load_images(self, images_archive_path: str, name: str): diff --git a/acto/lib/operator_config.py b/acto/lib/operator_config.py index 84c14d02f6..7822e903e8 100644 --- a/acto/lib/operator_config.py +++ b/acto/lib/operator_config.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import List, Optional +from typing import Dict, List, Optional from pydantic import BaseModel, Field @@ -31,6 +31,11 @@ class AnalysisConfig(BaseModel, extra='forbid'): description='The relative path of the main package for the operator') +class KubernetesEngineConfig(BaseModel, extra='forbid'): + feature_gates: Dict[str, str] = Field( + description='Path to the feature gates file', default=None) + + class OperatorConfig(BaseModel, extra='forbid'): """Configuration for porting operators to Acto""" deploy: DeployConfig @@ -50,6 +55,9 @@ class OperatorConfig(BaseModel, extra='forbid'): diff_ignore_fields: List[str] = Field(default_factory=list) kubernetes_version: str = Field( default='v1.22.9', description='Kubernetes version') + kubernetes_engine: KubernetesEngineConfig = Field( + default=KubernetesEngineConfig(), description='Configuration for the Kubernetes engine' + ) monkey_patch: Optional[str] = Field( default=None, description='Path to the monkey patch file') diff --git a/acto/post_process/post_diff_test.py b/acto/post_process/post_diff_test.py index a247cf54ca..c5226d3bb1 100644 --- a/acto/post_process/post_diff_test.py +++ b/acto/post_process/post_diff_test.py @@ -461,7 +461,8 @@ def __init__( def post_process(self, workdir: str, num_workers: int = 1): if not os.path.exists(workdir): os.mkdir(workdir) - cluster = kind.Kind(acto_namespace=self.acto_namespace) + cluster = kind.Kind(acto_namespace=self.acto_namespace, + feature_gates=self.config.kubernetes_engine.feature_gates) cluster.configure_cluster(self.config.num_nodes, self.config.kubernetes_version) deploy = Deploy(DeployMethod.YAML, self.config.deploy.file, self.config.deploy.init).new() # Build an archive to be preloaded @@ -518,7 +519,8 @@ def check_diff_test_result(self, workqueue: multiprocessing.Queue, workdir: str, generation = 0 # for additional runner additional_runner_dir = os.path.join(workdir, f'additional-runner-{worker_id}') - cluster = kind.Kind(acto_namespace=self.acto_namespace) + cluster = kind.Kind(acto_namespace=self.acto_namespace, + feature_gates=self.config.kubernetes_engine.feature_gates) cluster.configure_cluster(self.config.num_nodes, self.config.kubernetes_version) deploy = Deploy(DeployMethod.YAML, self.config.deploy.file, self.config.deploy.init).new() diff --git a/acto/post_process/simple_crash_test.py b/acto/post_process/simple_crash_test.py index 01a7b81177..9b502c201d 100644 --- a/acto/post_process/simple_crash_test.py +++ b/acto/post_process/simple_crash_test.py @@ -179,7 +179,8 @@ def post_process(self, workdir: str, num_workers: int = 1): posthook = partial(create_anvil_crash_config_map, cr_kind=cr_kind, namespace=namespace, cr_name=cr_name) - cluster = kind.Kind(acto_namespace=self.acto_namespace, posthooks=[posthook]) + cluster = kind.Kind(acto_namespace=self.acto_namespace, posthooks=[posthook], + feature_gates=self.config.kubernetes_engine.feature_gates) cluster.configure_cluster(self.config.num_nodes, self.config.kubernetes_version) deploy = Deploy(DeployMethod.YAML, self.config.deploy.file, self.config.deploy.init).new() diff --git a/performance_measurement/measure_performance.py b/performance_measurement/measure_performance.py index 162f160a21..756c8ae298 100644 --- a/performance_measurement/measure_performance.py +++ b/performance_measurement/measure_performance.py @@ -20,7 +20,6 @@ from acto.kubernetes_engine import kind from acto.lib.operator_config import OperatorConfig from acto.post_process.post_chain_inputs import ChainInputs -from acto.reproduce import load_cr_from_trial from acto.utils.preprocess import process_crd from performance_measurement.measure_runner import MeasurementRunner @@ -84,7 +83,8 @@ def load_inputs_from_dir(dir: str) -> list: # start the k8s cluster kubeconfig = os.path.join(os.path.expanduser("~"), ".kube", "anvil") - cluster = kind.Kind(acto_namespace=0) + cluster = kind.Kind(acto_namespace=0, + feature_gates=config.kubernetes_engine.feature_gates) cluster.configure_cluster(config.num_nodes, config.kubernetes_version) cluster.create_cluster(name="anvil", kubeconfig=kubeconfig)