Skip to content

Commit

Permalink
Alter the home of ceph conf to not interfere with other applications
Browse files Browse the repository at this point in the history
  • Loading branch information
addyess committed Sep 3, 2024
1 parent 70b81d9 commit b764968
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 54 deletions.
1 change: 1 addition & 0 deletions .charmcraft-channel
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.x/stable
8 changes: 6 additions & 2 deletions .github/workflows/vsphere-integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ jobs:
timeout-minutes: 120
steps:
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Read charmcraft version file
id: charmcraft
run: echo "channel=$(cat .charmcraft-channel)" >> $GITHUB_OUTPUT
- name: Setup Python
uses: actions/setup-python@v4
with:
Expand All @@ -21,7 +24,8 @@ jobs:
clouds-yaml: ${{ secrets.CLOUDS_YAML }}
bootstrap-constraints: "arch=amd64 cores=2 mem=4G"
bootstrap-options: "${{ secrets.JAMMY_BOOTSTRAP_OPTIONS }} --model-default datastore=vsanDatastore --model-default primary-network=VLAN_2763 --config caas-image-repo=rocks.canonical.com/cdk/jujusolutions"
juju-channel: "3.1/stable"
juju-channel: "3/stable"
charmcraft-channel: "${{ steps.charmcraft.outputs.channel }}"

- name: Run test
run: tox -e func -- --basetemp=/home/ubuntu/pytest
Expand Down
25 changes: 19 additions & 6 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import json
import logging
import subprocess
from functools import cached_property, wraps
from functools import cached_property, lru_cache, wraps
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, cast

Expand All @@ -31,6 +31,18 @@
logger = logging.getLogger(__name__)


def ceph_config_dir() -> Path:
return Path.cwd() / "ceph-conf"


def ceph_config_file() -> Path:
return ceph_config_dir() / "ceph.conf"


def ceph_keyring_file(app_name: str) -> Path:
return ceph_config_dir() / f"ceph.client.{app_name}.keyring"


def needs_leader(func: Callable) -> Callable:
"""Ensure that function with this decorator is executed only if on leader units."""

Expand Down Expand Up @@ -216,7 +228,7 @@ def write_ceph_cli_config(self) -> None:
"auth cluster required": self.auth or "",
"auth service required": self.auth or "",
"auth client required": self.auth or "",
"keyring": "/etc/ceph/$cluster.$name.keyring",
"keyring": f"{ceph_config_dir()}/$cluster.$name.keyring",
"mon host": " ".join(self.mon_hosts),
"log to syslog": "true",
"err to syslog": "true",
Expand All @@ -227,27 +239,28 @@ def write_ceph_cli_config(self) -> None:
}
config["client"] = {"log file": "/var/log/ceph.log"}

with Path("/etc/ceph/ceph.conf").open("w") as fp:
with ceph_config_file().open("w") as fp:
config.write(fp)

def write_ceph_cli_keyring(self) -> None:
"""Write Ceph CLI keyring file"""
config = configparser.ConfigParser()
config[f"client.{self.app.name}"] = {"key": self.key or ""}
with Path(f"/etc/ceph/ceph.client.{self.app.name}.keyring").open("w") as fp:
with ceph_keyring_file(self.app.name).open("w") as fp:
config.write(fp)

def configure_ceph_cli(self) -> None:
"""Configure Ceph CLI"""
Path("/etc/ceph").mkdir(parents=True, exist_ok=True)
ceph_config_dir().mkdir(mode=0o700, parents=True, exist_ok=True)
self.write_ceph_cli_config()
self.write_ceph_cli_keyring()

def ceph_cli(self, *args: str, timeout: int = 60) -> str:
"""Run Ceph CLI command"""
cmd = ["ceph", "--user", self.app.name] + list(args)
cmd = ["/usr/bin/ceph", "--conf", str(ceph_config_file()), "--user", self.app.name, *args]
return subprocess.check_output(cmd, timeout=timeout).decode("UTF-8")

@lru_cache(maxsize=None)
def get_ceph_fsid(self) -> str:
"""Get the Ceph FSID (cluster ID)"""
try:
Expand Down
100 changes: 54 additions & 46 deletions tests/unit/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,74 +29,78 @@ def harness():
harness.cleanup()


@pytest.fixture(autouse=True)
def ceph_conf_directory(tmp_path):
with mock.patch("charm.ceph_config_dir") as mock_path:
mock_path.return_value = tmp_path / "ceph-conf"
yield mock_path


@pytest.fixture(autouse=True)
def mock_apt():
with mock.patch("charms.operator_libs_linux.v0.apt.add_package", autospec=True) as mock_apt:
yield mock_apt


def test_write_ceph_cli_config(harness):
@mock.patch("charm.ceph_config_file")
def test_write_ceph_cli_config(ceph_config_file, harness, ceph_conf_directory):
"""Test writing of Ceph CLI config"""
harness.begin()
harness.charm.stored.ceph_data["auth"] = "cephx"
harness.charm.stored.ceph_data["mon_hosts"] = ["10.0.0.1", "10.0.0.2"]
with mock.patch("charm.Path") as mock_path:
harness.charm.write_ceph_cli_config()
mock_path.assert_called_once_with("/etc/ceph/ceph.conf")
path = mock_path.return_value
path.open.assert_called_once_with("w")
with path.open() as fp:
lines = [
"[global]",
"auth cluster required = cephx",
"auth service required = cephx",
"auth client required = cephx",
"keyring = /etc/ceph/$cluster.$name.keyring",
"mon host = 10.0.0.1 10.0.0.2",
"log to syslog = true",
"err to syslog = true",
"clog to syslog = true",
"mon cluster log to syslog = true",
"debug mon = 1/5",
"debug osd = 1/5",
"",
"[client]",
"log file = /var/log/ceph.log",
"",
]
fp.write.assert_has_calls([mock.call(_l + "\n") for _l in lines])
harness.charm.write_ceph_cli_config()
path = ceph_config_file.return_value
path.open.assert_called_once_with("w")
with path.open() as fp:
lines = [
"[global]",
"auth cluster required = cephx",
"auth service required = cephx",
"auth client required = cephx",
f"keyring = {ceph_conf_directory.return_value}/$cluster.$name.keyring",
"mon host = 10.0.0.1 10.0.0.2",
"log to syslog = true",
"err to syslog = true",
"clog to syslog = true",
"mon cluster log to syslog = true",
"debug mon = 1/5",
"debug osd = 1/5",
"",
"[client]",
"log file = /var/log/ceph.log",
"",
]
fp.write.assert_has_calls([mock.call(_l + "\n") for _l in lines])


def test_write_ceph_cli_keyring(harness):
@mock.patch("charm.ceph_keyring_file")
def test_write_ceph_cli_keyring(ceph_keyring_file, harness):
"""Test writing of Ceph CLI keyring file"""
harness.begin()
harness.charm.stored.ceph_data["key"] = "12345"
with mock.patch("charm.Path") as mock_path:
harness.charm.write_ceph_cli_keyring()
mock_path.assert_called_once_with("/etc/ceph/ceph.client.ceph-csi.keyring")
path = mock_path.return_value
path.open.assert_called_once_with("w")
with path.open() as fp:
lines = ["[client.ceph-csi]", "key = 12345", ""]
fp.write.assert_has_calls([mock.call(_l + "\n") for _l in lines])
harness.charm.write_ceph_cli_keyring()
ceph_keyring_file.return_value.open.assert_called_once_with("w")
with ceph_keyring_file.return_value.open() as fp:
lines = ["[client.ceph-csi]", "key = 12345", ""]
fp.write.assert_has_calls([mock.call(_l + "\n") for _l in lines])


@mock.patch("charm.CephCsiCharm.write_ceph_cli_config")
@mock.patch("charm.CephCsiCharm.write_ceph_cli_keyring")
def test_configure_ceph_cli(keyring, config, harness):
@mock.patch("charm.ceph_config_dir")
def test_configure_ceph_cli(ceph_config_dir, keyring, config, harness):
"""Test configuration of Ceph CLI"""
harness.begin()
with mock.patch("charm.Path") as mock_path:
harness.charm.configure_ceph_cli()
mock_path.assert_called_once_with("/etc/ceph")
path = mock_path.return_value
path.mkdir.assert_called_once_with(parents=True, exist_ok=True)
config.assert_called_once()
keyring.assert_called_once()
harness.charm.configure_ceph_cli()
ceph_config_dir.return_value.mkdir.assert_called_once_with(
mode=0o700, parents=True, exist_ok=True
)
config.assert_called_once()
keyring.assert_called_once()


@mock.patch("subprocess.check_output")
def test_ceph_context_getter(check_output, harness):
def test_ceph_context_getter(check_output, harness, ceph_conf_directory):
"""Test that ceph_context property returns properly formatted data."""
fsid = "12345"
key = "secret_key"
Expand All @@ -122,9 +126,13 @@ def test_ceph_context_getter(check_output, harness):
}

assert harness.charm.ceph_context == expected_context
check_output.assert_any_call(["ceph", "--user", "ceph-csi", "fsid"], timeout=60)
conf_file = str(ceph_conf_directory() / "ceph.conf")
check_output.assert_any_call(
["/usr/bin/ceph", "--conf", conf_file, "--user", "ceph-csi", "fsid"], timeout=60
)
check_output.assert_any_call(
["ceph", "--user", "ceph-csi", "fs", "ls", "-f", "json"], timeout=60
["/usr/bin/ceph", "--conf", conf_file, "--user", "ceph-csi", "fs", "ls", "-f", "json"],
timeout=60,
)


Expand Down

0 comments on commit b764968

Please sign in to comment.