Loading src/openop_tfsdk/contract/adapters/kubernetes.py +30 −21 Changes for src/openop_tfsdk/contract/adapters/kubernetes.py: 30 added lines, 21 removed lines. Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ import json import subprocess from typing import Optional from openop_tfsdk.contract.errors import DomainExecutionFailed, ValidationError from openop_tfsdk.contract.execution_models import ( CapabilityLifecycleRequest, CapabilityLifecycleResult, Loading @@ -19,6 +20,9 @@ from openop_tfsdk.contract.execution_models import ( RuntimeState, ) from openop_tfsdk.contract.models import SupportedAction from openop_tfsdk.logger import setup_logger log = setup_logger(__name__) LIFECYCLE_CAPABILITY_KINDS = frozenset({"workload"}) Loading @@ -33,7 +37,7 @@ RUNTIME_KINDS = ["helm"] class KubernetesCapabilityAdapter: """Declares the kubernetes execution surface. Performs no backend call.""" """Declares the kubernetes execution surface and executes it through helm.""" adapter_type = "kubernetes" Loading @@ -48,6 +52,8 @@ class KubernetesCapabilityAdapter: def activate(self, request: CapabilityLifecycleRequest) -> CapabilityLifecycleResult: unit = request.deployment_unit if unit is None or not unit.artifact_ref: raise ValidationError("activate needs a deployment_unit with an artifact_ref") release_name = unit.deployment_unit_ref namespace = self._infrastructure.get("namespace", "default") Loading @@ -59,6 +65,8 @@ class KubernetesCapabilityAdapter: unit.artifact_ref, "--namespace", namespace, "--create-namespace", "--atomic", "--wait", ], capture_output=True, Loading @@ -66,12 +74,8 @@ class KubernetesCapabilityAdapter: ) if result.returncode != 0: return CapabilityLifecycleResult( operation_id=request.operation_id, capability_kind=request.capability_kind, state=LifecycleState.FAILED, details={"stderr": result.stderr}, ) log.error("helm install failed: %s", result.stderr) raise DomainExecutionFailed("helm install failed") return CapabilityLifecycleResult( operation_id=request.operation_id, Loading @@ -81,6 +85,8 @@ class KubernetesCapabilityAdapter: ) def read_runtime_state(self, request: CapabilityRuntimeStateRequest) -> CapabilityRuntimeState: if not request.external_id: raise ValidationError("read_runtime_state needs the external_id returned by activate") release_name = request.external_id namespace = self._infrastructure.get("namespace", "default") Loading @@ -99,15 +105,20 @@ class KubernetesCapabilityAdapter: ) if result.returncode != 0: log.warning("helm status failed: %s", result.stderr) return CapabilityRuntimeState( external_id=release_name, capability_kind=request.capability_kind, state=RuntimeState.UNKNOWN, details={"stderr": result.stderr}, ) helm_status = json.loads(result.stdout)["info"]["status"] state = RuntimeState.ACTIVE if helm_status == "deployed" else RuntimeState.DEGRADED if helm_status == "deployed": state = RuntimeState.ACTIVE elif helm_status == "failed": state = RuntimeState.FAILED else: state = RuntimeState.DEGRADED return CapabilityRuntimeState( external_id=release_name, Loading @@ -117,6 +128,8 @@ class KubernetesCapabilityAdapter: ) def terminate(self, request: CapabilityLifecycleRequest) -> CapabilityLifecycleResult: if not request.external_id: raise ValidationError("terminate needs the external_id returned by activate") release_name = request.external_id namespace = self._infrastructure.get("namespace", "default") Loading @@ -134,12 +147,8 @@ class KubernetesCapabilityAdapter: ) if result.returncode != 0: return CapabilityLifecycleResult( operation_id=request.operation_id, capability_kind=request.capability_kind, state=LifecycleState.FAILED, details={"stderr": result.stderr}, ) log.error("helm uninstall failed: %s", result.stderr) raise DomainExecutionFailed("helm uninstall failed") return CapabilityLifecycleResult( operation_id=request.operation_id, Loading @@ -150,6 +159,10 @@ class KubernetesCapabilityAdapter: def update(self, request: CapabilityLifecycleRequest) -> CapabilityLifecycleResult: unit = request.deployment_unit if not request.external_id: raise ValidationError("update needs the external_id returned by activate") if unit is None or not unit.artifact_ref: raise ValidationError("update needs a deployment_unit with an artifact_ref") release_name = request.external_id namespace = self._infrastructure.get("namespace", "default") Loading @@ -168,12 +181,8 @@ class KubernetesCapabilityAdapter: ) if result.returncode != 0: return CapabilityLifecycleResult( operation_id=request.operation_id, capability_kind=request.capability_kind, state=LifecycleState.FAILED, details={"stderr": result.stderr}, ) log.error("helm upgrade failed: %s", result.stderr) raise DomainExecutionFailed("helm upgrade failed") return CapabilityLifecycleResult( operation_id=request.operation_id, Loading src/openop_tfsdk/contract/errors.py +10 −0 Changes for src/openop_tfsdk/contract/errors.py: 10 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -39,3 +39,13 @@ class InvalidTargetRef(TFSDKError): class UnknownAdapterKind(TFSDKError): error_code = "UNKNOWN_ADAPTER_KIND" retryable = False class ValidationError(TFSDKError): error_code = "VALIDATION_ERROR" retryable = False class DomainExecutionFailed(TFSDKError): error_code = "DOMAIN_EXECUTION_FAILED" retryable = False src/openop_tfsdk/contract/topology.py +2 −4 Changes for src/openop_tfsdk/contract/topology.py: 2 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -16,13 +16,13 @@ from typing import List, Optional, Tuple import yaml from openop_tfsdk.contract.adapters import capability_surface from openop_tfsdk.contract.errors import InvalidTargetRef from openop_tfsdk.contract.interfaces import TopologyDiscoveryAdapter from openop_tfsdk.contract.models import ( Capability, ControlPathBinding, Domain, State, SupportedAction, Zone, ) Loading Loading @@ -111,9 +111,7 @@ class BootstrapTopology(TopologyDiscoveryAdapter): # for its adapter kind and configuration, without building an adapter. control_path = self._control_paths.get(raw["control_path_ref"]) if control_path is None: supported_actions: List[SupportedAction] = [] supported_runtime_kinds: List[str] = [] else: raise InvalidTargetRef(raw["control_path_ref"]) adapter_type, infrastructure = control_path supported_actions, supported_runtime_kinds = capability_surface( adapter_type, capability_kind, infrastructure Loading Loading
src/openop_tfsdk/contract/adapters/kubernetes.py +30 −21 Changes for src/openop_tfsdk/contract/adapters/kubernetes.py: 30 added lines, 21 removed lines. Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ import json import subprocess from typing import Optional from openop_tfsdk.contract.errors import DomainExecutionFailed, ValidationError from openop_tfsdk.contract.execution_models import ( CapabilityLifecycleRequest, CapabilityLifecycleResult, Loading @@ -19,6 +20,9 @@ from openop_tfsdk.contract.execution_models import ( RuntimeState, ) from openop_tfsdk.contract.models import SupportedAction from openop_tfsdk.logger import setup_logger log = setup_logger(__name__) LIFECYCLE_CAPABILITY_KINDS = frozenset({"workload"}) Loading @@ -33,7 +37,7 @@ RUNTIME_KINDS = ["helm"] class KubernetesCapabilityAdapter: """Declares the kubernetes execution surface. Performs no backend call.""" """Declares the kubernetes execution surface and executes it through helm.""" adapter_type = "kubernetes" Loading @@ -48,6 +52,8 @@ class KubernetesCapabilityAdapter: def activate(self, request: CapabilityLifecycleRequest) -> CapabilityLifecycleResult: unit = request.deployment_unit if unit is None or not unit.artifact_ref: raise ValidationError("activate needs a deployment_unit with an artifact_ref") release_name = unit.deployment_unit_ref namespace = self._infrastructure.get("namespace", "default") Loading @@ -59,6 +65,8 @@ class KubernetesCapabilityAdapter: unit.artifact_ref, "--namespace", namespace, "--create-namespace", "--atomic", "--wait", ], capture_output=True, Loading @@ -66,12 +74,8 @@ class KubernetesCapabilityAdapter: ) if result.returncode != 0: return CapabilityLifecycleResult( operation_id=request.operation_id, capability_kind=request.capability_kind, state=LifecycleState.FAILED, details={"stderr": result.stderr}, ) log.error("helm install failed: %s", result.stderr) raise DomainExecutionFailed("helm install failed") return CapabilityLifecycleResult( operation_id=request.operation_id, Loading @@ -81,6 +85,8 @@ class KubernetesCapabilityAdapter: ) def read_runtime_state(self, request: CapabilityRuntimeStateRequest) -> CapabilityRuntimeState: if not request.external_id: raise ValidationError("read_runtime_state needs the external_id returned by activate") release_name = request.external_id namespace = self._infrastructure.get("namespace", "default") Loading @@ -99,15 +105,20 @@ class KubernetesCapabilityAdapter: ) if result.returncode != 0: log.warning("helm status failed: %s", result.stderr) return CapabilityRuntimeState( external_id=release_name, capability_kind=request.capability_kind, state=RuntimeState.UNKNOWN, details={"stderr": result.stderr}, ) helm_status = json.loads(result.stdout)["info"]["status"] state = RuntimeState.ACTIVE if helm_status == "deployed" else RuntimeState.DEGRADED if helm_status == "deployed": state = RuntimeState.ACTIVE elif helm_status == "failed": state = RuntimeState.FAILED else: state = RuntimeState.DEGRADED return CapabilityRuntimeState( external_id=release_name, Loading @@ -117,6 +128,8 @@ class KubernetesCapabilityAdapter: ) def terminate(self, request: CapabilityLifecycleRequest) -> CapabilityLifecycleResult: if not request.external_id: raise ValidationError("terminate needs the external_id returned by activate") release_name = request.external_id namespace = self._infrastructure.get("namespace", "default") Loading @@ -134,12 +147,8 @@ class KubernetesCapabilityAdapter: ) if result.returncode != 0: return CapabilityLifecycleResult( operation_id=request.operation_id, capability_kind=request.capability_kind, state=LifecycleState.FAILED, details={"stderr": result.stderr}, ) log.error("helm uninstall failed: %s", result.stderr) raise DomainExecutionFailed("helm uninstall failed") return CapabilityLifecycleResult( operation_id=request.operation_id, Loading @@ -150,6 +159,10 @@ class KubernetesCapabilityAdapter: def update(self, request: CapabilityLifecycleRequest) -> CapabilityLifecycleResult: unit = request.deployment_unit if not request.external_id: raise ValidationError("update needs the external_id returned by activate") if unit is None or not unit.artifact_ref: raise ValidationError("update needs a deployment_unit with an artifact_ref") release_name = request.external_id namespace = self._infrastructure.get("namespace", "default") Loading @@ -168,12 +181,8 @@ class KubernetesCapabilityAdapter: ) if result.returncode != 0: return CapabilityLifecycleResult( operation_id=request.operation_id, capability_kind=request.capability_kind, state=LifecycleState.FAILED, details={"stderr": result.stderr}, ) log.error("helm upgrade failed: %s", result.stderr) raise DomainExecutionFailed("helm upgrade failed") return CapabilityLifecycleResult( operation_id=request.operation_id, Loading
src/openop_tfsdk/contract/errors.py +10 −0 Changes for src/openop_tfsdk/contract/errors.py: 10 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -39,3 +39,13 @@ class InvalidTargetRef(TFSDKError): class UnknownAdapterKind(TFSDKError): error_code = "UNKNOWN_ADAPTER_KIND" retryable = False class ValidationError(TFSDKError): error_code = "VALIDATION_ERROR" retryable = False class DomainExecutionFailed(TFSDKError): error_code = "DOMAIN_EXECUTION_FAILED" retryable = False
src/openop_tfsdk/contract/topology.py +2 −4 Changes for src/openop_tfsdk/contract/topology.py: 2 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -16,13 +16,13 @@ from typing import List, Optional, Tuple import yaml from openop_tfsdk.contract.adapters import capability_surface from openop_tfsdk.contract.errors import InvalidTargetRef from openop_tfsdk.contract.interfaces import TopologyDiscoveryAdapter from openop_tfsdk.contract.models import ( Capability, ControlPathBinding, Domain, State, SupportedAction, Zone, ) Loading Loading @@ -111,9 +111,7 @@ class BootstrapTopology(TopologyDiscoveryAdapter): # for its adapter kind and configuration, without building an adapter. control_path = self._control_paths.get(raw["control_path_ref"]) if control_path is None: supported_actions: List[SupportedAction] = [] supported_runtime_kinds: List[str] = [] else: raise InvalidTargetRef(raw["control_path_ref"]) adapter_type, infrastructure = control_path supported_actions, supported_runtime_kinds = capability_surface( adapter_type, capability_kind, infrastructure Loading