From 8b6973d75f03529e4588f04a1ed6a92512cc85ae Mon Sep 17 00:00:00 2001 From: Ruth Netser Date: Sun, 18 Aug 2024 16:15:36 +0300 Subject: [PATCH] class_generator: Get `metadta` from spec (#2058) * update machine and metadta * add test --- class_generator/class_generator.py | 21 ++- .../tests/manifests/Machine/machine_res.py | 88 +++++++++++ class_generator/tests/test_class_generator.py | 4 + ocp_resources/machine.py | 149 ++++-------------- 4 files changed, 141 insertions(+), 121 deletions(-) create mode 100644 class_generator/tests/manifests/Machine/machine_res.py diff --git a/class_generator/class_generator.py b/class_generator/class_generator.py index 5ae6585460..efec6c0d66 100644 --- a/class_generator/class_generator.py +++ b/class_generator/class_generator.py @@ -274,9 +274,14 @@ def generate_resource_file_from_dict( ) formatted_kind_str = convert_camel_case_to_snake_case(string_=resource_dict["kind"]) + if add_tests: overwrite = True - _output_file = os.path.join(TESTS_MANIFESTS_DIR, resource_dict["kind"], f"{formatted_kind_str}_res.py") + tests_path = os.path.join(TESTS_MANIFESTS_DIR, resource_dict["kind"]) + if not os.path.exists(tests_path): + os.makedirs(tests_path) + + _output_file = os.path.join(tests_path, f"{formatted_kind_str}_res.py") elif output_file: _output_file = output_file @@ -346,12 +351,16 @@ def format_description(description: str) -> str: def prepare_property_dict( schema: Dict[str, Any], - requeired: List[str], + required: List[str], resource_dict: Dict[str, Any], dict_key: str, ) -> Dict[str, Any]: + keys_to_ignore = ["kind", "apiVersion", "status", SPEC_STR.lower()] + if dict_key != SPEC_STR.lower(): + keys_to_ignore.append("metadata") + for key, val in schema.items(): - if key in {"metadata", "kind", "apiVersion", "status", SPEC_STR.lower()}: + if key in keys_to_ignore: continue val_schema = get_property_schema(property=val) @@ -360,7 +369,7 @@ def prepare_property_dict( resource_dict[dict_key].append({ "name-for-class-arg": python_name, "property-name": key, - "required": key in requeired, + "required": key in required, "description": format_description(description=val_schema["description"]), "type-for-docstring": type_dict["type-for-doc"], "type-for-class-arg": f"{python_name}: {type_dict['type-for-init']}", @@ -392,14 +401,14 @@ def parse_explain( spec_requeired = spec_schema.get("required", []) resource_dict = prepare_property_dict( schema=spec_schema.get("properties", {}), - requeired=spec_requeired, + required=spec_requeired, resource_dict=resource_dict, dict_key="spec", ) resource_dict = prepare_property_dict( schema=schema_properties, - requeired=fields_requeired, + required=fields_requeired, resource_dict=resource_dict, dict_key="fields", ) diff --git a/class_generator/tests/manifests/Machine/machine_res.py b/class_generator/tests/manifests/Machine/machine_res.py new file mode 100644 index 0000000000..b8282d6698 --- /dev/null +++ b/class_generator/tests/manifests/Machine/machine_res.py @@ -0,0 +1,88 @@ +# Generated using https://github.com/RedHatQE/openshift-python-wrapper/blob/main/scripts/resource/README.md + +from typing import Any, Dict, List, Optional +from ocp_resources.resource import NamespacedResource + + +class Machine(NamespacedResource): + """ + Machine is the Schema for the machines API Compatibility level 2: Stable within a major release for a minimum of 9 months or 3 minor releases (whichever is longer). + """ + + api_group: str = NamespacedResource.ApiGroup.MACHINE_OPENSHIFT_IO + + def __init__( + self, + lifecycle_hooks: Optional[Dict[str, Any]] = None, + metadata: Optional[Dict[str, Any]] = None, + provider_id: Optional[str] = "", + provider_spec: Optional[Dict[str, Any]] = None, + taints: Optional[List[Any]] = None, + **kwargs: Any, + ) -> None: + """ + Args: + lifecycle_hooks(Dict[str, Any]): LifecycleHooks allow users to pause operations on the machine at + certain predefined points within the machine lifecycle. + + metadata(Dict[str, Any]): ObjectMeta will autopopulate the Node created. Use this to indicate + what labels, annotations, name prefix, etc., should be used when + creating the Node. + + provider_id(str): ProviderID is the identification ID of the machine provided by the + provider. This field must match the provider ID as seen on the + node object corresponding to this machine. This field is required + by higher level consumers of cluster-api. Example use case is + cluster autoscaler with cluster-api as provider. Clean-up logic in + the autoscaler compares machines to nodes to find out machines at + provider which could not get registered as Kubernetes nodes. With + cluster-api as a generic out-of-tree provider for autoscaler, this + field is required by autoscaler to be able to have a provider view + of the list of machines. Another list of nodes is queried from the + k8s apiserver and then a comparison is done to find out + unregistered machines and are marked for delete. This field will + be set by the actuators and consumed by higher level entities like + autoscaler that will be interfacing with cluster-api as generic + provider. + + provider_spec(Dict[str, Any]): ProviderSpec details Provider-specific configuration to use during + node creation. + + taints(List[Any]): The list of the taints to be applied to the corresponding Node in + additive manner. This list will not overwrite any other taints + added to the Node on an ongoing basis by other entities. These + taints should be actively reconciled e.g. if you ask the machine + controller to apply a taint and then manually remove the taint the + machine controller will put it back) but not have the machine + controller remove any taints + + """ + super().__init__(**kwargs) + + self.lifecycle_hooks = lifecycle_hooks + self.metadata = metadata + self.provider_id = provider_id + self.provider_spec = provider_spec + self.taints = taints + + def to_dict(self) -> None: + super().to_dict() + + if not self.yaml_file: + self.res["spec"] = {} + _spec = self.res["spec"] + + if self.lifecycle_hooks: + _spec["lifecycleHooks"] = self.lifecycle_hooks + + if self.metadata: + _spec["metadata"] = self.metadata + + if self.provider_id: + _spec["providerID"] = self.provider_id + + if self.provider_spec: + _spec["providerSpec"] = self.provider_spec + + if self.taints: + _spec["taints"] = self.taints diff --git a/class_generator/tests/test_class_generator.py b/class_generator/tests/test_class_generator.py index 74ed72beed..7175bae425 100644 --- a/class_generator/tests/test_class_generator.py +++ b/class_generator/tests/test_class_generator.py @@ -39,6 +39,10 @@ "APIServer", os.path.join(TESTS_MANIFESTS_DIR, "APIServer", "api_server_res.py"), ), + ( + "Machine", + os.path.join(TESTS_MANIFESTS_DIR, "Machine", "machine_res.py"), + ), ), ) def test_parse_explain(tmpdir_factory, kind, result_file): diff --git a/ocp_resources/machine.py b/ocp_resources/machine.py index 9b2b97d306..a4270d8303 100644 --- a/ocp_resources/machine.py +++ b/ocp_resources/machine.py @@ -6,9 +6,7 @@ class Machine(NamespacedResource): """ - Machine is the Schema for the machines API Compatibility level 2: Stable - within a major release for a minimum of 9 months or 3 minor releases - (whichever is longer). + Machine is the Schema for the machines API Compatibility level 2: Stable within a major release for a minimum of 9 months or 3 minor releases (whichever is longer). """ api_group: str = NamespacedResource.ApiGroup.MACHINE_OPENSHIFT_IO @@ -24,118 +22,39 @@ def __init__( ) -> None: """ Args: - lifecycle_hooks(Dict[Any, Any]): LifecycleHooks allow users to pause operations on the machine at certain - predefined points within the machine lifecycle. - - FIELDS: - preDrain <[]Object> - PreDrain hooks prevent the machine from being drained. This also blocks - further lifecycle events, such as termination. - - preTerminate <[]Object> - PreTerminate hooks prevent the machine from being terminated. PreTerminate - hooks be actioned after the Machine has been drained. - - metadata(Dict[Any, Any]): ObjectMeta will autopopulate the Node created. Use this to indicate what - labels, annotations, name prefix, etc., should be used when creating the - Node. - - FIELDS: - annotations - Annotations is an unstructured key value map stored with a resource that may - be set by external tools to store and retrieve arbitrary metadata. They are - not queryable and should be preserved when modifying objects. More info: - http://kubernetes.io/docs/user-guide/annotations - - generateName - GenerateName is an optional prefix, used by the server, to generate a unique - name ONLY IF the Name field has not been provided. If this field is used, - the name returned to the client will be different than the name passed. This - value will also be combined with a unique suffix. The provided value has the - same validation rules as the Name field, and may be truncated by the length - of the suffix required to make the value unique on the server. - If this field is specified and the generated name exists, the server will - NOT return a 409 - instead, it will either return 201 Created or 500 with - Reason ServerTimeout indicating a unique name could not be found in the time - allotted, and the client should retry (optionally after the time indicated - in the Retry-After header). - Applied only if Name is not specified. More info: - https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency - - labels - Map of string keys and values that can be used to organize and categorize - (scope and select) objects. May match selectors of replication controllers - and services. More info: http://kubernetes.io/docs/user-guide/labels - - name - Name must be unique within a namespace. Is required when creating resources, - although some resources may allow a client to request the generation of an - appropriate name automatically. Name is primarily intended for creation - idempotence and configuration definition. Cannot be updated. More info: - http://kubernetes.io/docs/user-guide/identifiers#names - - namespace - Namespace defines the space within each name must be unique. An empty - namespace is equivalent to the "default" namespace, but "default" is the - canonical representation. Not all objects are required to be scoped to a - namespace - the value of this field for those objects will be empty. - Must be a DNS_LABEL. Cannot be updated. More info: - http://kubernetes.io/docs/user-guide/namespaces - - ownerReferences <[]Object> - List of objects depended by this object. If ALL objects in the list have - been deleted, this object will be garbage collected. If this object is - managed by a controller, then an entry in this list will point to this - controller, with the controller field set to true. There cannot be more than - one managing controller. - - provider_id(str): ProviderID is the identification ID of the machine provided by the provider. - This field must match the provider ID as seen on the node object - corresponding to this machine. This field is required by higher level - consumers of cluster-api. Example use case is cluster autoscaler with - cluster-api as provider. Clean-up logic in the autoscaler compares machines - to nodes to find out machines at provider which could not get registered as - Kubernetes nodes. With cluster-api as a generic out-of-tree provider for - autoscaler, this field is required by autoscaler to be able to have a - provider view of the list of machines. Another list of nodes is queried from - the k8s apiserver and then a comparison is done to find out unregistered - machines and are marked for delete. This field will be set by the actuators - and consumed by higher level entities like autoscaler that will be - interfacing with cluster-api as generic provider. - - provider_spec(Dict[Any, Any]): ProviderSpec details Provider-specific configuration to use during node - creation. - - FIELDS: - value - Value is an inlined, serialized representation of the resource - configuration. It is recommended that providers maintain their own versioned - API types that should be serialized/deserialized from this field, akin to - component config. - - taints(List[Any]): The list of the taints to be applied to the corresponding Node in additive - manner. This list will not overwrite any other taints added to the Node on - an ongoing basis by other entities. These taints should be actively - reconciled e.g. if you ask the machine controller to apply a taint and then - manually remove the taint the machine controller will put it back) but not - have the machine controller remove any taints - The node this Taint is attached to has the "effect" on any pod that does not - tolerate the Taint. - - FIELDS: - effect -required- - Required. The effect of the taint on pods that do not tolerate the taint. - Valid effects are NoSchedule, PreferNoSchedule and NoExecute. - - key -required- - Required. The taint key to be applied to a node. - - timeAdded - TimeAdded represents the time at which the taint was added. It is only - written for NoExecute taints. - - value - The taint value corresponding to the taint key. + lifecycle_hooks(Dict[str, Any]): LifecycleHooks allow users to pause operations on the machine at + certain predefined points within the machine lifecycle. + + metadata(Dict[str, Any]): ObjectMeta will autopopulate the Node created. Use this to indicate + what labels, annotations, name prefix, etc., should be used when + creating the Node. + + provider_id(str): ProviderID is the identification ID of the machine provided by the + provider. This field must match the provider ID as seen on the + node object corresponding to this machine. This field is required + by higher level consumers of cluster-api. Example use case is + cluster autoscaler with cluster-api as provider. Clean-up logic in + the autoscaler compares machines to nodes to find out machines at + provider which could not get registered as Kubernetes nodes. With + cluster-api as a generic out-of-tree provider for autoscaler, this + field is required by autoscaler to be able to have a provider view + of the list of machines. Another list of nodes is queried from the + k8s apiserver and then a comparison is done to find out + unregistered machines and are marked for delete. This field will + be set by the actuators and consumed by higher level entities like + autoscaler that will be interfacing with cluster-api as generic + provider. + + provider_spec(Dict[str, Any]): ProviderSpec details Provider-specific configuration to use during + node creation. + + taints(List[Any]): The list of the taints to be applied to the corresponding Node in + additive manner. This list will not overwrite any other taints + added to the Node on an ongoing basis by other entities. These + taints should be actively reconciled e.g. if you ask the machine + controller to apply a taint and then manually remove the taint the + machine controller will put it back) but not have the machine + controller remove any taints """ super().__init__(**kwargs)