Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: No such command when running client.py in Docker #104

Open
kking-biometrica opened this issue Jun 5, 2020 · 2 comments
Open

Error: No such command when running client.py in Docker #104

kking-biometrica opened this issue Jun 5, 2020 · 2 comments

Comments

@kking-biometrica
Copy link

kking-biometrica commented Jun 5, 2020

I am unable to generate a config.yml file using the provided tool in the Docker container.

Expected Outcome:

config.yaml generated from an existing Cachet instance when calling client.py

Actual Outcome:

I receive the error Error: No such command "[url]"

Steps to reproduce:

docker pull mtakaki/cachet-url-monitor
docker run -it mtakaki/cachet-url-monitor /bin/sh
python cachet_url_monitor/client.py [url] [key] ./config/config.yml

help produces the following output:

Usage: client.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Resolution:
change line 150 of cachet_url_monitor/client.py to:

    run_client()
@ygwane
Copy link

ygwane commented Jun 8, 2020

Same issue but client.py file contains only 148 lines. Which line does you replace ?

@kking-biometrica
Copy link
Author

kking-biometrica commented Jun 8, 2020

Here's the client.py in my container - it's the last line that calls main. I changed it from calling cli()

#!/usr/bin/env python
from typing import Dict
from typing import Optional

import click
import requests
from yaml import dump
from cachet_url_monitor import latency_unit, status, exceptions


def normalize_url(url: str) -> str:
    """If passed url doesn't include schema return it with default one - http."""
    if not url.lower().startswith("http"):
        return f"http://{url}"
    print(url)
    return url


def save_config(config_map, filename: str):
    with open(filename, "w") as file:
        dump(config_map, file)


class CachetClient(object):
    """Utility class to interact with CahetHQ server."""

    url: str
    token: str
    headers: Dict[str, str]

    def __init__(self, url: str, token: str):
        self.url = normalize_url(url)
        self.token = token
        self.headers = {"X-Cachet-Token": token}

    def get_components(self):
        """Retrieves all components registered in cachet-hq"""
        return requests.get(f"{self.url}/components", headers=self.headers).json()["data"]

    def get_metrics(self):
        """Retrieves all metrics registered in cachet-hq"""
        return requests.get(f"{self.url}/metrics", headers=self.headers).json()["data"]

    def generate_config(self):
        components = self.get_components()
        generated_endpoints = [
            {
                "name": component["name"],
                "url": component["link"],
                "method": "GET",
                "timeout": 1,
                "expectation": [{"type": "HTTP_STATUS", "status_range": "200-300", "incident": "MAJOR"}],
                "allowed_fails": 0,
                "frequency": 30,
                "component_id": component["id"],
                "action": ["CREATE_INCIDENT", "UPDATE_STATUS"],
                "public_incidents": True,
            }
            for component in components
            if component["enabled"]
        ]
        generated_config = {"cachet": {"api_url": self.url, "token": self.token}, "endpoints": generated_endpoints}
        print(generated_config)
        return generated_config

    def get_default_metric_value(self, metric_id):
        """Returns default value for configured metric."""
        get_metric_request = requests.get(f"{self.url}/metrics/{metric_id}", headers=self.headers)

        if get_metric_request.ok:
            return get_metric_request.json()["data"]["default_value"]
        else:
            raise exceptions.MetricNonexistentError(metric_id)

    def get_component_status(self, component_id: int) -> Optional[status.ComponentStatus]:
        """Retrieves the current status of the given component. It will fail if the component does
        not exist or doesn't respond with the expected data.
        :return component status.
        """
        get_status_request = requests.get(f"{self.url}/components/{component_id}", headers=self.headers)

        if get_status_request.ok:
            # The component exists.
            return status.ComponentStatus(int(get_status_request.json()["data"]["status"]))
        else:
            raise exceptions.ComponentNonexistentError(component_id)

    def push_status(self, component_id: int, component_status: status.ComponentStatus):
        """Pushes the status of the component to the cachet server.
        """
        params = {"id": component_id, "status": component_status.value}
        return requests.put(f"{self.url}/components/{component_id}", params=params, headers=self.headers)

    def push_metrics(self, metric_id: int, latency_time_unit: str, elapsed_time_in_seconds: int, timestamp: int):
        """Pushes the total amount of seconds the request took to get a response from the URL.
        """
        value = latency_unit.convert_to_unit(latency_time_unit, elapsed_time_in_seconds)
        params = {"id": metric_id, "value": value, "timestamp": timestamp}
        return requests.post(f"{self.url}/metrics/{metric_id}/points", params=params, headers=self.headers)

    def push_incident(
        self,
        status_value: status.ComponentStatus,
        is_public_incident: bool,
        component_id: int,
        title: str,
        previous_incident_id=None,
        message=None,
    ):
        """If the component status has changed, we create a new incident (if this is the first time it becomes unstable)
        or updates the existing incident once it becomes healthy again.
        """
        if previous_incident_id and status_value == status.ComponentStatus.OPERATIONAL:
            # If the incident already exists, it means it was unhealthy but now it's healthy again, post update
            params = {"status": status.IncidentStatus.FIXED.value, "message": title}

            return requests.post(
                f"{self.url}/incidents/{previous_incident_id}/updates", params=params, headers=self.headers
            )
        elif not previous_incident_id and status_value != status.ComponentStatus.OPERATIONAL:
            # This is the first time the incident is being created.
            params = {
                "name": title,
                "message": message,
                "status": status.IncidentStatus.INVESTIGATING.value,
                "visible": is_public_incident,
                "component_id": component_id,
                "component_status": status_value.value,
                "notify": True,
            }
            return requests.post(f"{self.url}/incidents", params=params, headers=self.headers)


@click.group()
def cli():
    pass


@click.command()
@click.argument("url")
@click.argument("token")
@click.argument("output")
def run_client(url, token, output):
    client = CachetClient(url, token)
    config = client.generate_config()
    save_config(config, output)


if __name__ == "__main__":
    run_client()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants