summaryrefslogtreecommitdiffstats
path: root/integrations/gen_docs_integrations.py
diff options
context:
space:
mode:
Diffstat (limited to 'integrations/gen_docs_integrations.py')
-rw-r--r--integrations/gen_docs_integrations.py386
1 files changed, 386 insertions, 0 deletions
diff --git a/integrations/gen_docs_integrations.py b/integrations/gen_docs_integrations.py
new file mode 100644
index 000000000..d8006dd2a
--- /dev/null
+++ b/integrations/gen_docs_integrations.py
@@ -0,0 +1,386 @@
+import json
+import shutil
+from pathlib import Path
+import re
+
+# Dictionary responsible for making the symbolic links at the end of the script's run.
+symlink_dict = {}
+am_i_inside_go = "go.d.plugin" in str(Path.cwd())
+
+
+def cleanup():
+ """
+ clean directories that are either data collection or exporting integrations
+ """
+ if am_i_inside_go:
+ for element in Path("modules").glob('**/*/'):
+ if "integrations" in str(element):
+ shutil.rmtree(element)
+ else:
+ for element in Path("collectors").glob('**/*/'):
+ if "integrations" in str(element):
+ shutil.rmtree(element)
+
+ for element in Path("exporting").glob('**/*/'):
+ if "integrations" in str(element):
+ shutil.rmtree(element)
+ for element in Path("integrations/cloud-notifications").glob('**/*/'):
+ if "integrations" in str(element) and not "metadata.yaml" in str(element):
+ shutil.rmtree(element)
+
+
+def generate_category_from_name(category_fragment, category_array):
+ """
+ Takes a category ID in splitted form ("." as delimiter) and the array of the categories, and returns the proper category name that Learn expects.
+ """
+
+ category_name = ""
+ i = 0
+ dummy_id = category_fragment[0]
+
+ while i < len(category_fragment):
+ for category in category_array:
+
+ if dummy_id == category['id']:
+ category_name = category_name + "/" + category["name"]
+ try:
+ # print("equals")
+ # print(fragment, category_fragment[i+1])
+ dummy_id = dummy_id + "." + category_fragment[i+1]
+ # print(dummy_id)
+ except IndexError:
+ return category_name.split("/", 1)[1]
+ category_array = category['children']
+ break
+ i += 1
+
+
+def clean_and_write(md, path):
+ """
+ This function takes care of the special details element, and converts it to the equivalent that md expects.
+ Then it writes the buffer on the file provided.
+ """
+ # clean first, replace
+ md = md.replace("{% details summary=\"", "<details><summary>").replace(
+ "\" %}", "</summary>\n").replace("{% /details %}", "</details>\n")
+
+ path.write_text(md)
+
+
+def add_custom_edit_url(markdown_string, meta_yaml_link, sidebar_label_string, mode='default'):
+ """
+ Takes a markdown string and adds a "custom_edit_url" metadata to the metadata field
+ """
+
+ output = ""
+
+ if mode == 'default':
+ path_to_md_file = f'{meta_yaml_link.replace("/metadata.yaml", "")}/integrations/{clean_string(sidebar_label_string)}'
+
+ elif mode == 'cloud-notifications':
+ path_to_md_file = meta_yaml_link.replace("metadata.yaml", f'integrations/{clean_string(sidebar_label_string)}')
+
+ elif mode == 'agent-notifications':
+ path_to_md_file = meta_yaml_link.replace("metadata.yaml", "README")
+
+ output = markdown_string.replace(
+ "<!--startmeta",
+ f'<!--startmeta\ncustom_edit_url: \"{path_to_md_file}.md\"')
+
+ return output
+
+
+def clean_string(string):
+ """
+ simple function to get rid of caps, spaces, slashes and parentheses from a given string
+
+ The string represents an integration name, as it would be displayed in the final text
+ """
+
+ return string.lower().replace(" ", "_").replace("/", "-").replace("(", "").replace(")", "")
+
+
+def read_integrations_js(path_to_file):
+ """
+ Open integrations/integrations.js and extract the dictionaries
+ """
+
+ try:
+ data = Path(path_to_file).read_text()
+
+ categories_str = data.split("export const categories = ")[1].split("export const integrations = ")[0]
+ integrations_str = data.split("export const categories = ")[1].split("export const integrations = ")[1]
+
+ return json.loads(categories_str), json.loads(integrations_str)
+
+ except FileNotFoundError as e:
+ print("Exception", e)
+
+
+def create_overview(integration, filename):
+
+ split = re.split(r'(#.*\n)', integration['overview'], 1)
+
+ first_overview_part = split[1]
+ rest_overview_part = split[2]
+
+ if len(filename) > 0:
+ return f"""{first_overview_part}
+
+<img src="https://netdata.cloud/img/{filename}" width="150"/>
+
+{rest_overview_part}
+"""
+ else:
+ return f"""{first_overview_part}{rest_overview_part}
+"""
+
+
+def build_readme_from_integration(integration, mode=''):
+ # COLLECTORS
+ if mode == 'collector':
+
+ try:
+ # initiate the variables for the collector
+ meta_yaml = integration['edit_link'].replace("blob", "edit")
+ sidebar_label = integration['meta']['monitored_instance']['name']
+ learn_rel_path = generate_category_from_name(
+ integration['meta']['monitored_instance']['categories'][0].split("."), categories)
+ # build the markdown string
+
+ md = \
+ f"""<!--startmeta
+meta_yaml: "{meta_yaml}"
+sidebar_label: "{sidebar_label}"
+learn_status: "Published"
+learn_rel_path: "{learn_rel_path}"
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
+endmeta-->
+
+{create_overview(integration, integration['meta']['monitored_instance']['icon_filename'])}"""
+
+ if integration['metrics']:
+ md += f"""
+{integration['metrics']}
+"""
+
+ if integration['alerts']:
+ md += f"""
+{integration['alerts']}
+"""
+
+ if integration['setup']:
+ md += f"""
+{integration['setup']}
+"""
+
+ if integration['troubleshooting']:
+ md += f"""
+{integration['troubleshooting']}
+"""
+ except Exception as e:
+ print("Exception in collector md construction", e, integration['id'])
+
+ # EXPORTERS
+ elif mode == 'exporter':
+ try:
+ # initiate the variables for the exporter
+ meta_yaml = integration['edit_link'].replace("blob", "edit")
+ sidebar_label = integration['meta']['name']
+ learn_rel_path = generate_category_from_name(integration['meta']['categories'][0].split("."), categories)
+
+ # build the markdown string
+ md = \
+ f"""<!--startmeta
+meta_yaml: "{meta_yaml}"
+sidebar_label: "{sidebar_label}"
+learn_status: "Published"
+learn_rel_path: "Exporting"
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE EXPORTER'S metadata.yaml FILE"
+endmeta-->
+
+{create_overview(integration, integration['meta']['icon_filename'])}"""
+
+ if integration['setup']:
+ md += f"""
+{integration['setup']}
+"""
+
+ if integration['troubleshooting']:
+ md += f"""
+{integration['troubleshooting']}
+"""
+ except Exception as e:
+ print("Exception in exporter md construction", e, integration['id'])
+
+ # NOTIFICATIONS
+ elif mode == 'notification':
+ try:
+ # initiate the variables for the notification method
+ meta_yaml = integration['edit_link'].replace("blob", "edit")
+ sidebar_label = integration['meta']['name']
+ learn_rel_path = generate_category_from_name(integration['meta']['categories'][0].split("."), categories)
+
+ # build the markdown string
+ md = \
+ f"""<!--startmeta
+meta_yaml: "{meta_yaml}"
+sidebar_label: "{sidebar_label}"
+learn_status: "Published"
+learn_rel_path: "{learn_rel_path.replace("notifications", "Alerting/Notifications")}"
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE NOTIFICATION'S metadata.yaml FILE"
+endmeta-->
+
+{create_overview(integration, integration['meta']['icon_filename'])}"""
+
+ if integration['setup']:
+ md += f"""
+{integration['setup']}
+"""
+
+ if integration['troubleshooting']:
+ md += f"""
+{integration['troubleshooting']}
+"""
+
+ except Exception as e:
+ print("Exception in notification md construction", e, integration['id'])
+
+ if "community" in integration['meta'].keys():
+ community = "<img src=\"https://img.shields.io/badge/maintained%20by-Community-blue\" />"
+ else:
+ community = "<img src=\"https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44\" />"
+
+ return meta_yaml, sidebar_label, learn_rel_path, md, community
+
+
+def build_path(meta_yaml_link):
+ """
+ funtion that takes a metadata yaml file link, and makes it into a path that gets used to write to a file.
+ """
+ return meta_yaml_link.replace("https://github.com/netdata/", "") \
+ .split("/", 1)[1] \
+ .replace("edit/master/", "") \
+ .replace("/metadata.yaml", "")
+
+
+def write_to_file(path, md, meta_yaml, sidebar_label, community, mode='default'):
+ """
+ takes the arguments needed to write the integration markdown to the proper file.
+ """
+
+ upper, lower = md.split("##", 1)
+
+ md = upper + community + f"\n\n##{lower}"
+
+ if mode == 'default':
+ # Only if the path exists, this caters for running the same script on both the go and netdata repos.
+ if Path(path).exists():
+ if not Path(f'{path}/integrations').exists():
+ Path(f'{path}/integrations').mkdir()
+
+ try:
+ md = add_custom_edit_url(md, meta_yaml, sidebar_label)
+ clean_and_write(
+ md,
+ Path(f'{path}/integrations/{clean_string(sidebar_label)}.md')
+ )
+
+ except FileNotFoundError as e:
+ print("Exception in writing to file", e)
+
+ # If we only created one file inside the directory, add the entry to the symlink_dict, so we can make the symbolic link
+ if len(list(Path(f'{path}/integrations').iterdir())) == 1:
+ symlink_dict.update(
+ {path: f'integrations/{clean_string(sidebar_label)}.md'})
+ else:
+ try:
+ symlink_dict.pop(path)
+ except KeyError:
+ # We don't need to print something here.
+ pass
+ elif mode == 'notification':
+
+ if "cloud-notifications" in path:
+ # for cloud notifications we generate them near their metadata.yaml
+ name = clean_string(integration['meta']['name'])
+
+ if not Path(f'{path}/integrations').exists():
+ Path(f'{path}/integrations').mkdir()
+
+ # proper_edit_name = meta_yaml.replace(
+ # "metadata.yaml", f'integrations/{clean_string(sidebar_label)}.md\"')
+
+ md = add_custom_edit_url(md, meta_yaml, sidebar_label, mode='cloud-notifications')
+
+ finalpath = f'{path}/integrations/{name}.md'
+ else:
+ # add custom_edit_url as the md file, so we can have uniqueness in the ingest script
+ # afterwards the ingest will replace this metadata with meta_yaml
+ md = add_custom_edit_url(md, meta_yaml, sidebar_label, mode='agent-notifications')
+
+ finalpath = f'{path}/README.md'
+
+ try:
+ clean_and_write(
+ md,
+ Path(finalpath)
+ )
+
+ except FileNotFoundError as e:
+ print("Exception in writing to file", e)
+
+
+def make_symlinks(symlink_dict):
+ """
+ takes a dictionary with directories that have a 1:1 relationship between their README and the integration (only one) inside the "integrations" folder.
+ """
+ for element in symlink_dict:
+ # Remove the README to prevent it being a normal file
+ Path(f'{element}/README.md').unlink()
+ # and then make a symlink to the actual markdown
+ Path(f'{element}/README.md').symlink_to(symlink_dict[element])
+
+ filepath = Path(f'{element}/{symlink_dict[element]}')
+ md = filepath.read_text()
+
+ # This preserves the custom_edit_url for most files as it was,
+ # so the existing links don't break, this is vital for link replacement afterwards
+ filepath.write_text(md.replace(
+ f'{element}/{symlink_dict[element]}', f'{element}/README.md'))
+
+
+cleanup()
+
+categories, integrations = read_integrations_js('integrations/integrations.js')
+
+
+# Iterate through every integration
+for integration in integrations:
+
+ if integration['integration_type'] == "collector":
+
+ meta_yaml, sidebar_label, learn_rel_path, md, community = build_readme_from_integration(
+ integration, mode='collector')
+ path = build_path(meta_yaml)
+ write_to_file(path, md, meta_yaml, sidebar_label, community)
+
+ elif not am_i_inside_go:
+ # kind of specific if clause, so we can avoid running excessive code in the go repo
+ if integration['integration_type'] == "exporter":
+
+ meta_yaml, sidebar_label, learn_rel_path, md, community = build_readme_from_integration(
+ integration, mode='exporter')
+ path = build_path(meta_yaml)
+ write_to_file(path, md, meta_yaml, sidebar_label, community)
+
+ # kind of specific if clause, so we can avoid running excessive code in the go repo
+ elif integration['integration_type'] == "notification":
+
+ meta_yaml, sidebar_label, learn_rel_path, md, community = build_readme_from_integration(
+ integration, mode='notification')
+ path = build_path(meta_yaml)
+ write_to_file(path, md, meta_yaml, sidebar_label, community, mode='notification')
+
+
+make_symlinks(symlink_dict)