con un clic
adding-config
Adding and validating charm configuration options
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
Adding and validating charm configuration options
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
Known-good Juju bundle shapes (COS Lite, 12-Factor + COS, Identity Platform, Charmed Kubeflow) — canonical app lists and relation edges
Implementing Juju actions for operational tasks in charms. WHEN: add Juju actions to a charm, implement backup / rotate-credentials / restore actions, declare actions in charmcraft.yaml, write action handlers, test actions with Scenario, run actions with juju run.
Adding and validating charm configuration options. WHEN: add charm configuration options, declare config in charmcraft.yaml, validate config values in config-changed, apply config to a Pebble layer, apply config to a machine charm, write Scenario tests for config.
End-to-end workflow for building ops-framework charms for custom applications (K8s and machine). WHEN: build a custom Juju charm, write src/charm.py with Pebble, write a machine charm with systemd, decide K8s vs machine substrate, scaffold a non-paas-charm with charmcraft init, customise the ops framework charm lifecycle.
Workflow for charming infrastructure software (databases, caches, message brokers, proxies, monitoring). WHEN: charm infrastructure software, charm a database / cache / message broker / proxy / monitoring system, implement peer relations, leader election, primary/replica failover, backup and restore actions, clustering.
Expert guidance for developing, building, testing, and publishing Juju charms using charmcraft. WHEN: edit charmcraft.yaml, run charmcraft pack, run charmcraft init, manage charm libraries, publish a charm to Charmhub, set up multi-base builds, configure relations / config options, set up storage or containers.
| name | adding-config |
| description | Adding and validating charm configuration options |
| globs | ["config.yaml","charmcraft.yaml","metadata.yaml","src/charm.py","src/**/charm.py"] |
Charm configuration lets operators customise behaviour without modifying code. Config values are set with juju config and delivered to the charm via config-changed events.
charmcraft.yaml with types, defaults, and descriptionsconfig-changed fires whenever any config value changesself.config any timeconfig-changed and set BlockedStatus if invalidIn charmcraft.yaml:
config:
options:
log-level:
type: string
default: info
description: |
Logging verbosity. One of: debug, info, warning, error.
port:
type: int
default: 8080
description: |
Port the workload listens on.
enable-tls:
type: boolean
default: false
description: |
Enable TLS for the workload.
max-connections:
type: int
default: 100
description: |
Maximum number of concurrent connections.
| Type | Juju Type | Python Type | Example |
|---|---|---|---|
| String | string | str | "info" |
| Integer | int | int | 8080 |
| Float | float | float | 0.5 |
| Boolean | boolean | bool | true |
import ops
VALID_LOG_LEVELS = {"debug", "info", "warning", "error"}
class MyCharm(ops.CharmBase):
def __init__(self, framework: ops.Framework):
super().__init__(framework)
self.framework.observe(self.on.config_changed, self._on_config_changed)
def _on_config_changed(self, event: ops.ConfigChangedEvent):
log_level = self.config.get("log-level", "info")
if log_level not in VALID_LOG_LEVELS:
self.unit.status = ops.BlockedStatus(
f"Invalid log-level: {log_level!r}. "
f"Must be one of: {', '.join(sorted(VALID_LOG_LEVELS))}"
)
return
port = self.config["port"]
if not (1 <= port <= 65535):
self.unit.status = ops.BlockedStatus(
f"Invalid port: {port}. Must be between 1 and 65535."
)
return
# Config is valid — apply it.
self._apply_config()
self.unit.status = ops.ActiveStatus()
def _apply_config(self):
"""Apply current config to the workload."""
# For K8s charms, update the Pebble layer.
# For machine charms, update config files and restart services.
...
def _apply_config(self):
container = self.unit.get_container("workload")
if not container.can_connect():
return
layer = {
"services": {
"workload": {
"override": "replace",
"command": "/app/start",
"environment": {
"LOG_LEVEL": self.config["log-level"],
"PORT": str(self.config["port"]),
"ENABLE_TLS": str(self.config["enable-tls"]).lower(),
"MAX_CONNECTIONS": str(self.config["max-connections"]),
},
},
},
}
container.add_layer("workload", layer, combine=True)
container.replan()
def _apply_config(self):
import subprocess
config_content = self._render_config_file()
config_path = "/etc/myapp/config.yaml"
# Write the config file.
with open(config_path, "w") as f:
f.write(config_content)
# Restart the service.
subprocess.check_call(["systemctl", "restart", "myapp"])
Config is always available via self.config, not just in config-changed:
def _on_database_changed(self, event: ops.RelationChangedEvent):
port = self.config["port"]
# Use port when configuring the database connection.
...
from ops import testing
def test_valid_config():
state = testing.State(config={"log-level": "debug", "port": 9090})
ctx = testing.Context(MyCharm)
out = ctx.run(ctx.on.config_changed(), state)
assert out.unit_status == testing.ActiveStatus()
def test_invalid_log_level():
state = testing.State(config={"log-level": "verbose"})
ctx = testing.Context(MyCharm)
out = ctx.run(ctx.on.config_changed(), state)
assert isinstance(out.unit_status, testing.BlockedStatus)
assert "Invalid log-level" in out.unit_status.message
def test_invalid_port():
state = testing.State(config={"port": 0})
ctx = testing.Context(MyCharm)
out = ctx.run(ctx.on.config_changed(), state)
assert isinstance(out.unit_status, testing.BlockedStatus)
# Set a single option.
juju config my-charm log-level=debug
# Set multiple options.
juju config my-charm log-level=debug port=9090
# Reset to default.
juju config my-charm --reset log-level
# View current config.
juju config my-charm
Validate all config values. Set BlockedStatus with a clear message if any value is invalid. Never silently ignore bad config.
Use sensible defaults. The charm should work out of the box with default config values. Operators should only need to change config for customisation.
Config names use hyphens (e.g., log-level, max-connections). This is the Juju convention. Access them in Python with self.config["log-level"].
Keep config options stable. Renaming or removing config options breaks existing deployments. Add new options freely; deprecate old ones gradually.
Document each option clearly. The description field in charmcraft.yaml is what operators see when running juju config my-charm --all. Write it for a human audience.
Apply config idempotently. config-changed fires on every juju config call, even if the value did not actually change. The handler should apply config without side effects if nothing changed.
BlockedStatus. charmlint covers both deterministically (CFG004 flags any option that does not appear as self.config[...] / self.config.get(...) in src/; CFG005 flags charms with config options but no BlockedStatus reference at all). Run charmlint to surface these — the skill body no longer recites them.config-changed at all — config values are still accessible via self.config, but the workload will not be updated until the charm explicitly applies them.BlockedStatus instead of raising exceptions.replan() after updating a Pebble layer — the service will not restart with the new config until replan() is called.