charm.py 3.94 KiB
Newer Older
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
# Copyright © 2020 Dominik Fleischmann dominik.fleischmann@canonical.com

"""Operator Charm main library."""
# Load modules from lib directory
import logging

import setuppath  # noqa:F401
from ops.charm import CharmBase
from ops.framework import StoredState
from ops.main import main
from ops.model import ActiveStatus, MaintenanceStatus
import subprocess


class SquidK8SCharm(CharmBase):
    """Class reprisenting this Operator charm."""

    state = StoredState()

    def __init__(self, *args):
        """Initialize charm and configure states and events to observe."""
        super().__init__(*args)
        # -- standard hook observation
        self.framework.observe(self.on.install, self.on_install)
        self.framework.observe(self.on.start, self.on_start)
        self.framework.observe(self.on.config_changed, self.on_config_changed)
        self.framework.observe(self.on.deleteurl_action, self)
        # -- initialize states --
        self.state.set_default(installed=False)
        self.state.set_default(configured=False)
        self.state.set_default(started=False)

    def make_pod_spec(self):
        config = self.framework.model.config
        ports = [{"name": "squid", "containerPort": config["port"], "protocol": "TCP"}]

        spec = {
            "containers": [{
                "name": self.framework.model.app.name,
                "image": config["image"],
                "ports": ports,
            }],
        }

        return spec

    def _apply_spec(self, spec):
        # Only apply the spec if this unit is a leader
        if self.framework.model.unit.is_leader():
            self.framework.model.pod.set_spec(spec)
            self.state.spec = spec

    def on_install(self, event):
        """Handle install state."""
        self.unit.status = MaintenanceStatus("Installing charm software")
        # Perform install tasks
        self.unit.status = MaintenanceStatus("Install complete")
        logging.info("Install of software complete")
        self.state.installed = True

    def on_config_changed(self, event):
        """Handle config changed."""

        if not self.state.installed:
            logging.warning("Config changed called before install complete, deferring event: {}.".format(event.handle))

            return

        if self.state.started:
            # Stop if necessary for reconfig
            logging.info("Stopping for configuration, event handle: {}".format(event.handle))
        # Configure the software
        logging.info("Configuring")
        self.state.configured = True

    def on_start(self, event):
        """Handle start state."""

        if not self.state.configured:
            logging.warning("Start called before configuration complete, deferring event: {}".format(event.handle))

            return
        self.unit.status = MaintenanceStatus("Applying pod spec")
        # Start software
        new_pod_spec = self.make_pod_spec()
        self._apply_spec(new_pod_spec)

        self.unit.status = ActiveStatus("Unit is ready")
        self.state.started = True
        logging.info("Started")

    def on_deleteurl_action(self, event):
        """Handle the deleteurl action."""
        url = event.params["url"]

        line_to_delete = "acl allowedurls dstdomain .{}".format(url)
        line_deleted = False

        with open("/etc/squid/squid.conf", "r") as f:
            lines = f.readlines()
        with open("/etc/squid/squid.conf", "w") as f:
            for line in lines:
                if line_to_delete not in line:
                    f.write(line)
                else:
                    line_deleted = True

        if line_deleted:
            event.set_results({"output": "URL deleted succesfully"})
            subprocess.Popen("sleep 1 && kill -HUP `cat /var/run/squid.pid`", shell=True)
        else:
            event.fail("No URL was deleted")


if __name__ == "__main__":
    from ops.main import main
    main(SquidK8SCharm)