Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions harvester_robot_tests/keywords/addon.resource
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
*** Settings ***
Documentation Addon Keywords - Reusable keywords for addon operations
Library ../libs/keywords/addon_keywords.py
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addon.resource file references the ${WAIT_TIMEOUT} variable which is not imported. The file should include 'Resource variables.resource' in the Settings section to access this variable, similar to how other .resource files (like virtualmachine.resource and image.resource) import it.

Suggested change
Library ../libs/keywords/addon_keywords.py
Library ../libs/keywords/addon_keywords.py
Resource variables.resource

Copilot uses AI. Check for mistakes.

*** Keywords ***
Get Addon Initial State
[Arguments] ${addon_name}
[Documentation] Get initial state of addon (enabled/disabled)
... Args:
... addon_name: Name of the addon
... Returns:
... bool: True if enabled, False if disabled
Log Getting initial state of addon: ${addon_name}
${initial_state}= get_addon_initial_state ${addon_name}
Log Addon ${addon_name} initial state: ${initial_state}
[Return] ${initial_state}

Enable Addon
[Arguments] ${addon_name}
[Documentation] Enable an addon
... Args:
... addon_name: Name of the addon to enable
Log Enabling addon: ${addon_name}
enable_addon ${addon_name}
Log Addon ${addon_name} enable request sent

Disable Addon
[Arguments] ${addon_name}
[Documentation] Disable an addon
... Args:
... addon_name: Name of the addon to disable
Log Disabling addon: ${addon_name}
disable_addon ${addon_name}
Log Addon ${addon_name} disable request sent

Wait For Addon Enabled
[Arguments] ${addon_name} ${timeout}=${WAIT_TIMEOUT}
[Documentation] Wait until addon is enabled
... Args:
... addon_name: Name of the addon
... timeout: Maximum time to wait (default: WAIT_TIMEOUT)
Log Waiting for addon ${addon_name} to be enabled (timeout: ${timeout}s)
wait_for_addon_enabled ${addon_name} ${timeout}
Log Addon ${addon_name} is now enabled

Wait For Addon Disabled
[Arguments] ${addon_name} ${timeout}=${WAIT_TIMEOUT}
[Documentation] Wait until addon is disabled
... Args:
... addon_name: Name of the addon
... timeout: Maximum time to wait (default: WAIT_TIMEOUT)
Log Waiting for addon ${addon_name} to be disabled (timeout: ${timeout}s)
wait_for_addon_disabled ${addon_name} ${timeout}
Log Addon ${addon_name} is now disabled

Is Addon Enabled
[Arguments] ${addon_name}
[Documentation] Check if addon is enabled
... Args:
... addon_name: Name of the addon
... Returns:
... bool: True if enabled, False otherwise
${is_enabled}= is_addon_enabled ${addon_name}
[Return] ${is_enabled}

Wait For Monitoring Pods Running
[Arguments] ${namespace} ${timeout}=${WAIT_TIMEOUT}
[Documentation] Wait for monitoring pods (Prometheus, Grafana) to be running
... Args:
... namespace: Kubernetes namespace
... timeout: Maximum time to wait (default: WAIT_TIMEOUT)
Log Waiting for monitoring pods in namespace ${namespace} to be running
wait_for_monitoring_pods_running ${namespace} ${timeout}
Log Monitoring pods are now running

Port Forward To Prometheus
[Arguments] ${namespace} ${pod_name} ${local_port}=9090
[Documentation] Port forward to Prometheus pod
... Args:
... namespace: Kubernetes namespace
... pod_name: Name of the Prometheus pod
... local_port: Local port to forward to (default: 9090)
Log Port forwarding to Prometheus pod ${pod_name} on port ${local_port}
port_forward_to_prometheus ${namespace} ${pod_name} ${local_port}
Log Port forward established

Stop Port Forward
[Documentation] Stop port forwarding
Log Stopping port forward
stop_port_forward
Log Port forward stopped

Query Prometheus
[Arguments] ${query} ${prometheus_url}=http://localhost:9090
[Documentation] Query Prometheus for metrics
... Args:
... query: PromQL query string
... prometheus_url: Prometheus URL (default: http://localhost:9090)
... Returns:
... dict: Query result
Log Querying Prometheus: ${query}
${result}= query_prometheus ${query} ${prometheus_url}
Log Prometheus query successful
[Return] ${result}

Verify Prometheus Metric Exists
[Arguments] ${query} ${prometheus_url}=http://localhost:9090
[Documentation] Verify that a Prometheus metric exists
... Args:
... query: PromQL query string
... prometheus_url: Prometheus URL (default: http://localhost:9090)
... Returns:
... bool: True if metric exists and has data
Log Verifying Prometheus metric: ${query}
${exists}= verify_prometheus_metric_exists ${query} ${prometheus_url}
Should Be True ${exists} Metric ${query} should exist
Log Metric ${query} verified successfully

Restore Addon State
[Arguments] ${addon_name} ${initial_state}
[Documentation] Restore addon to its initial state
... Args:
... addon_name: Name of the addon
... initial_state: Initial state (True for enabled, False for disabled)
Log Restoring addon ${addon_name} to initial state: ${initial_state}
restore_addon_state ${addon_name} ${initial_state}
Log Addon ${addon_name} restored to initial state
7 changes: 7 additions & 0 deletions harvester_robot_tests/libs/addon/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""
Addon module for Harvester addon operations
"""

from addon.addon import Addon

__all__ = ['Addon']
69 changes: 69 additions & 0 deletions harvester_robot_tests/libs/addon/addon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""
Addon Component - delegates to CRD or REST implementation
Layer 4: Selects implementation based on strategy
"""

from constant import HarvesterOperationStrategy
from addon.rest import Rest
from addon.crd import CRD
from addon.base import Base


class Addon(Base):
"""
Addon component that delegates to CRD or REST implementation

The implementation is selected based on:
- HARVESTER_OPERATION_STRATEGY environment variable
- Defaults to 'crd' if not set
"""
Comment on lines +12 to +19
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addon.py module docstring states 'Selects implementation based on strategy' but doesn't document the strategy selection mechanism or how to configure it. Consider adding documentation about the HARVESTER_OPERATION_STRATEGY environment variable (once it's implemented) and the default behavior.

Copilot uses AI. Check for mistakes.

# Set desired operation strategy here
_strategy = HarvesterOperationStrategy.CRD
Comment on lines +16 to +22
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The strategy pattern implementation hardcodes the strategy selection at class level (_strategy = HarvesterOperationStrategy.CRD). According to the docstring, the implementation should be selected based on the HARVESTER_OPERATION_STRATEGY environment variable, but this is not implemented. Consider reading from the environment variable like: '_strategy = HarvesterOperationStrategy(os.getenv("HARVESTER_OPERATION_STRATEGY", "crd"))' to allow runtime configuration, which would match the documented behavior and provide flexibility for different testing scenarios.

Copilot uses AI. Check for mistakes.

def __init__(self):
"""Initialize Addon component"""
if self._strategy == HarvesterOperationStrategy.CRD:
self.addon = CRD()
else:
self.addon = Rest()

def get_addon(self, addon_name):
"""Get addon details - delegates to implementation"""
return self.addon.get_addon(addon_name)

def enable_addon(self, addon_name):
"""Enable an addon - delegates to implementation"""
return self.addon.enable_addon(addon_name)

def disable_addon(self, addon_name):
"""Disable an addon - delegates to implementation"""
return self.addon.disable_addon(addon_name)

def wait_for_addon_enabled(self, addon_name, timeout):
"""Wait for addon to be enabled - delegates to implementation"""
return self.addon.wait_for_addon_enabled(addon_name, timeout)

def wait_for_addon_disabled(self, addon_name, timeout):
"""Wait for addon to be disabled - delegates to implementation"""
return self.addon.wait_for_addon_disabled(addon_name, timeout)

def get_addon_status(self, addon_name):
"""Get addon status - delegates to implementation"""
return self.addon.get_addon_status(addon_name)

def is_addon_enabled(self, addon_name):
"""Check if addon is enabled - delegates to implementation"""
return self.addon.is_addon_enabled(addon_name)

def wait_for_pods_running(self, namespace, label_selector, timeout):
"""Wait for pods to be running - delegates to implementation"""
return self.addon.wait_for_pods_running(namespace, label_selector, timeout)

def port_forward(self, namespace, pod_name, local_port, remote_port):
"""Port forward to a pod - delegates to implementation"""
return self.addon.port_forward(namespace, pod_name, local_port, remote_port)

def stop_port_forward(self):
"""Stop port forwarding - delegates to implementation"""
return self.addon.stop_port_forward()
58 changes: 58 additions & 0 deletions harvester_robot_tests/libs/addon/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
Base class for Addon operations
"""
from abc import ABC, abstractmethod


class Base(ABC):
"""Base class for Addon implementations"""

@abstractmethod
def get_addon(self, addon_name):
"""Get addon details"""
pass

@abstractmethod
def enable_addon(self, addon_name):
"""Enable an addon"""
pass

@abstractmethod
def disable_addon(self, addon_name):
"""Disable an addon"""
pass

@abstractmethod
def wait_for_addon_enabled(self, addon_name, timeout):
"""Wait for addon to be enabled"""
pass

@abstractmethod
def wait_for_addon_disabled(self, addon_name, timeout):
"""Wait for addon to be disabled"""
pass

@abstractmethod
def get_addon_status(self, addon_name):
"""Get addon status"""
pass

@abstractmethod
def is_addon_enabled(self, addon_name):
"""Check if addon is enabled"""
pass

@abstractmethod
def wait_for_pods_running(self, namespace, label_selector, timeout):
"""Wait for pods to be running in a namespace"""
pass

@abstractmethod
def port_forward(self, namespace, pod_name, local_port, remote_port):
"""Port forward to a pod"""
pass

@abstractmethod
def stop_port_forward(self):
"""Stop port forwarding"""
pass
Loading
Loading