blob: a03bdfb1b979d8b34a40f076f6cedd64bf9dcd4c [file] [log] [blame]
# Copyright (C) 2020 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os.path
import subprocess
import yaml
from ._globals import HELM_CHARTS
TEMPLATES = [
"charts/namespace.yaml",
"charts/prometheus",
"charts/loki",
"charts/grafana",
"promtail",
]
HELM_REPOS = {
"stable": "https://kubernetes-charts.storage.googleapis.com",
"loki": "https://grafana.github.io/loki/charts",
}
LOOSE_RESOURCES = [
"namespace.yaml",
"configuration",
"dashboards",
"storage",
]
def _create_dashboard_configmaps(output_dir, namespace):
dashboards_dir = os.path.abspath("./dashboards")
output_dir = os.path.join(output_dir, "dashboards")
if not os.path.exists(output_dir):
os.mkdir(output_dir)
for dashboard in os.listdir(dashboards_dir):
dashboard_path = os.path.join(dashboards_dir, dashboard)
dashboard_name = os.path.splitext(dashboard)[0]
output_file = f"{output_dir}/{dashboard_name}.dashboard.yaml"
command = (
f"kubectl create configmap {dashboard_name} -o yaml "
f"--from-file={dashboard_path} --dry-run=client --namespace={namespace} "
f"> {output_file}"
)
try:
subprocess.check_output(command, shell=True)
except subprocess.CalledProcessError as err:
print(err.output)
with open(output_file, "r") as f:
dashboard_cm = yaml.load(f, Loader=yaml.SafeLoader)
dashboard_cm["metadata"]["labels"] = dict()
dashboard_cm["metadata"]["labels"]["grafana_dashboard"] = dashboard_name
with open(output_file, "w") as f:
yaml.dump(dashboard_cm, f)
def _create_promtail_configs(output_dir):
if not os.path.exists(os.path.join(output_dir, "promtail")):
os.mkdir(os.path.join(output_dir, "promtail"))
with open(os.path.join(output_dir, "promtail.yaml")) as f:
for config in yaml.load_all(f, Loader=yaml.SafeLoader):
with open(
os.path.join(
output_dir,
"promtail",
"promtail-%s"
% config["scrape_configs"][0]["static_configs"][0]["labels"][
"host"
],
),
"w",
) as f:
yaml.dump(config, f)
os.remove(os.path.join(output_dir, "promtail.yaml"))
def _run_ytt(config, output_dir):
config_string = "#@data/values\n---\n"
config_string += yaml.dump(config)
command = [
"ytt",
]
for template in TEMPLATES:
command += ["-f", template]
command += [
"--output-directory",
output_dir,
"--ignore-unknown-comments",
"-f",
"-",
]
try:
# pylint: disable=E1123
print(subprocess.check_output(command, input=config_string, text=True))
except subprocess.CalledProcessError as err:
print(err.output)
def _update_helm_repos():
for repo, url in HELM_REPOS.items():
command = ["helm", "repo", "add", repo, url]
try:
subprocess.check_output(" ".join(command), shell=True)
except subprocess.CalledProcessError as err:
print(err.output)
try:
print(subprocess.check_output(["helm", "repo", "update"]).decode("utf-8"))
except subprocess.CalledProcessError as err:
print(err.output)
def _deploy_loose_resources(output_dir):
for resource in LOOSE_RESOURCES:
command = [
"kubectl",
"apply",
"-f",
f"{output_dir}/{resource}",
]
print(subprocess.check_output(command).decode("utf-8"))
def _get_installed_charts_in_namespace(namespace):
command = ["helm", "ls", "-n", namespace, "--short"]
return subprocess.check_output(command).decode("utf-8").split("\n")
def _install_or_update_charts(output_dir, namespace):
installed_charts = _get_installed_charts_in_namespace(namespace)
charts_path = os.path.abspath("./charts")
for chart, repo in HELM_CHARTS.items():
chart_name = chart + "-" + namespace
with open(f"{charts_path}/{chart}/VERSION", "r") as f:
chart_version = f.readlines()[0].strip()
command = ["helm"]
command.append("upgrade" if chart_name in installed_charts else "install")
command += [
chart_name,
repo,
"--version",
chart_version,
"--values",
f"{output_dir}/{chart}.yaml",
"--namespace",
namespace,
]
try:
print(subprocess.check_output(command).decode("utf-8"))
except subprocess.CalledProcessError as err:
print(err.output)
def install(config_manager, output_dir, dryrun, update_repo):
"""Create the final configuration for the helm charts and Kubernetes resources
and install them to Kubernetes, if not run in --dryrun mode.
Arguments:
config_manager {AbstractConfigManager} -- ConfigManager that contains the
configuration of the monitoring setup to be uninstalled.
output_dir {string} -- Path to the directory where the generated files
should be safed in
dryrun {boolean} -- Whether the installation will be run in dryrun mode
update_repo {boolean} -- Whether to update the helm repositories locally
"""
_run_ytt(config_manager.get_config(), output_dir)
namespace = config_manager.get_config()["namespace"]
_create_dashboard_configmaps(output_dir, namespace)
_create_promtail_configs(output_dir)
if not dryrun:
if update_repo:
_update_helm_repos()
_deploy_loose_resources(output_dir)
_install_or_update_charts(output_dir, namespace)