Skip to content

Commit

Permalink
Remove legacy slave integration (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
arturo-seijas authored Jan 30, 2024
1 parent c5ac369 commit a443cfc
Show file tree
Hide file tree
Showing 12 changed files with 35 additions and 412 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/integration_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ jobs:
./tests/integration/pre_run_script.sh"
extra-arguments: |
--kube-config ${GITHUB_WORKSPACE}/kube-config
modules: '["test_agent_k8s.py", "test_agent_machine.py"]'
modules: '["test_agent_k8s.py"]'
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ by creating and accepting our cross-model relation. We do this from within the
k8s model:

```bash
juju offer jenkins-agent-k8s:slave
juju offer jenkins-agent-k8s:agent
# The output will be something like:
# Application "jenkins-agent" endpoints [slave] available at "admin/jenkins-agent-k8s.jenkins-agent"
# Application "jenkins-agent" endpoints [agent] available at "admin/jenkins-agent-k8s.jenkins-agent"
```

Switch back to your IaaS model where you deployed jenkins and run:
Expand Down
2 changes: 0 additions & 2 deletions metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,5 @@ resources:
type: oci-image
description: OCI image for Jenkins k8s agent
provides:
slave:
interface: jenkins-slave
agent:
interface: jenkins_agent_v0
108 changes: 1 addition & 107 deletions src/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import pebble
import server
from state import AGENT_RELATION, SLAVE_RELATION, State
from state import AGENT_RELATION, State

logger = logging.getLogger()

Expand All @@ -30,15 +30,6 @@ def __init__(self, charm: ops.CharmBase, state: State, pebble_service: pebble.Pe
self.state = state
self.pebble_service = pebble_service

charm.framework.observe(
charm.on[SLAVE_RELATION].relation_joined, self._on_slave_relation_joined
)
charm.framework.observe(
charm.on[SLAVE_RELATION].relation_changed, self._on_slave_relation_changed
)
charm.framework.observe(
charm.on[SLAVE_RELATION].relation_departed, self._on_slave_relation_departed
)
charm.framework.observe(
charm.on[AGENT_RELATION].relation_joined, self._on_agent_relation_joined
)
Expand All @@ -49,27 +40,6 @@ def __init__(self, charm: ops.CharmBase, state: State, pebble_service: pebble.Pe
charm.on[AGENT_RELATION].relation_departed, self._on_agent_relation_departed
)

def _on_slave_relation_joined(self, event: ops.RelationJoinedEvent) -> None:
"""Handle slave relation joined event.
Args:
event: The event fired when an agent has joined the "slave" relation.
"""
if self.state.jenkins_config:
logger.warning(
"Jenkins configuration already exists. Ignoring %s relation.", event.relation.name
)
return

logger.info("%s relation joined.", event.relation.name)
self.charm.unit.status = ops.MaintenanceStatus(
f"Setting up '{event.relation.name}' relation."
)

relation_data = self.state.agent_meta.get_jenkins_slave_interface_dict()
logger.debug("Slave relation data set: %s", relation_data)
event.relation.data[self.charm.unit].update(relation_data)

def _on_agent_relation_joined(self, event: ops.RelationJoinedEvent) -> None:
"""Handle agent relation joined event.
Expand All @@ -91,73 +61,6 @@ def _on_agent_relation_joined(self, event: ops.RelationJoinedEvent) -> None:
logger.debug("Agent relation data set: %s", relation_data)
event.relation.data[self.charm.unit].update(relation_data)

def _on_slave_relation_changed(self, event: ops.RelationChangedEvent) -> None:
"""Handle slave relation changed event.
Args:
event: The event fired when slave relation data has changed.
Raises:
AgentJarDownloadError: if the Jenkins agent failed to download.
"""
logger.info("%s relation changed.", event.relation.name)

if self.state.jenkins_config or self.state.agent_relation_credentials:
logger.warning(
"Jenkins configuration already exists. Ignoring %s relation.", event.relation.name
)
return

container = self.charm.unit.get_container(self.state.jenkins_agent_service_name)
if not container.can_connect():
logger.warning("Jenkins agent container not yet ready. Deferring.")
event.defer()
return

# Check if the pebble service has started and set agent ready.
if container.exists(str(server.AGENT_READY_PATH)):
logger.warning("Given agent already registered. Skipping.")
return

if not self.state.slave_relation_credentials:
self.charm.unit.status = ops.WaitingStatus("Waiting for complete relation data.")
logger.info("Waiting for complete relation data.")
# The event needs to be retried after the agent credentials have been set.
event.defer()
return

self.charm.unit.status = ops.MaintenanceStatus("Downloading Jenkins agent executable.")
try:
server.download_jenkins_agent(
server_url=self.state.slave_relation_credentials.address, container=container
)
except server.AgentJarDownloadError as exc:
logger.error("Failed to download Jenkins agent executable, %s", exc)
raise

self.charm.unit.status = ops.MaintenanceStatus("Validating credentials.")
if not server.validate_credentials(
agent_name=self.state.agent_meta.name,
credentials=self.state.slave_relation_credentials,
container=container,
add_random_delay=True,
):
# The jenkins server sets credentials one by one, if some other agent unit in the
# relation took this credential first, it the agent unit should wait for the next
# secret update and try grabbing that.
logger.warning(
"Failed credential for agent %s, will wait for next credentials to be set",
self.state.agent_meta.name,
)
self.charm.unit.status = ops.WaitingStatus("Waiting for credentials.")
return

self.start_agent_from_relation(
container=container,
credentials=self.state.slave_relation_credentials,
agent_name=self.state.agent_meta.name,
)

def _on_agent_relation_changed(self, event: ops.RelationChangedEvent) -> None:
"""Handle agent relation changed event.
Expand Down Expand Up @@ -227,15 +130,6 @@ def start_agent_from_relation(
)
self.charm.unit.status = ops.ActiveStatus()

def _on_slave_relation_departed(self, _: ops.RelationDepartedEvent) -> None:
"""Handle slave relation departed event."""
container = self.charm.unit.get_container(self.state.jenkins_agent_service_name)
if not container.can_connect():
logger.warning("Relation departed before service ready.")
return
self.pebble_service.stop_agent(container=container)
self.charm.unit.status = ops.BlockedStatus("Waiting for config/relation.")

def _on_agent_relation_departed(self, _: ops.RelationDepartedEvent) -> None:
"""Handle agent relation departed event."""
container = self.charm.unit.get_container(self.state.jenkins_agent_service_name)
Expand Down
14 changes: 2 additions & 12 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import agent
import pebble
import server
from state import AGENT_RELATION, SLAVE_RELATION, InvalidStateError, State
from state import AGENT_RELATION, InvalidStateError, State

logger = logging.getLogger()

Expand Down Expand Up @@ -62,21 +62,11 @@ def _register_via_config(
event.defer()
return

if (
not self.state.jenkins_config
and not self.model.get_relation(SLAVE_RELATION)
and not self.model.get_relation(AGENT_RELATION)
):
if not self.state.jenkins_config and not self.model.get_relation(AGENT_RELATION):
self.model.unit.status = ops.BlockedStatus("Waiting for config/relation.")
return

if not self.state.jenkins_config:
if self.model.get_relation(SLAVE_RELATION):
self.model.unit.status = ops.BlockedStatus(
"Please remove and re-relate slave relation."
)
return
# Support fallback relation to AGENT_RELATION.
self.model.unit.status = ops.BlockedStatus(
"Please remove and re-relate agent relation."
)
Expand Down
12 changes: 0 additions & 12 deletions src/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,6 @@ class Agent(BaseModel):
labels: str
name: str

def get_jenkins_slave_interface_dict(self) -> typing.Dict[str, str]:
"""Generate dictionary representation of agent metadata.
Returns:
A dictionary adhering to jenkins-slave interface.
"""
return {
"executors": str(self.num_executors),
"labels": self.labels,
"slavehost": self.name,
}

def get_jenkins_agent_v0_interface_dict(self) -> typing.Dict[str, str]:
"""Generate dictionary representation of agent metadata.
Expand Down
32 changes: 0 additions & 32 deletions src/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
import metadata
import server

# relation name used for compatibility with machine Jenkins server charm.
SLAVE_RELATION = "slave"
# agent relation name
AGENT_RELATION = "agent"

Expand Down Expand Up @@ -103,24 +101,6 @@ def _get_jenkins_unit(
return None


def _get_credentials_from_slave_relation(
server_unit_databag: ops.RelationDataContent,
) -> typing.Optional[server.Credentials]:
"""Import server metadata from databag in slave relation.
Args:
server_unit_databag: The relation databag content from slave relation.
Returns:
Metadata if complete values(url, secret) are set. None otherwise.
"""
address = server_unit_databag.get("url")
secret = server_unit_databag.get("secret")
if not address or not secret:
return None
return server.Credentials(address=address, secret=secret)


def _get_credentials_from_agent_relation(
server_unit_databag: ops.RelationDataContent, unit_name: str
) -> typing.Optional[server.Credentials]:
Expand All @@ -147,16 +127,13 @@ class State:
Attrs:
agent_meta: The Jenkins agent metadata to register on Jenkins server.
jenkins_config: Jenkins configuration value from juju config.
slave_relation_credentials: The full set of credentials from the slave relation. None if
partial data is set.
agent_relation_credentials: The full set of credentials from the agent relation. None if
partial data is set or the credentials do not belong to current agent.
jenkins_agent_service_name: The Jenkins agent workload container name.
"""

agent_meta: metadata.Agent
jenkins_config: typing.Optional[JenkinsConfig]
slave_relation_credentials: typing.Optional[server.Credentials]
agent_relation_credentials: typing.Optional[server.Credentials]
jenkins_agent_service_name: str = "jenkins-k8s-agent"

Expand Down Expand Up @@ -189,14 +166,6 @@ def from_charm(cls, charm: ops.CharmBase) -> "State":
logging.error("Invalid jenkins config values, %s", exc)
raise InvalidStateError("Invalid jenkins config values.") from exc

slave_relation = charm.model.get_relation(SLAVE_RELATION)
slave_relation_credentials: typing.Optional[server.Credentials] = None
if slave_relation and (
slave_relation_jenkins_unit := _get_jenkins_unit(slave_relation.units, charm.app.name)
):
slave_relation_credentials = _get_credentials_from_slave_relation(
slave_relation.data[slave_relation_jenkins_unit]
)
agent_relation = charm.model.get_relation(AGENT_RELATION)
agent_relation_credentials: typing.Optional[server.Credentials] = None
if agent_relation and (
Expand All @@ -209,6 +178,5 @@ def from_charm(cls, charm: ops.CharmBase) -> "State":
return cls(
agent_meta=agent_meta,
jenkins_config=jenkins_config,
slave_relation_credentials=slave_relation_credentials,
agent_relation_credentials=agent_relation_credentials,
)
6 changes: 3 additions & 3 deletions tests/integration/test_agent_k8s.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ def containers_ready() -> bool:
pod_status: kubernetes.client.V1PodStatus = kube_core_client.read_namespaced_pod_status(
name=pod_name, namespace=model.name
).status
container_statuses: list[
kubernetes.client.V1ContainerStatus
] = pod_status.container_statuses
container_statuses: list[kubernetes.client.V1ContainerStatus] = (
pod_status.container_statuses
)
return all(status.ready for status in container_statuses)

await wait_for(containers_ready, timeout=60 * 10)
Expand Down
45 changes: 0 additions & 45 deletions tests/integration/test_agent_machine.py

This file was deleted.

Loading

0 comments on commit a443cfc

Please sign in to comment.