diff --git a/.custom_wordlist.txt b/.custom_wordlist.txt new file mode 100644 index 000000000..e69de29bb diff --git a/.github/workflows/automatic-doc-checks.yml b/.github/workflows/automatic-doc-checks.yml new file mode 100644 index 000000000..5f90423f7 --- /dev/null +++ b/.github/workflows/automatic-doc-checks.yml @@ -0,0 +1,16 @@ +name: Main Documentation Checks + +on: + - push + - pull_request + - workflow_dispatch + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + documentation-checks: + uses: canonical/documentation-workflows/.github/workflows/documentation-checks.yaml@main + with: + working-directory: '.' \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..2968da57e --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ + +# Starter pack rules start here +/*env*/ +.sphinx/venv/ +.sphinx/warnings.txt +.sphinx/.wordlist.dic +.sphinx/.doctrees/ +.sphinx/node_modules/ +package*.json +_build +.DS_Store +__pycache__ +.idea/ +.vscode/ +.sphinx/styles/* +.sphinx/vale.ini \ No newline at end of file diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..acd83ee16 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,30 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + jobs: + pre_install: + - git fetch --unshallow || true + +# Build documentation in the docs/ directory with Sphinx +sphinx: + builder: dirhtml + configuration: ./conf.py + fail_on_warning: true + +# If using Sphinx, optionally build your docs in additional formats such as PDF +formats: + - pdf + +# Optionally declare the Python requirements required to build your docs +python: + install: + - requirements: ./.sphinx/requirements.txt diff --git a/.sphinx/.wordlist.txt b/.sphinx/.wordlist.txt new file mode 100644 index 000000000..fb4137d3b --- /dev/null +++ b/.sphinx/.wordlist.txt @@ -0,0 +1,61 @@ +# This wordlist is from the Sphinx starter pack and should not be +# modified. Add any custom terms to .custom_wordlist.txt instead. + +addons +API +APIs +balancer +Charmhub +CLI +Diátaxis +dropdown +EBS +EKS +enablement +favicon +Furo +Git +GitHub +Grafana +IAM +installable +JSON +Juju +Kubeflow +Kubernetes +Launchpad +linter +LTS +LXD +Makefile +Makefiles +Matrix +Mattermost +MicroCeph +MicroCloud +MicroOVN +MyST +namespace +namespaces +NodePort +Numbat +observability +OEM +OLM +Permalink +pre +Quickstart +ReadMe +reST +reStructuredText +roadmap +RTD +subdirectories +subfolders +subtree +Ubuntu +UI +UUID +VM +webhook +YAML diff --git a/.sphinx/_static/favicon.png b/.sphinx/_static/favicon.png new file mode 100644 index 000000000..7f175e461 Binary files /dev/null and b/.sphinx/_static/favicon.png differ diff --git a/.sphinx/_static/tag.png b/.sphinx/_static/tag.png new file mode 100644 index 000000000..f6f6e5aa4 Binary files /dev/null and b/.sphinx/_static/tag.png differ diff --git a/.sphinx/_templates/header.html b/.sphinx/_templates/header.html new file mode 100644 index 000000000..1a128b6f8 --- /dev/null +++ b/.sphinx/_templates/header.html @@ -0,0 +1,36 @@ + diff --git a/.sphinx/get_vale_conf.py b/.sphinx/get_vale_conf.py new file mode 100644 index 000000000..2f1c566f9 --- /dev/null +++ b/.sphinx/get_vale_conf.py @@ -0,0 +1,29 @@ +#! /usr/bin/env python + +import requests +import os + +DIR=os.getcwd() + +def main(): + + if os.path.exists(f"{DIR}/.sphinx/styles/Canonical"): + print("Vale directory exists") + else: + os.makedirs(f"{DIR}/.sphinx/styles/Canonical") + + url = "https://api.github.com/repos/canonical/praecepta/contents/styles/Canonical" + r = requests.get(url) + for item in r.json(): + download = requests.get(item["download_url"]) + file = open(".sphinx/styles/Canonical/" + item["name"], "w") + file.write(download.text) + file.close() + + config = requests.get("https://raw.githubusercontent.com/canonical/praecepta/main/vale.ini") + file = open(".sphinx/vale.ini", "w") + file.write(config.text) + file.close() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/.sphinx/pa11y.json b/.sphinx/pa11y.json new file mode 100644 index 000000000..8df0cb9cb --- /dev/null +++ b/.sphinx/pa11y.json @@ -0,0 +1,9 @@ +{ + "chromeLaunchConfig": { + "args": [ + "--no-sandbox" + ] + }, + "reporter": "cli", + "standard": "WCAG2AA" +} \ No newline at end of file diff --git a/.sphinx/requirements.txt b/.sphinx/requirements.txt new file mode 100644 index 000000000..c019f178a --- /dev/null +++ b/.sphinx/requirements.txt @@ -0,0 +1,2 @@ +git+https://github.com/canonical/canonical-sphinx@main#egg=canonical-sphinx +sphinx-autobuild diff --git a/.sphinx/spellingcheck.yaml b/.sphinx/spellingcheck.yaml new file mode 100644 index 000000000..5f3dbad35 --- /dev/null +++ b/.sphinx/spellingcheck.yaml @@ -0,0 +1,30 @@ +matrix: +- name: rST files + aspell: + lang: en + d: en_GB + dictionary: + wordlists: + - .sphinx/.wordlist.txt + - .custom_wordlist.txt + output: .sphinx/.wordlist.dic + sources: + - _build/**/*.html + pipeline: + - pyspelling.filters.html: + comments: false + attributes: + - title + - alt + ignores: + - code + - pre + - spellexception + - link + - title + - div.relatedlinks + - strong.command + - div.visually-hidden + - img + - a.p-navigation__link + - a.contributor diff --git a/.wokeignore b/.wokeignore new file mode 100644 index 000000000..c64a60376 --- /dev/null +++ b/.wokeignore @@ -0,0 +1,4 @@ +# the cheat sheets contain a link to a repository with a block word which we +# cannot avoid for now, ie +# https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html +doc-cheat-sheet* diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..a861ba84a --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +# This Makefile stub allows you to customize starter pack (SP) targets. +# Consider this file as a bridge between your project +# and the starter pack's predefined targets that reside in Makefile.sp. +# +# You can add your own, non-SP targets here or override SP targets +# to fit your project's needs. For example, you can define and use targets +# named "install" or "run", but continue to use SP targets like "sp-install" +# or "sp-run" when working on the documentation. + +# Put it first so that "make" without argument is like "make help". +help: + @echo "\n" \ + "------------------------------------------------------------- \n" \ + "* watch, build and serve the documentation: make run \n" \ + "* only build: make html \n" \ + "* only serve: make serve \n" \ + "* clean built doc files: make clean-doc \n" \ + "* clean full environment: make clean \n" \ + "* check links: make linkcheck \n" \ + "* check spelling: make spelling \n" \ + "* check spelling (without building again): make spellcheck \n" \ + "* check inclusive language: make woke \n" \ + "* check accessibility: make pa11y \n" \ + "* check style guide compliance: make vale \n" \ + "* check style guide compliance on target: make vale TARGET=* \n" \ + "* other possible targets: make \n" \ + "------------------------------------------------------------- \n" + +%: + $(MAKE) -f Makefile.sp sp-$@ diff --git a/Makefile.sp b/Makefile.sp new file mode 100644 index 000000000..a7f4a7121 --- /dev/null +++ b/Makefile.sp @@ -0,0 +1,110 @@ +# Minimal makefile for Sphinx documentation +# +# `Makefile.sp` is from the Sphinx starter pack and should not be +# modified. +# Add your customisation to `Makefile` instead. + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXDIR = .sphinx +SPHINXOPTS ?= -c . -d $(SPHINXDIR)/.doctrees +SPHINXBUILD ?= $(VENVDIR)/bin/sphinx-build +SOURCEDIR = . +BUILDDIR = _build +VENVDIR = $(SPHINXDIR)/venv +PA11Y = $(SPHINXDIR)/node_modules/pa11y/bin/pa11y.js --config $(SPHINXDIR)/pa11y.json +VENV = $(VENVDIR)/bin/activate +TARGET = * +ALLFILES = *.rst **/*.rst + +.PHONY: sp-full-help sp-woke-install sp-pa11y-install sp-install sp-run sp-html \ + sp-epub sp-serve sp-clean sp-clean-doc sp-spelling sp-spellcheck sp-linkcheck sp-woke \ + sp-pa11y Makefile.sp sp-vale + +sp-full-help: $(VENVDIR) + @. $(VENV); $(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @echo "\n\033[1;31mNOTE: This help texts shows unsupported targets!\033[0m" + @echo "Run 'make help' to see supported targets." + +# If requirements are updated, venv should be rebuilt and timestamped. +$(VENVDIR): + python3 -c "import venv" || \ + (echo "You must install python3-venv before you can build the documentation."; exit 1) + @echo "... setting up virtualenv" + python3 -m venv $(VENVDIR) + . $(VENV); pip install --require-virtualenv \ + --upgrade -r $(SPHINXDIR)/requirements.txt \ + --log $(VENVDIR)/pip_install.log + @test ! -f $(VENVDIR)/pip_list.txt || \ + mv $(VENVDIR)/pip_list.txt $(VENVDIR)/pip_list.txt.bak + @. $(VENV); pip list --local --format=freeze > $(VENVDIR)/pip_list.txt + @touch $(VENVDIR) + +sp-woke-install: + @type woke >/dev/null 2>&1 || \ + { echo "Installing \"woke\" snap... \n"; sudo snap install woke; } + +sp-pa11y-install: + @type $(PA11Y) >/dev/null 2>&1 || { \ + echo "Installing \"pa11y\" from npm... \n"; \ + mkdir -p $(SPHINXDIR)/node_modules/ ; \ + npm install --prefix $(SPHINXDIR) pa11y; \ + } + +sp-install: $(VENVDIR) + +sp-run: sp-install + . $(VENV); $(VENVDIR)/bin/sphinx-autobuild -b dirhtml "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) + +# Doesn't depend on $(BUILDDIR) to rebuild properly at every run. +sp-html: sp-install + . $(VENV); $(SPHINXBUILD) -W --keep-going -b dirhtml "$(SOURCEDIR)" "$(BUILDDIR)" -w $(SPHINXDIR)/warnings.txt $(SPHINXOPTS) + +sp-epub: sp-install + . $(VENV); $(SPHINXBUILD) -b epub "$(SOURCEDIR)" "$(BUILDDIR)" -w $(SPHINXDIR)/warnings.txt $(SPHINXOPTS) + +sp-serve: sp-html + cd "$(BUILDDIR)"; python3 -m http.server --bind 127.0.0.1 8000 + +sp-clean: sp-clean-doc + @test ! -e "$(VENVDIR)" -o -d "$(VENVDIR)" -a "$(abspath $(VENVDIR))" != "$(VENVDIR)" + rm -rf $(VENVDIR) + rm -rf $(SPHINXDIR)/node_modules/ + rm -rf $(SPHINXDIR)/styles + rm -rf $(SPHINXDIR)/vale.ini + +sp-clean-doc: + git clean -fx "$(BUILDDIR)" + rm -rf $(SPHINXDIR)/.doctrees + +sp-spellcheck: + . $(VENV) ; python3 -m pyspelling -c $(SPHINXDIR)/spellingcheck.yaml -j $(shell nproc) + +sp-spelling: sp-html sp-spellcheck + +sp-linkcheck: sp-install + . $(VENV) ; $(SPHINXBUILD) -b linkcheck "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) || { grep --color -F "[broken]" "$(BUILDDIR)/output.txt"; exit 1; } + exit 0 + +sp-woke: sp-woke-install + woke $(ALLFILES) --exit-1-on-failure \ + -c https://github.com/canonical/Inclusive-naming/raw/main/config.yml + +sp-pa11y: sp-pa11y-install sp-html + find $(BUILDDIR) -name *.html -print0 | xargs -n 1 -0 $(PA11Y) + +sp-vale: sp-install + @. $(VENV); test -d $(SPHINXDIR)/venv/lib/python*/site-packages/vale || pip install vale + @. $(VENV); test -f $(SPHINXDIR)/vale.ini || python3 $(SPHINXDIR)/get_vale_conf.py + @. $(VENV); find $(SPHINXDIR)/venv/lib/python*/site-packages/vale/vale_bin -size 195c -exec vale --config "$(SPHINXDIR)/vale.ini" $(TARGET) > /dev/null \; + @echo "" + @echo "Running Vale against $(TARGET). To change target set TARGET= with make command" + @echo "" + @. $(VENV); vale --config "$(SPHINXDIR)/vale.ini" --glob='*.{md,txt,rst}' $(TARGET) + + + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile.sp + . $(VENV); $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/README.md b/README.md index 1357e0b31..b0af0a133 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Documentation for Juju -JUJU DOCUMENTATION HAS MOVED TO DISCOURSE: +The Juju documentation is now on Read The Docs: -https://discourse.jujucharms.com/t/documentation-migrated-to-discourse +https://canonical-juju.readthedocs-hosted.com/en/latest/ -The bug tracker on GitHub is still active but as of March 18 2019 pull requests are no longer being processed. +Please report Juju documentation issues on [github.com/juju/juju](https://github.com/juju/juju) diff --git a/conf.py b/conf.py new file mode 100644 index 000000000..e20927977 --- /dev/null +++ b/conf.py @@ -0,0 +1,220 @@ +import datetime + +# Configuration for the Sphinx documentation builder. +# All configuration specific to your project should be done in this file. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html +# For our custom configuration, see the Canonical Sphinx extension: +# https://github.com/canonical/canonical-sphinx +# +# If you're not familiar with Sphinx and don't want to use advanced +# features, it is sufficient to update the settings in the "Project +# information" section. + +############################################################ +### Project information +############################################################ + +# Product name +project = 'Juju' +author = 'Canonical Ltd.' + +# The title you want to display for the documentation in the sidebar. +# You might want to include a version number here. +# To not display any title, set this option to an empty string. +html_title = project + ' documentation' + +# The default value uses CC-BY-SA as the license and the current year +# as the copyright year. +# +# If your documentation needs a different copyright license, use that +# instead of 'CC-BY-SA'. Also, if your documentation is included as +# part of the code repository of your project, it'll inherit the license +# of the code. So you'll need to specify that license here (instead of +# 'CC-BY-SA'). +# +# For static works, it is common to provide the year of first publication. +# Another option is to give the first year and the current year +# for documentation that is often changed, e.g. 2022–2023 (note the en-dash). +# +# A way to check a GitHub repo's creation date is to obtain a classic GitHub +# token with 'repo' permissions here: https://github.com/settings/tokens +# Next, use 'curl' and 'jq' to extract the date from the GitHub API's output: +# +# curl -H 'Authorization: token ' \ +# -H 'Accept: application/vnd.github.v3.raw' \ +# https://api.github.com/repos/canonical/ | jq '.created_at' + +copyright = '%s CC-BY-SA, %s' % (datetime.date.today().year, author) + +## Open Graph configuration - defines what is displayed as a link preview +## when linking to the documentation from another website (see https://ogp.me/) +# The URL where the documentation will be hosted (leave empty if you +# don't know yet) +ogp_site_url = 'https://canonical-juju.readthedocs-hosted.com/' +# The documentation website name (usually the same as the product name) +ogp_site_name = project +# The URL of an image or logo that is used in the preview +ogp_image = 'https://assets.ubuntu.com/v1/253da317-image-document-ubuntudocs.svg' + +# Update with the local path to the favicon for your product +# (default is the circle of friends) +# html_favicon = '.sphinx/_static/favicon.png' + +# (Some settings must be part of the html_context dictionary, while others +# are on root level. Don't move the settings.) +html_context = { + + # Change to the link to the website of your product (without "https://") + # For example: "ubuntu.com/lxd" or "microcloud.is" + # If there is no product website, edit the header template to remove the + # link (see the readme for instructions). + 'product_page': 'juju.is', + + # Add your product tag (the orange part of your logo, will be used in the + # header) to ".sphinx/_static" and change the path here (start with "_static") + # (default is the circle of friends) + # 'product_tag': '_static/tag.png', + + # Change to the discourse instance you want to be able to link to + # using the :discourse: metadata at the top of a file + # (use an empty value if you don't want to link) + 'discourse': 'https://discourse.charmhub.io/', + + # Change to the Mattermost channel you want to link to + # (use an empty value if you don't want to link) + 'mattermost': '', + + # Change to the Matrix channel you want to link to + # (use an empty value if you don't want to link) + 'matrix': 'https://matrix.to/#/#charmhub-juju:ubuntu.com', + + # Change to the GitHub URL for your project + # This is used, for example, to link to the source files and allow creating GitHub issues directly from the documentation. + 'github_url': 'https://github.com/juju/docs', + + # Change to the branch for this version of the documentation + # 'github_version': 'main', + + # Change to the folder that contains the documentation + # (usually "/" or "/docs/") + 'github_folder': '/', + + # Change to an empty value if your GitHub repo doesn't have issues enabled. + # This will disable the feedback button and the issue link in the footer. + # 'github_issues': 'enabled', + + # Controls the existence of Previous / Next buttons at the bottom of pages + # Valid options: none, prev, next, both + # 'sequential_nav': "none", + + # Uncomment to disable displaying the contributors for each file. + # (You can also limit the time frame for displaying contributors + # by setting a "display_contributors_since" variable.) + # "display_contributors": False, + +} + +# If your project is on documentation.ubuntu.com, specify the project +# slug (for example, "lxd") here. +# slug = "" + +# These paths are needed if you want to override any default assets. +# You can comment them out if you don't need this (but you can also just +# leave them). + +html_static_path = ['.sphinx/_static'] +templates_path = ['.sphinx/_templates'] + +############################################################ +### Redirects +############################################################ + +# Set up redirects (https://documatt.gitlab.io/sphinx-reredirects/usage.html) +# For example: 'explanation/old-name.html': '../how-to/prettify.html', +# You can also configure redirects in the Read the Docs project dashboard +# (see https://docs.readthedocs.io/en/stable/guides/redirects.html). +# NOTE: If this variable is not defined, set to None, or the dictionary is empty, +# the sphinx_reredirects extension will be disabled. +redirects = {} + +############################################################ +### Link checker exceptions +############################################################ + +# Links to ignore when checking links +linkcheck_ignore = [ + 'http://127.0.0.1:8000/' + ] + +# Pages on which to ignore anchors +# (This list will be appended to linkcheck_anchors_ignore_for_url) +linkcheck_anchors_ignore_for_url = [ + r'https://github/.com/.*' +] + +############################################################ +### Additions to default configuration +############################################################ + +## The following settings are appended to the default configuration. +## Use them to extend the default functionality. + +# By default, the following MyST extensions are enabled: +# substitution, deflist, linkify +# If you need more extensions, add them here. +# myst_enable_extensions = set() + +# You must include the canonical_sphinx extension here. +# This extension automatically enables the following Sphinx extensions: +# custom-rst-roles, myst_parser, notfound.extension, related-links, +# sphinx_copybutton, sphinx_design, sphinx_tabs.tabs, +# sphinx_reredirects, sphinxcontrib.jquery, sphinxext.opengraph, +# terminal-output, youtube-links +# If you need more extensions, add them here (in addition to +# canonical_sphinx). +extensions = [ + 'canonical_sphinx' + ] + +# Add files or directories that should be excluded from processing. +exclude_patterns = [ + 'doc-cheat-sheet*', + 'pull_request_template.md', + 'README.md' + ] + +# Add custom CSS files (located in .sphinx/_static/) +# html_css_files = [] + +# Add custom JavaScript files (located in .sphinx/_static/) +# html_js_files = [] + +## The following settings override the default configuration. + +# Specify a reST string that is included at the end of each file. +# The suggested value pulls the reuse/links.txt file into each reST file. +rst_epilog = ''' +.. include:: /reuse/links.txt +''' + +# By default, the documentation includes a feedback button at the top. +# You can disable it by setting the following configuration to True. +# disable_feedback_button = False + +# If you are using the :manpage: role, set this variable to the URL for the version +# that you want to link to: +# manpages_url = "https://manpages.ubuntu.com/manpages/noble/en/man%7Bsection%7D/%7Bpage%7D.%7Bsection%7D.html" + +############################################################ +### Additional configuration +############################################################ + +## Add any configuration that is not covered by the common conf.py file. + +# Define a :center: role that can be used to center the content of table cells. +rst_prolog = ''' +.. role:: center + :class: align-center +''' diff --git a/doc-cheat-sheet-myst.md b/doc-cheat-sheet-myst.md new file mode 100644 index 000000000..6257c9db0 --- /dev/null +++ b/doc-cheat-sheet-myst.md @@ -0,0 +1,261 @@ +--- +orphan: true +myst: + substitutions: + reuse_key: "This is **included** text." + advanced_reuse_key: "This is a substitution that includes a code block: + ``` + code block + ```" +--- + + + +(cheat-sheet-myst)= +# Markdown/MyST cheat sheet + + + +This file contains the syntax for commonly used Markdown and MyST markup. +Open it in your text editor to quickly copy and paste the markup you need. + +See the [MyST style guide](https://canonical-documentation-with-sphinx-and-readthedocscom.readthedocs-hosted.com/style-guide-myst/) for detailed information and conventions. + +Also see the [MyST documentation](https://myst-parser.readthedocs.io/en/latest/index.html) for detailed information on MyST, and the [Canonical Documentation Style Guide](https://docs.ubuntu.com/styleguide/en) for general style conventions. + +## H2 heading + +### H3 heading + +#### H4 heading + +##### H5 heading + +## Inline formatting + +- {guilabel}`UI element` +- `code` +- {command}`command` +- {kbd}`Key` +- *Italic* +- **Bold** + +## Code blocks + +Start a code block: + + code: + - example: true + +``` +# Demonstrate a code block +code: + - example: true +``` + +```yaml +# Demonstrate a code block +code: + - example: true +``` + +(a_section_target_myst)= +## Links + +- [Canonical website](https://canonical.com/) +- {ref}`a_section_target_myst` +- {ref}`Link text ` +- {doc}`index` +- {doc}`Link text ` + +## Navigation + +Use the following syntax:: + + ```{toctree} + :hidden: + + sub-page1 + sub-page2 + ``` + +## Lists + +1. Step 1 + - Item 1 + * Sub-item + - Item 2 + 1. Sub-step 1 + 1. Sub-step 2 +1. Step 2 + 1. Sub-step 1 + - Item + 1. Sub-step 2 + +Term 1 +: Definition + +Term 2 +: Definition + +## Tables + +## Markdown tables + +| Header 1 | Header 2 | +|------------------------------------|----------| +| Cell 1
Second paragraph | Cell 2 | +| Cell 3 | Cell 4 | + +Centred: + +| Header 1 | Header 2 | +|:----------------------------------:|:--------:| +| Cell 1
Second paragraph | Cell 2 | +| Cell 3 | Cell 4 | + +## List tables + +```{list-table} + :header-rows: 1 + +* - Header 1 + - Header 2 +* - Cell 1 + + Second paragraph + - Cell 2 +* - Cell 3 + - Cell 4 +``` + +Centred: + +```{list-table} + :header-rows: 1 + :align: center + +* - Header 1 + - Header 2 +* - Cell 1 + + Second paragraph + - Cell 2 +* - Cell 3 + - Cell 4 +``` + +## Notes + +```{note} +A note. +``` + +```{tip} +A tip. +``` + +```{important} +Important information +``` + +```{caution} +This might damage your hardware! +``` + +## Images + +![Alt text](https://assets.ubuntu.com/v1/b3b72cb2-canonical-logo-166.png) + +```{figure} https://assets.ubuntu.com/v1/b3b72cb2-canonical-logo-166.png + :width: 100px + :alt: Alt text + + Figure caption +``` + +## Reuse + +### Keys + +Keys can be defined at the top of a file, or in a `myst_substitutions` option in `conf.py`. + +{{reuse_key}} + +{{advanced_reuse_key}} + +### File inclusion + +```{include} index.md + :start-after: include_start + :end-before: include_end +``` + +## Tabs + +````{tabs} +```{group-tab} Tab 1 + +Content Tab 1 +``` + +```{group-tab} Tab 2 +Content Tab 2 +``` +```` + +## Glossary + +```{glossary} + +some term + Definition of the example term. +``` + +{term}`some term` + +## More useful markup + +- ```{versionadded} X.Y +- {abbr}`API (Application Programming Interface)` + +---- + +## Custom extensions + +Related links at the top of the page (surrounded by `---`): + + relatedlinks: https://github.com/canonical/lxd-sphinx-extensions, [RTFM](https://www.google.com) + discourse: 12345 + +Terms that should not be checked by the spelling checker: {spellexception}`PurposelyWrong` + +A single-line terminal view that separates input from output: + +```{terminal} + :input: command + :user: root + :host: vampyr + :dir: /home/user/directory/ + +the output +``` + +A multi-line version of the same: + +```{terminal} + :user: root + :host: vampyr + :dir: /home/user/directory/ + +:input: command 1 +output 1 +:input: command 2 +output 2 +``` + +A link to a YouTube video: + +```{youtube} https://www.youtube.com/watch?v=iMLiK1fX4I0 + :title: Demo +``` diff --git a/doc-cheat-sheet.rst b/doc-cheat-sheet.rst new file mode 100644 index 000000000..ca3fd0622 --- /dev/null +++ b/doc-cheat-sheet.rst @@ -0,0 +1,285 @@ +:orphan: + +.. vale off + +.. _cheat-sheet: + +ReStructuredText cheat sheet +============================ + +.. vale on + +This file contains the syntax for commonly used reST markup. +Open it in your text editor to quickly copy and paste the markup you need. + +See the `reStructuredText style guide `_ for detailed information and conventions. + +Also see the `Sphinx reStructuredText Primer `_ for more details on reST, and the `Canonical Documentation Style Guide `_ for general style conventions. + +H2 heading +---------- + +H3 heading +~~~~~~~~~~ + +H4 heading +^^^^^^^^^^ + +H5 heading +.......... + +Inline formatting +----------------- + +- :guilabel:`UI element` +- ``code`` +- :file:`file path` +- :command:`command` +- :kbd:`Key` +- *Italic* +- **Bold** + +Code blocks +----------- + +Start a code block:: + + code: + - example: true + +.. code:: + + # Demonstrate a code block + code: + - example: true + +.. code:: yaml + + # Demonstrate a code block + code: + - example: true + +.. _a_section_target: + +Links +----- + +- `Canonical website `_ +- `Canonical website`_ (defined in ``reuse/links.txt`` or at the bottom of the page) +- https:\ //canonical.com/ +- :ref:`a_section_target` +- :ref:`Link text ` +- :doc:`index` +- :doc:`Link text ` + + +Navigation +---------- + +Use the following syntax:: + + .. toctree:: + :hidden: + + sub-page1 + sub-page2 + + +Lists +----- + +1. Step 1 + + - Item 1 + + * Sub-item + - Item 2 + + i. Sub-step 1 + #. Sub-step 2 +#. Step 2 + + a. Sub-step 1 + + - Item + #. Sub-step 2 + +Term 1: + Definition +Term 2: + Definition + +Tables +------ + ++----------------------+------------+ +| Header 1 | Header 2 | ++======================+============+ +| Cell 1 | Cell 2 | +| | | +| Second paragraph | | ++----------------------+------------+ +| Cell 3 | Cell 4 | ++----------------------+------------+ + ++----------------------+------------------+ +| :center:`Header 1` | Header 2 | ++======================+==================+ +| Cell 1 | Cell 2 | +| | | +| Second paragraph | | ++----------------------+------------------+ +| Cell 3 | :center:`Cell 4` | ++----------------------+------------------+ + +.. list-table:: + :header-rows: 1 + + * - Header 1 + - Header 2 + * - Cell 1 + + Second paragraph + - Cell 2 + * - Cell 3 + - Cell 4 + +.. rst-class:: align-center + + +----------------------+------------+ + | Header 1 | Header 2 | + +======================+============+ + | Cell 1 | Cell 2 | + | | | + | Second paragraph | | + +----------------------+------------+ + | Cell 3 | Cell 4 | + +----------------------+------------+ + +.. list-table:: + :header-rows: 1 + :align: center + + * - Header 1 + - Header 2 + * - Cell 1 + + Second paragraph + - Cell 2 + * - Cell 3 + - Cell 4 + +Notes +----- + +.. note:: + A note. + +.. tip:: + A tip. + +.. important:: + Important information + +.. caution:: + This might damage your hardware! + +Images +------ + +.. image:: https://assets.ubuntu.com/v1/b3b72cb2-canonical-logo-166.png + +.. figure:: https://assets.ubuntu.com/v1/b3b72cb2-canonical-logo-166.png + :width: 100px + :alt: Alt text + + Figure caption + +Reuse +----- + +.. |reuse_key| replace:: This is **included** text. + +|reuse_key| + +.. include:: index.rst + :start-after: include_start + :end-before: include_end + +Tabs +---- + +.. tabs:: + + .. group-tab:: Tab 1 + + Content Tab 1 + + .. group-tab:: Tab 2 + + Content Tab 2 + + +Glossary +-------- + +.. glossary:: + + example term + Definition of the example term. + +:term:`example term` + +More useful markup +------------------ + +- .. versionadded:: X.Y +- | Line 1 + | Line 2 + | Line 3 +- .. This is a comment +- :abbr:`API (Application Programming Interface)` + +---- + +Custom extensions +----------------- + +Related links at the top of the page:: + + :relatedlinks: https://github.com/canonical/lxd-sphinx-extensions, [RTFM](https://www.google.com) + :discourse: 12345 + +Terms that should not be checked by the spelling checker: :spellexception:`PurposelyWrong` + +A single-line terminal view that separates input from output: + +.. terminal:: + :input: command + :user: root + :host: vampyr + :dir: /home/user/directory/ + + the output + +A multi-line version of the same: + +.. terminal:: + :user: root + :host: vampyr + :dir: /home/user/directory/ + + :input: command 1 + output 1 + :input: command 2 + output 2 + +A link to a YouTube video: + +.. youtube:: https://www.youtube.com/watch?v=iMLiK1fX4I0 + :title: Demo + + + +.. LINKS +.. _Canonical website: https://canonical.com/ diff --git a/explanation/index.md b/explanation/index.md new file mode 100644 index 000000000..ba10574c5 --- /dev/null +++ b/explanation/index.md @@ -0,0 +1,2 @@ +(exp)= +# Explanation diff --git a/how-to/index.md b/how-to/index.md new file mode 100644 index 000000000..1c395857e --- /dev/null +++ b/how-to/index.md @@ -0,0 +1,2 @@ +(howto)= +# How-to guides diff --git a/index.md b/index.md new file mode 100644 index 000000000..b0c50d6a1 --- /dev/null +++ b/index.md @@ -0,0 +1,74 @@ +(home)= +# Juju documentation + +Juju is an open-source ecosystem of tools designed to revolutionise the speed and quality of software operations on any cloud. + +When you do operations the Juju way, things are simple as `juju deploy`, `juju configure`, `juju integrate...`, `juju scale...`, `juju upgrade`, etc. – for any charmed application (i.e., application equipped with operations code for use with Juju) on any type of supported cloud (Kubernetes or otherwise). + +> [Browse existing charms on Charmhub](https://charmhub.io/)

[Browse supported clouds](https://juju.is/docs/juju/juju-supported-clouds) + +At a time when operations code is still largely stuck with handcrafted code, YAML, or Kubernetes-only operators, the Juju paradigm offers reusable code, testable operations, and multicloud, so you can achieve a lot more, a lot better, and a lot faster. + +> [Did you know? Juju also has an integration with Terraform!](https://juju.is/docs/juju/terraform-juju-client) + +Whether you are a developer wondering how to make your application ready for the cloud, a cloud system administrator or an SRE tired of YAML, or a business owner, Juju can help. + + + + +--- + +## In this documentation + +````{grid} 1 1 2 2 + +```{grid-item} [Tutorial](/tutorial/get_started) + +**Start here**: a hands-on introduction to Juju for new users +``` + +```{grid-item} [How-to guides](/how-to/index) + +**Step-by-step guides** covering key operations and common tasks +``` + +```` + +````{grid} 1 1 2 2 +:reverse: + +```{grid-item} [Reference](/reference/index) + +**Technical information** - specifications, APIs, architecture +``` + +```{grid-item} [Explanation](/explanation/index) + +**Discussion and clarification** of key topics +``` + +```` + +--- + +## Project and community + +Juju is a member of the Ubuntu family. It’s an open source project that warmly welcomes community projects, contributions, suggestions, fixes and constructive feedback. + +* [Code of conduct](https://ubuntu.com/community/ethos/code-of-conduct) +* [Join our online forum](https://discourse.charmhub.io/) +* [Join our online chat](https://matrix.to/#/#charmhub-juju:ubuntu.com) +* Contribute +* Roadmap +* Thinking about using Juju for your next project? Get in touch! + + + +```{toctree} +:hidden: +:maxdepth: 2 + +Tutorial +How-to guides +Reference +Explanation diff --git a/make.bat b/make.bat new file mode 100644 index 000000000..32bb24529 --- /dev/null +++ b/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/reference/index.md b/reference/index.md new file mode 100644 index 000000000..1f1d06a5c --- /dev/null +++ b/reference/index.md @@ -0,0 +1,2 @@ +(ref)= +# Reference diff --git a/reuse/links.txt b/reuse/links.txt new file mode 100644 index 000000000..f9d3d5cdc --- /dev/null +++ b/reuse/links.txt @@ -0,0 +1 @@ +.. _link text: https://example.com diff --git a/tmp/t/10211.md b/tmp/t/10211.md new file mode 100644 index 000000000..2767d3c5a --- /dev/null +++ b/tmp/t/10211.md @@ -0,0 +1,54 @@ +[note type=caution] +The information in this doc is based on Juju version 3.5.5, +and may not accurately reflect other versions of Juju. +[/note] + +> See also: [create-storage-pool](/t/10093), [remove-storage-pool](/t/10068) +**Alias:** storage-pools + +## Summary +List storage pools. + +### Options +| Flag | Default | Usage | +| --- | --- | --- | +| `-B`, `--no-browser-login` | false | Do not use web browser for authentication | +| `--format` | tabular | Specify output format (json|tabular|yaml) | +| `-m`, `--model` | | Model to operate in. Accepts [<controller name>:]<model name>|<model UUID> | +| `--name` | | Only show pools with these names | +| `-o`, `--output` | | Specify an output file | +| `--provider` | | Only show pools of these provider types | + +## Examples + +List all storage pools: + + juju storage-pools + +List only pools of type kubernetes, azure, ebs: + + juju storage-pools --provider kubernetes,azure,ebs + +List only pools named pool1 and pool2: + + juju storage-pools --name pool1,pool2 + + +## Details + +The user can filter on pool type, name. + +If no filter is specified, all current pools are listed. +If at least 1 name and type is specified, only pools that match both a name +AND a type from criteria are listed. +If only names are specified, only mentioned pools will be listed. +If only types are specified, all pools of the specified types will be listed. + +Both pool types and names must be valid. +Valid pool types are pool types that are registered for Juju model. + + +--- + +------------------------- + diff --git a/tmp/t/12-factor-app-charm.md b/tmp/t/12-factor-app-charm.md new file mode 100644 index 000000000..48235a886 --- /dev/null +++ b/tmp/t/12-factor-app-charm.md @@ -0,0 +1,23 @@ +(12-factor-app-charm)= +# 12-Factor app charm + +A **12-Factor app charm** is a {ref}`charm ` that has been created using certain coordinated pairs of {ref}`Rockcraft ` and {ref}`Charmcraft ` {ref}`profiles ` designed to give you most of the content you will need to generate a [rock^](https://documentation.ubuntu.com/rockcraft/en/latest/explanation/rocks/) for a charm, and then the charm itself, for a particular type of workload (e.g., an application developed with Flask). + +```{tip} + +**Did you know?** The OCI images produced by the 12-Factor-app-geared Rockcraft extension are designed to work standalone and are also well integrated with the rest of the Flask framework tooling. + +``` + +When you initialise a rock with a 12-Factor-app-charm-geared profile, the initialisation will generate all the basic structure and content you'll need for the rock, including a [`rockcraft.yaml`^](https://canonical-rockcraft.readthedocs-hosted.com/en/latest/reference/rockcraft.yaml/#) prepopulated with an extension matching the profile. Similarly, when you initialise a charm with a 12-Factor-app-charm-geared profile, that will generate all the basic structure content you'll need for the charm, including a {ref}``charmcraft.yaml` ` pre-populated with an extension matching the profile as well as a `src/charm.py` pre-loaded with a library (`paas_charm`) with constructs matching the profile and the extension. + + +At present, there are four pairs of profiles: +- `flask-framework` ({ref}`Rockcraft extension 'flask-framework' `, {ref}`Charmcraft extension 'flask-framework' `) +- `django-framework` ({ref}`Rockcraft extension 'django-framework' `, {ref}`Charmcraft extension 'django-framework' `) +- `fastapi-framework` ({ref}`Rockcraft extension 'fastapi-framework' `, {ref}`Charmcraft extension 'fastapi-framework' `) +- `go-framework` (Rockcraft extension 'go-framework', {ref}`Charmcraft extension 'go-framework' `) + +
+ +> **Contributors:** @econley, @jdkandersson, @javierdelapuente, @tmihoc \ No newline at end of file diff --git a/tmp/t/about-application-modelling.md b/tmp/t/about-application-modelling.md new file mode 100644 index 000000000..d1d65c169 --- /dev/null +++ b/tmp/t/about-application-modelling.md @@ -0,0 +1,32 @@ +(about-application-modelling)= +# About application modelling + +> See also: {ref}`Application `, {ref}`Model ` + +Juju provides simplicity, stability and security. Models reduce the cognitive gap between the whiteboard picture of your service and how it is implemented. An application model is a definition of which applications are providing a service and how they inter-relate. + +Technical details such as CPU core counts, disk write throughput, and IP addresses are secondary. They are accessible to administrators, but an application model places the applications at the front. + +The primary function of a model is to enable you to maintain an uncluttered view of your service. Operational simplicity improves communication and understanding. Models provide an abstract view of the infrastructure that's hosting your service. + +More specifically, advantages include: + + +### A. Service isolation + +Juju models enforce service isolation. A model maintains exclusive access of the resources under its control. + + +### B. Access control + +Models provide access control. Juju enables you to create user accounts that have limited ability to alter the deployment. + + +### C. Repeatability + +Models provide repeatable infrastructure deployments. Once your model is in-place and functional, it becomes simple to export a model's definition as a bundle, then re-deploy that model in another host. + + +### D. Boundaries + +Models respect bureaucratic boundaries. Models enable you to partition compute resources according to your internal guidelines. You may wish to keep a central set of databases in the same model. Juju's access controls are model-specific, enabling you to know exactly who has permissions to perform direct database administration. Those databases could be made available to various consuming applications from other models via relations (which can span models). Other use cases for central models include secrets (using the [vault charm](https://jaas.ai/vault/)) and identity management (using the [keystone charm](https://jaas.ai/keystone)). \ No newline at end of file diff --git a/tmp/t/about-charming-history.md b/tmp/t/about-charming-history.md new file mode 100644 index 000000000..edf63ca64 --- /dev/null +++ b/tmp/t/about-charming-history.md @@ -0,0 +1,46 @@ +(about-charming-history)= +# About charming history + +Since its beginnings in 2009, the writing of charms has gone through multiple phases, comprising three different frameworks based on three different libraries. This document explains this evolution. + + +**Contents:** + +- [Background](#heading--background) +- [2014: The Services Framework](#heading---the-services-framework) +- [2015: The Reactive Framework](#heading---the-reactive-framework) +- [2019 - Present: The Operator Framework Ops](#heading--2019---present-the-operator-framework-ops) + +

Background

+ +Juju was initially conceived in 2009 by Mark Shuttleworth, Gustavo Niemeyer and Simon Wardley [[1](https://blog.labix.org/2013/06/25/the-heart-of-juju)] as a way to simplify the deployment and operation of applications and their supporting services. Juju enables users to both graphically and non-graphically model complex deployments which are composed using one or more Charms - reusable packages that contain all the instructions necessary to deploy, configure, operate and integrate software. + +The Juju team adopted the Go language early-on, noting its suitability to cloud-first environments. This is an observation that has been made by numerous subsequent cloud-native projects - [Kubernetes](https://kubernetes.io/), [Terraform](https://terraform.io), [containerd](https://containerd.io/) to name just a few. + +Juju has evolved significantly over time, and throughout its life a number of frameworks and libraries have been authored to simplify and standardise the way Charms are written. One thing that hasn't changed throughout time however, is the approach that Juju takes to operating workloads - Juju's fundamentally works just like it did in 2009, but now supports many more underlying compute platforms (such as Kubernetes). + +Charms can be written in any language - that was as true in 2009 as it is today. Over time however, the Charming community has settled on Python as its language of choice. Python is extremely popular, has a huge community and is already commonly used to perform system automation tasks, making it a great choice for authoring Charms and for ensuring re-usability among the charming community. + +

2014: The Services Framework

+ + +The Services Framework evolved from the [charmhelpers](https://github.com/juju/charm-helpers) library; it aimed to standardise an approach to event handling and Charm structure. The services framework provided the first steps toward more declarative operators, and more consistency across the charm landscape. + +

2015: The Reactive Framework

+ + +Derived from [reactive programming](https://en.wikipedia.org/wiki/Reactive_programming), the [charms.reactive](https://charmsreactive.readthedocs.io/) library was first announced on the [Ubuntu Blog](https://ubuntu.com/blog/charming-2-0-now-with-100-more-awesome). It enabled developers to continue authoring Charm actions in the familiar “hook contexts” that were fundamental to the Services Framework, but favoured an event driven approach that simplified the development of charms. The Reactive framework also saw the introduction of *layers*, which could be reused and composed into new charms. + +

2019 - Present: The Operator Framework Ops

+ +The latest and most current framework is the [Ops Framework](https://github.com/canonical/operator), which was released with initial focus on enabling the development of charms for Kubernetes, while also simplifying the development of charms for other substrates. This framework provides a single library for developers to target when authoring charms for any substrate. The Ops Framework extends the operator pattern beyond Kubernetes and into multi-cloud, multi-substrate application management. + +The Ops Framework is event driven and implements the [observer pattern](https://en.wikipedia.org/wiki/Observer_pattern). The Juju controller emits events that charms observe and respond to at key points during an application’s [lifecycle](https://juju.is/docs/sdk/events). In-keeping with the goal of enabling developers to share and reuse quality, reviewed operator code, the Ops Framework introduced the concept of charm [libraries](https://juju.is/docs/sdk/libraries). + +In addition to the new framework, a new tool was introduced named [`charmcraft`](https://github.com/canonical/charmcraft), which enables developers to easily create new charms (templated for use with the Ops Framework), and publish/release charms to the [Charmhub](https://charmhub.io) - the home of the Open Operator Collection. + +The Ops Framework, in combination with Charmcraft, is the recommended way to write charms now. However, charms previously written using other frameworks and libraries will continue to work. + + \ No newline at end of file diff --git a/tmp/t/action.md b/tmp/t/action.md new file mode 100644 index 000000000..1a1cec356 --- /dev/null +++ b/tmp/t/action.md @@ -0,0 +1,88 @@ +(action)= +# Action + + + +> See also: {ref}`How to manage actions ` + +In Juju, an **action** is a script that is triggered via {ref}`the `juju` CLI client ` and applied to a {ref}`unit `. It contains a list of commands defined by a {ref}`charm ` to allow a {ref}`user ` with the right {ref}`access level ` to interact with an {ref}`application ` in ways specific to the application. This may include anything from creating a snapshot of a database, adding a user to a system, dumping debug information, etc. + +> See examples: [Charmhub | `kafka` > Actions](https://charmhub.io/kafka/actions), [Charmhub | `prometheus-k8s` > Actions](https://charmhub.io/prometheus-k8s/actions), etc. + + + +(Starting with `juju v.3.0`: ) + Actions are identified by integers (instead of [UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier)). + +(Starting with `juju v.3.0`: ) Running an action defaults to waiting for the output before returning. This synchronous behaviour allows actions to be easily included in command-line pipelines. + +(Starting with `juju v.3.0`: ) The execution of an action is organised into {ref}`tasks ` and {ref}`operations `. (If an action defines a named unit of work -- e.g., back up the database -- that can be executed on selected units, a task is the execution of the action on each target unit, and an operation is the group of tasks queued by running an action across one or more units.) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tmp/t/add-alert-rules.md b/tmp/t/add-alert-rules.md new file mode 100644 index 000000000..5b1562929 --- /dev/null +++ b/tmp/t/add-alert-rules.md @@ -0,0 +1,37 @@ +(add-alert-rules)= +# Add alert rules + +Support for providing alert rules through the relation is available for [`loki-k8s`](https://charmhub.io/loki-k8s) and [`prometheus-k8s`](https://charmhub.io/prometheus-k8s), both directly and through intermediary charms like [`grafana-agent-k8s`](https://charmhub.io/grafana-agent-k8s) and [`prometheus-scrape-config-k8s`](https://charmhub.io/prometheus-scrape-config-k8s). + +### Prerequisites +To be able to pass on alert rules, you also need to implement their corresponding telemetry relation interface, as well as instantiating their corresponding library classes. For Prometheus, the relation interface is either `prometheus_scrape` and `MetricsEndpointProvider`, or `prometheus_remote_write` and `RemoteWriteConsumer`. For Loki, this is `loki_push_api` and either `LokiPushApiConsumer` or `LogProxyConsumer`. If both interfaces are implemented, it would roughly look as follows: + +``` +provides: + metrics-endpoint: + interface: prometheus_scrape + +requires: + logging: + interface: loki_push_api +``` + +### Create an alert rule +An alert rule consists of a name, an expression, a duration, as well as optionally a set of labels and annotations. In this how to, we'll use an alert rule for `zinc-k8s`. For the sake of this how to, the details of the rule are of less importance. If you want to learn more about crafting alert rules, have a look at the official [Prometheus](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/) or [Loki](https://grafana.com/docs/loki/latest/rules/#example) documentation. + +```yaml +alert: ZincTargetMissing +expr: up == 0 +for: 0m +labels: + severity: critical +annotations: + summary: Prometheus target missing (instance {{ $labels.instance }}) + description: | + A Prometheus target has disappeared. An exporter might be crashed. + VALUE = {{ $value }}\n LABELS = {{ $labels }} +``` + +Save the file in `./src/prometheus_alert_rules` for Prometheus, or `./src/loki_alert_rules` for Loki, using a file name ending with `.rule`. Next time you pack and deploy your charm, the alert rules will be transferred over as you integrate it with something using a supported relation interface. + +> Note: Custom paths must be passed as an argument to the constructor (e.g. `alert_rules_path=./src/rules/loki`). \ No newline at end of file diff --git a/tmp/t/agent-introspection-juju_engine_report.md b/tmp/t/agent-introspection-juju_engine_report.md new file mode 100644 index 000000000..43b696bcd --- /dev/null +++ b/tmp/t/agent-introspection-juju_engine_report.md @@ -0,0 +1,102 @@ +(agent-introspection-juju_engine_report)= +# Agent introspection: juju_engine_report + +> See also: {ref}`Agent introspection ` + +The engine report is a window into the internals of the agent. This is primarily useful to developers to help debug problems that may be occurring in deployed systems. + +In order to manage complexity in the juju agents, there are *workers* that have very distinct and limited purpose. Workers can have dependencies on other workers. The [dependency engine](https://godoc.org/gopkg.in/juju/worker.v1/dependency) is the entity that runs the workers and deals with those dependencies. The `juju_engine_report` is the current view into the dependency engine running the agent's workers. + +# Usage +Can be run on any juju machine, expected state is different for controller machines, ha, and machines running workloads. +```code +juju_engine_report +``` +## Example output +```text +manifolds: + agent: + inputs: [] + report: + agent: machine-0 + model-uuid: 1b13f1f5-c0cf-47c5-86ae-55c393e19405 + resource-log: [] + start-count: 1 + started: 2018-08-09 22:01:39 + state: started + api-address-updater: + inputs: + - agent + - api-caller + - migration-fortress + - migration-inactive-flag + report: + servers: + - - 10.173.141.131:17070 + - 127.0.0.1:17070 + - '[::1]:17070' + resource-log: + - name: migration-inactive-flag + type: '*engine.Flag' + - name: migration-fortress + type: '*fortress.Guest' + - name: agent + type: '*agent.Agent' + - name: api-caller + type: '*base.APICaller' + start-count: 1 + started: 2018-08-09 22:01:41 + state: started + api-caller: + inputs: + - agent + - api-config-watcher + resource-log: + - name: agent + type: '*agent.Agent' + start-count: 1 + started: 2018-08-09 22:01:40 + state: started +# and many more +``` + +# Interesting Output + +* Dependencies with a larger `start_count` than others. This can indicate that the worker is bouncing. + +* Dependencies that are stopped when they should be started. Perhaps the `inputs` are not starting. + +* Dependencies that are started which should be stopped. Can prevent a unit from upgrading or migrating if the workers do not quiesce. + +* A controllers engine report will contain the model cache contents as of 2.9 + +* The report from an individual unit contains the local-state and relation, formerly in a file on the unit: +``` + report: + local-state: + hook-kind: continue + hook-step: pending + installed: true + leader: true + removed: false + started: true + stopped: false + relations: + "0": + application-members: + ntp: 0 + dying: false + endpoint: ntp-peers + is-peer: false + members: {} + relation: ntp:ntp-peers + "1": + application-members: + ubuntu: 0 + dying: false + endpoint: juju-info + is-peer: false + members: + ubuntu/0: 0 + relation: ntp:juju-info ubuntu:juju-info +``` \ No newline at end of file diff --git a/tmp/t/agent-introspection-juju_goroutines.md b/tmp/t/agent-introspection-juju_goroutines.md new file mode 100644 index 000000000..44d9c5327 --- /dev/null +++ b/tmp/t/agent-introspection-juju_goroutines.md @@ -0,0 +1,128 @@ +(agent-introspection-juju_goroutines)= +# Agent introspection: juju_goroutines + +> See also: {ref}`Agent introspection ` + +The `juju_goroutines` function allows the operator to quickly get a list of running goroutines from the agent. + +When called without any argument the goroutines for the machine agent are returned. + +The output of this is mostly just useful for Juju developers to help identify where things may be stuck. + +```bash +$ juju_goroutines +Querying @jujud-machine-0 introspection socket: /debug/pprof/goroutine?debug=1 +goroutine profile: total 234 +19 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0x951ada 0x9c34ed 0x9c0177 0x45b211 +# 0x951ad9 gopkg.in/tomb%2ev1.(*Tomb).Wait+0x49 /home/tim/go/src/gopkg.in/tomb.v1/tomb.go:113 +# 0x9c34ec github.com/juju/juju/api/watcher.(*commonWatcher).Wait+0x2c /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:138 +# 0x9c0176 github.com/juju/juju/worker/catacomb.(*Catacomb).add.func1+0x86 /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:175 + +19 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0x9599f9 0xa20249 0x9c6673 0x9c69e9 0x45b211 +# 0x9599f8 github.com/juju/juju/rpc.(*Conn).Call+0x128 /home/tim/go/src/github.com/juju/juju/rpc/client.go:148 +# 0xa20248 github.com/juju/juju/api.(*state).APICall+0x1c8 /home/tim/go/src/github.com/juju/juju/api/apiclient.go:917 +# 0x9c6672 github.com/juju/juju/api/watcher.makeWatcherAPICaller.func1+0x142 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:54 +# 0x9c69e8 github.com/juju/juju/api/watcher.(*commonWatcher).commonLoop.func2+0xe8 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:104 + +19 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0x9c6732 0x45b211 +# 0x9c6731 github.com/juju/juju/api/watcher.(*commonWatcher).commonLoop.func1+0x71 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:88 + +19 @ 0x42f59a 0x42f64e 0x43ff34 0x43fb59 0x4646a2 0x9c3438 0x45b211 +# 0x43fb58 sync.runtime_Semacquire+0x38 /snap/go/2130/src/runtime/sema.go:56 +# 0x4646a1 sync.(*WaitGroup).Wait+0x71 /snap/go/2130/src/sync/waitgroup.go:129 +# 0x9c3437 github.com/juju/juju/api/watcher.(*commonWatcher).commonLoop+0xf7 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:128 + +19 @ 0x42f59a 0x43f2b0 0x9c02d8 0x45b211 +# 0x9c02d7 github.com/juju/juju/worker/catacomb.(*Catacomb).add.func2+0x107 /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:181 + +15 @ 0x42f59a 0x43f2b0 0x9bffcd 0x45b211 +# 0x9bffcc github.com/juju/juju/worker/catacomb.Invoke.func2+0x14c /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:101 + +13 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0xe5ee92 0xe60115 0x45b211 +# 0xe5ee91 github.com/juju/juju/worker/fortress.(*fortress).Visit+0x191 /home/tim/go/src/github.com/juju/juju/worker/fortress/fortress.go:63 +# 0xe60114 github.com/juju/juju/worker/fortress.Occupy.func2+0x44 /home/tim/go/src/github.com/juju/juju/worker/fortress/occupy.go:50 + +11 @ 0x42f59a 0x42f64e 0x406c62 0x40695b 0x9c3813 0x9c6c73 0x45b211 +# 0x9c3812 github.com/juju/juju/api/watcher.(*notifyWatcher).loop+0x1c2 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:180 +# 0x9c6c72 github.com/juju/juju/api/watcher.NewNotifyWatcher.func1+0x52 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:160 + +7 @ 0x42f59a 0x43f2b0 0x9c0e35 0x9c1aca 0x9bfdd5 0x9c00a1 0x45b211 +# 0x9c0e34 github.com/juju/juju/watcher.(*NotifyWorker).loop+0x154 /home/tim/go/src/github.com/juju/juju/watcher/notify.go:90 +# 0x9c1ac9 github.com/juju/juju/watcher.(*NotifyWorker).(github.com/juju/juju/watcher.loop)-fm+0x29 /home/tim/go/src/github.com/juju/juju/watcher/notify.go:71 +# 0x9bfdd4 github.com/juju/juju/worker/catacomb.runSafely+0x54 /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:289 +# 0x9c00a0 github.com/juju/juju/worker/catacomb.Invoke.func3+0x80 /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:116 + +6 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0x951ada 0x9bf89d 0x9c11f1 0xe5e49f 0xe5bbd7 0x45b211 +# 0x951ad9 gopkg.in/tomb%2ev1.(*Tomb).Wait+0x49 /home/tim/go/src/gopkg.in/tomb.v1/tomb.go:113 +# 0x9bf89c github.com/juju/juju/worker/catacomb.(*Catacomb).Wait+0x2c /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:204 +# 0x9c11f0 github.com/juju/juju/watcher.(*NotifyWorker).Wait+0x30 /home/tim/go/src/github.com/juju/juju/watcher/notify.go:138 +# 0xe5e49e github.com/juju/juju/worker/dependency.(*Engine).runWorker.func2+0x4ce /home/tim/go/src/github.com/juju/juju/worker/dependency/engine.go:464 +# 0xe5bbd6 github.com/juju/juju/worker/dependency.(*Engine).runWorker+0x1c6 /home/tim/go/src/github.com/juju/juju/worker/dependency/engine.go:468 + +6 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0x951ada 0x9bf89d 0x9c11f1 0xe600bb 0xe5f6e1 0x45b211 +# 0x951ad9 gopkg.in/tomb%2ev1.(*Tomb).Wait+0x49 /home/tim/go/src/gopkg.in/tomb.v1/tomb.go:113 +# 0x9bf89c github.com/juju/juju/worker/catacomb.(*Catacomb).Wait+0x2c /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:204 +# 0x9c11f0 github.com/juju/juju/watcher.(*NotifyWorker).Wait+0x30 /home/tim/go/src/github.com/juju/juju/watcher/notify.go:138 +# 0xe600ba github.com/juju/juju/worker/fortress.Occupy.func1+0xca /home/tim/go/src/github.com/juju/juju/worker/fortress/occupy.go:38 +# 0xe5f6e0 github.com/juju/juju/worker/fortress.guestTicket.complete+0x40 /home/tim/go/src/github.com/juju/juju/worker/fortress/fortress.go:151 + +[extra bits snipped for brevity] +``` + +To call on a unit agent, the agent name as defined in `/var/lib/juju/agents/` should be specified as the second argument. + +```bash +$ juju_goroutines unit-ubuntu-lite-2 +Querying @jujud-unit-ubuntu-lite-2 introspection socket: /debug/pprof/goroutine?debug=1 +goroutine profile: total 216 +19 @ 0x42f59a 0x43f2b0 0x9c02d8 0x45b211 +# 0x9c02d7 github.com/juju/juju/worker/catacomb.(*Catacomb).add.func2+0x107 /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:181 + +17 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0x951ada 0x9c34ed 0x9c0177 0x45b211 +# 0x951ad9 gopkg.in/tomb%2ev1.(*Tomb).Wait+0x49 /home/tim/go/src/gopkg.in/tomb.v1/tomb.go:113 +# 0x9c34ec github.com/juju/juju/api/watcher.(*commonWatcher).Wait+0x2c /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:138 +# 0x9c0176 github.com/juju/juju/worker/catacomb.(*Catacomb).add.func1+0x86 /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:175 + +17 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0x9599f9 0xa20249 0x9c6673 0x9c69e9 0x45b211 +# 0x9599f8 github.com/juju/juju/rpc.(*Conn).Call+0x128 /home/tim/go/src/github.com/juju/juju/rpc/client.go:148 +# 0xa20248 github.com/juju/juju/api.(*state).APICall+0x1c8 /home/tim/go/src/github.com/juju/juju/api/apiclient.go:917 +# 0x9c6672 github.com/juju/juju/api/watcher.makeWatcherAPICaller.func1+0x142 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:54 +# 0x9c69e8 github.com/juju/juju/api/watcher.(*commonWatcher).commonLoop.func2+0xe8 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:104 + +17 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0x9c6732 0x45b211 +# 0x9c6731 github.com/juju/juju/api/watcher.(*commonWatcher).commonLoop.func1+0x71 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:88 + +17 @ 0x42f59a 0x42f64e 0x43ff34 0x43fb59 0x4646a2 0x9c3438 0x45b211 +# 0x43fb58 sync.runtime_Semacquire+0x38 /snap/go/2130/src/runtime/sema.go:56 +# 0x4646a1 sync.(*WaitGroup).Wait+0x71 /snap/go/2130/src/sync/waitgroup.go:129 +# 0x9c3437 github.com/juju/juju/api/watcher.(*commonWatcher).commonLoop+0xf7 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:128 + +13 @ 0x42f59a 0x42f64e 0x406c62 0x40695b 0x9c3813 0x9c6c73 0x45b211 +# 0x9c3812 github.com/juju/juju/api/watcher.(*notifyWatcher).loop+0x1c2 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:180 +# 0x9c6c72 github.com/juju/juju/api/watcher.NewNotifyWatcher.func1+0x52 /home/tim/go/src/github.com/juju/juju/api/watcher/watcher.go:160 + +11 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0xe5ee92 0xe60115 0x45b211 +# 0xe5ee91 github.com/juju/juju/worker/fortress.(*fortress).Visit+0x191 /home/tim/go/src/github.com/juju/juju/worker/fortress/fortress.go:63 +# 0xe60114 github.com/juju/juju/worker/fortress.Occupy.func2+0x44 /home/tim/go/src/github.com/juju/juju/worker/fortress/occupy.go:50 + +10 @ 0x42f59a 0x42f64e 0x406c62 0x40695b 0xa2f2b8 0x45b211 +# 0xa2f2b7 gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun+0x57 /home/tim/go/src/gopkg.in/natefinch/lumberjack.v2/lumberjack.go:379 + +10 @ 0x42f59a 0x43f2b0 0x9bffcd 0x45b211 +# 0x9bffcc github.com/juju/juju/worker/catacomb.Invoke.func2+0x14c /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:101 + +5 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0x951ada 0x9bf89d 0x9c11f1 0xe5e49f 0xe5bbd7 0x45b211 +# 0x951ad9 gopkg.in/tomb%2ev1.(*Tomb).Wait+0x49 /home/tim/go/src/gopkg.in/tomb.v1/tomb.go:113 +# 0x9bf89c github.com/juju/juju/worker/catacomb.(*Catacomb).Wait+0x2c /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:204 +# 0x9c11f0 github.com/juju/juju/watcher.(*NotifyWorker).Wait+0x30 /home/tim/go/src/github.com/juju/juju/watcher/notify.go:138 +# 0xe5e49e github.com/juju/juju/worker/dependency.(*Engine).runWorker.func2+0x4ce /home/tim/go/src/github.com/juju/juju/worker/dependency/engine.go:464 +# 0xe5bbd6 github.com/juju/juju/worker/dependency.(*Engine).runWorker+0x1c6 /home/tim/go/src/github.com/juju/juju/worker/dependency/engine.go:468 + +5 @ 0x42f59a 0x42f64e 0x406c62 0x40691b 0x951ada 0x9bf89d 0x9c11f1 0xe600bb 0xe5f6e1 0x45b211 +# 0x951ad9 gopkg.in/tomb%2ev1.(*Tomb).Wait+0x49 /home/tim/go/src/gopkg.in/tomb.v1/tomb.go:113 +# 0x9bf89c github.com/juju/juju/worker/catacomb.(*Catacomb).Wait+0x2c /home/tim/go/src/github.com/juju/juju/worker/catacomb/catacomb.go:204 +# 0x9c11f0 github.com/juju/juju/watcher.(*NotifyWorker).Wait+0x30 /home/tim/go/src/github.com/juju/juju/watcher/notify.go:138 +# 0xe600ba github.com/juju/juju/worker/fortress.Occupy.func1+0xca /home/tim/go/src/github.com/juju/juju/worker/fortress/occupy.go:38 +# 0xe5f6e0 github.com/juju/juju/worker/fortress.guestTicket.complete+0x40 /home/tim/go/src/github.com/juju/juju/worker/fortress/fortress.go:151 +[extra bits snipped for brevity] +``` \ No newline at end of file diff --git a/tmp/t/agent-introspection-juju_heap_profile.md b/tmp/t/agent-introspection-juju_heap_profile.md new file mode 100644 index 000000000..59db79982 --- /dev/null +++ b/tmp/t/agent-introspection-juju_heap_profile.md @@ -0,0 +1,51 @@ +(agent-introspection-juju_heap_profile)= +# Agent introspection: juju_heap_profile + +> See also: {ref}`Agent introspection ` + +The heap profile provides memory allocation samples. Helpful to monitor current memory usage and find memory leaks. This is primarily useful to developers to help debug problems that may be occurring in deployed systems. + + + +# Usage +Can be run on any juju machine. Suggest putting the output in a file, so it can be compared at different points in time. +```code +juju_heap_profile > heap_profile.01 +``` +## Example output +```text +heap profile: 31: 694464 [33638: 106713992] @ heap/1048576 +1: 196608 [1: 196608] @ 0x2d3fa53 0x2d3f9f6 0x2d40545 0x2d4030f 0x2d05ddb 0x2d0479f 0x2d05bf1 0x2d26c32 0x8a3cef 0x2d0381d 0x2d04146 0x2d008ba 0x2d01fbe 0x8a779b 0x8a2797 0x468fc1 +# 0x2d3fa52 github.com/juju/juju/core/logger.NewBufferedLogger+0x192 /home/heather/work-test/src/github.com/juju/juju/core/logger/buf.go:43 +# 0x2d3f9f5 github.com/juju/juju/apiserver.(*apiServerLoggers).getLogger+0x135 /home/heather/work-test/src/github.com/juju/juju/apiserver/logsink.go:66 +# 0x2d40544 github.com/juju/juju/apiserver.(*agentLoggingStrategy).init+0x1c4 /home/heather/work-test/src/github.com/juju/juju/apiserver/logsink.go:175 +# 0x2d4030e github.com/juju/juju/apiserver.newAgentLogWriteCloserFunc.func1+0xae /home/heather/work-test/src/github.com/juju/juju/apiserver/logsink.go:147 +# 0x2d05dda github.com/juju/juju/apiserver/logsink.(*logSinkHandler).ServeHTTP.func1+0x19a /home/heather/work-test/src/github.com/juju/juju/apiserver/logsink/logsink.go:196 +# 0x2d0479e github.com/juju/juju/apiserver/websocket.Serve+0xde /home/heather/work-test/src/github.com/juju/juju/apiserver/websocket/websocket.go:55 +# 0x2d05bf0 github.com/juju/juju/apiserver/logsink.(*logSinkHandler).ServeHTTP+0xb0 /home/heather/work-test/src/github.com/juju/juju/apiserver/logsink/logsink.go:270 +# 0x2d26c31 github.com/juju/juju/apiserver.(*Server).trackRequests.func1+0x111 /home/heather/work-test/src/github.com/juju/juju/apiserver/apiserver.go:1015 +# 0x8a3cee net/http.HandlerFunc.ServeHTTP+0x2e /snap/go/9605/src/net/http/server.go:2084 +# 0x2d0381c github.com/juju/juju/apiserver/httpcontext.(*BasicAuthHandler).ServeHTTP+0x3fc /home/heather/work-test/src/github.com/juju/juju/apiserver/httpcontext/auth.go:168 +# 0x2d04145 github.com/juju/juju/apiserver/httpcontext.(*QueryModelHandler).ServeHTTP+0x325 /home/heather/work-test/src/github.com/juju/juju/apiserver/httpcontext/model.go:52 +# 0x2d008b9 github.com/bmizerany/pat.(*PatternServeMux).ServeHTTP+0x199 /home/heather/work-test/pkg/mod/github.com/bmizerany/pat@v0.0.0-20160217103242-c068ca2f0aac/mux.go:117 +# 0x2d01fbd github.com/juju/juju/apiserver/apiserverhttp.(*Mux).ServeHTTP+0x9d /home/heather/work-test/src/github.com/juju/juju/apiserver/apiserverhttp/mux.go:67 +# 0x8a779a net/http.serverHandler.ServeHTTP+0x43a /snap/go/9605/src/net/http/server.go:2916 +# 0x8a2796 net/http.(*conn).serve+0x5d6 /snap/go/9605/src/net/http/server.go:1966 + +1: 196608 [1: 196608] @ 0x2ffcec5 0x2ffce0e 0x2ffd07c 0x778495 0x468fc1 +# 0x2ffcec4 github.com/juju/juju/core/logger.NewBufferedLogger+0x264 /home/heather/work-test/src/github.com/juju/juju/core/logger/buf.go:43 +# 0x2ffce0d github.com/juju/juju/worker/modelworkermanager.newModelLogger+0x1ad /home/heather/work-test/src/github.com/juju/juju/worker/modelworkermanager/recordlogger.go:28 +# 0x2ffd07b github.com/juju/juju/worker/modelworkermanager.(*modelWorkerManager).starter.func1+0x41b /home/heather/work-test/src/github.com/juju/juju/worker/modelworkermanager/modelworkermanager.go:261 +# 0x778494 github.com/juju/worker/v3.(*Runner).runWorker+0x2d4 /home/heather/work-test/pkg/mod/github.com/juju/worker/v3@v3.0.0-20220204100750-e23db69a42d2/runner.go:580 + +# and many more +``` + +# Interesting Output +The output of the heap profile can be difficult to read on its own. Using the pprof go tool can help. + +To find a memory leak, compare 2 heap profiles: +```text +go tool pprof -http localhost:8100 -base juju_heap_profile-2022-06-11.00 jujud-2.9.29/jujud juju_heap_profile-2022-06-12.16 +``` +Find jujud binaries in the [streams](https://streams.canonical.com/juju/tools/agent/) \ No newline at end of file diff --git a/tmp/t/agent-introspection-juju_machine_lock.md b/tmp/t/agent-introspection-juju_machine_lock.md new file mode 100644 index 000000000..923050629 --- /dev/null +++ b/tmp/t/agent-introspection-juju_machine_lock.md @@ -0,0 +1,68 @@ +(agent-introspection-juju_machine_lock)= +# Agent introspection: juju_machine_lock + +> See also: {ref}`Agent introspection `, {ref}`Logfile: /var/log/juju/machine-lock.log ` + +The `juju_machine_lock` introspection function was introduced in 2.3.9 and 2.4.2. + +This function actually calls into every agent on the machine to ask about the agent's view of the hook execution lock. Where the {ref}`machine-lock.log ` file shows the history of the machine lock, the introspection endpoint shows the current status of the lock, whether the agent holds the lock, or is waiting for the lock. + +During a deploy of `hadoop-kafka`, after the machine 0 has started, and is deploying the two units, we can see the following: + +``` +machine-0: + holder: none +unit-namenode-0: + holder: uniter (run install hook), holding 1m42s +unit-resourcemanager-0: + holder: none + waiting: + - uniter (run install hook), waiting 1m41s +``` +You can see that the `namenode/0` unit has the uniter worker holding the hook, and it is running the install hook, and at the time of executing the `juju_machine_lock` command it had been holding the lock for one minute and 42 seconds. + +You can additionally see that the `resourcemanager/0` unit is waiting to run its install hook. + +As the installation progresses, the subordinate units are deployed, and the output looks more like this: + +``` +machine-0: + holder: none +unit-ganglia-node-7: + holder: none + waiting: + - uniter (run install hook), waiting 1s +unit-ganglia-node-8: + holder: none +unit-namenode-0: + holder: uniter (run relation-joined (2; slave/0) hook), holding 1s +unit-resourcemanager-0: + holder: none + waiting: + - uniter (run relation-joined (1; namenode/0) hook), waiting 1s +unit-rsyslog-forwarder-ha-7: + holder: none + waiting: + - uniter (run install hook), waiting 1s +unit-rsyslog-forwarder-ha-8: + holder: none +``` + +When everything is idle, the output looks like this: + +``` +machine-0: + holder: none +unit-ganglia-node-7: + holder: none +unit-ganglia-node-8: + holder: none +unit-namenode-0: + holder: none +unit-resourcemanager-0: + holder: none +unit-rsyslog-forwarder-ha-7: + holder: none +unit-rsyslog-forwarder-ha-8: + holder: none +``` \ No newline at end of file diff --git a/tmp/t/agent-introspection-juju_metrics.md b/tmp/t/agent-introspection-juju_metrics.md new file mode 100644 index 000000000..45c1f8df5 --- /dev/null +++ b/tmp/t/agent-introspection-juju_metrics.md @@ -0,0 +1,45 @@ +(agent-introspection-juju_metrics)= +# Agent introspection: juju_metrics + +> See also: {ref}`Agent introspection ` + +The juju metrics introspection tool provides the current values of metrics which juju tracking. Some of these are more interesting over time using a tool such as grafana. More information can be found in [Monitoring Juju Controllers](https://discourse.charmhub.io/t/monitoring-juju-controllers/430/5). + +This is primarily useful to developers to help debug problems that may be occurring in deployed systems. Advance admins can use the data to see when investigation is required, or an error is hidden. + +# Usage + +Can be run on any juju machine. + +```code +juju_metrics +``` +## Example output +```text +# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{quantile="0"} 4.6921e-05 +go_gc_duration_seconds{quantile="0.25"} 8.3083e-05 +go_gc_duration_seconds{quantile="0.5"} 9.8263e-05 +go_gc_duration_seconds{quantile="0.75"} 0.00013904 +go_gc_duration_seconds{quantile="1"} 0.000921937 +go_gc_duration_seconds_sum 0.201048689 +go_gc_duration_seconds_count 1521 +# HELP go_goroutines Number of goroutines that currently exist. +# TYPE go_goroutines gauge +go_goroutines 704 +# HELP go_info Information about the Go environment. +# TYPE go_info gauge +go_info{version="go1.18.1"} 1 +# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. +# TYPE go_memstats_alloc_bytes gauge +go_memstats_alloc_bytes 2.3154496e+07 +# and many more +``` +# Interesting Output + +* `process_open_fds`: how many file descriptors are open. This should not grow over time in a stable juju deployment. + +* `juju_dependency_engine_worker_start`: how many times a dependency has started. Each dependency has an individual number. None should have a number significantly higher than the rest. This indicates the worker is restarting due to an error. Also seen in the [juju_engine_report](https://discourse.jujucharms.com/t/agent-introspection-juju-engine-report/146). + +* `go_goroutines`: a gauge for the current number of goroutines. Should not be growing in a stable config. \ No newline at end of file diff --git a/tmp/t/agent-introspection-juju_start_unit.md b/tmp/t/agent-introspection-juju_start_unit.md new file mode 100644 index 000000000..3445307ce --- /dev/null +++ b/tmp/t/agent-introspection-juju_start_unit.md @@ -0,0 +1,13 @@ +(agent-introspection-juju_start_unit)= +# Agent introspection: juju_start_unit + +> See also: {ref}`Agent introspection ` + +The `juju_start_unit` introspection function was introduced in 2.9. + +In 2.9 the machine and unit agents were combined into a single process running on juju deployed machines. This tools allows you to see the start a stopped unit agent running inside of that single process. It takes a unit name as input. Example output: + +``` +$ juju_start_unit neutron-openvswitch/0 +neutron-openvswitch/0: started +``` \ No newline at end of file diff --git a/tmp/t/agent-introspection-juju_stop_unit.md b/tmp/t/agent-introspection-juju_stop_unit.md new file mode 100644 index 000000000..b3570cc0b --- /dev/null +++ b/tmp/t/agent-introspection-juju_stop_unit.md @@ -0,0 +1,19 @@ +(agent-introspection-juju_stop_unit)= +# Agent introspection: juju_stop_unit + +> See also: {ref}`Agent introspection ` + +```{caution} + +**For Kubernetes charms:**
This is not currently supported. (In the future it may be supported through Pebble.) + +``` + +The `juju_stop_unit` introspection function was introduced in 2.9. + +In 2.9 the machine and unit agents were combined into a single process running on juju deployed machines. This tools allows you to stop a unit agent running inside of that single process. It takes a unit name as input. Example output: + +``` +$ juju_stop_unit neutron-openvswitch/0 +neutron-openvswitch/0: stopped +``` \ No newline at end of file diff --git a/tmp/t/agent-introspection-juju_unit_status.md b/tmp/t/agent-introspection-juju_unit_status.md new file mode 100644 index 000000000..07a3b27bf --- /dev/null +++ b/tmp/t/agent-introspection-juju_unit_status.md @@ -0,0 +1,16 @@ +(agent-introspection-juju_unit_status)= +# Agent introspection: juju_unit_status + +> See also: {ref}`Agent introspection ` + +The `juju_unit_status` introspection function was introduced in 2.9. + +In 2.9 the machine and unit agents were combined into a single process running on juju deployed machines. This tools allows you to see the status of agents running inside of that single process. Example output: + +``` +agent: machine-6 +units: + lxd/0: running + neutron-openvswitch/0: running + nova-compute/0: running +``` \ No newline at end of file diff --git a/tmp/t/agent-introspection.md b/tmp/t/agent-introspection.md new file mode 100644 index 000000000..898915992 --- /dev/null +++ b/tmp/t/agent-introspection.md @@ -0,0 +1,27 @@ +(agent-introspection)= +# Agent introspection + +Sometimes software doesn't do what you'd expect. Each of the {ref}`agents <117md>` that Juju runs has an internal worker for responding to introspection requests. + +As the agents start up, a goroutine is started to listen on an abstract domain socket. The listener talks HTTP, and has a number of registered endpoints. The initial work was to expose the internal golang runtime debugging endpoints for getting access to the running goroutines, CPU profiles, and memory heap profiles. This was then extended to add additional endpoints for much more Juju specific information. + +The Juju machine agent writes out a file to `/etc/profile.d/juju-introspection.sh` that defines a number of functions to easily get information out of the agent. These function names changed in Juju 2.3.9 and 2.4.2 to use underscores instead of dashes. + +- `juju_agent` +- `juju_agent_call` +- `juju_application_agent_name` +- `juju_controller_agent_name` +- `juju_cpu_profile` +- {ref}``juju_engine_report` ` +- {ref}``juju_goroutines` ` +- {ref}``juju_heap_profile` ` +- `juju_machine_agent_name` +- {ref}``juju_machine_lock` ` (since 2.3.9, 2.4.2) +- {ref}``juju_metrics` ` +- `juju_pubsub_report` (since 2.3) +- `juju_presence_report` (since 2.4) +- {ref}``juju_start_unit` ` (since 2.9) +- `juju_statepool_report` +- `juju_statetracker_report` +- {ref}``juju_stop_unit` ` (since 2.9) +- {ref}``juju_unit_status` ` (since 2.9) \ No newline at end of file diff --git a/tmp/t/agent.md b/tmp/t/agent.md new file mode 100644 index 000000000..724610b82 --- /dev/null +++ b/tmp/t/agent.md @@ -0,0 +1,116 @@ +(agent)= +# Agent + +In Juju, an **agent** is a {ref}``jujud` ` / {ref}``containeragent` ` process that works to realise the state declared by a Juju end-user with a Juju client (e.g., {ref}`the Juju CLI `) for a Juju entity (e.g., controller, model, machine, unit) via {ref}`workers `. + +On machines, an agent is managed by `systemd`. + +> See more: [Juju Dev | Agent](https://juju.is/docs/dev/agent) + +**Contents:** + +- [Controller agent](#heading--controller-agent) +- [Machine agent](#heading--machine-agent) +- [Model agent](#heading--model-agent) +- [Unit agent](#heading--unit-agent) + +

Controller agent

+ +On machine and Kubernetes clouds, a `jujud` process running workers responsible for a {ref}`controller `. This includes, among others, the `apiserver` worker, which is responsible for running the Juju API server. + + +

Machine agent

+ +On machine clouds, a `jujud` process running workers responsible for a {ref}`machine `. + +

Model agent

+ +On machine and Kubernetes clouds, a `jujud` process running workers responsible for all the {ref}`models ` associated with a given controller. + + +

Unit agent

+ +On machine / Kubernetes clouds, a `jujud` / `containeragent` process responsible for a {ref}`unit `. + +When a Juju user uses the client (e.g., types a command in the CLI), this goes to the controller agent's apiserver, which passes it on to the database. The database runs a background process that checks if anything has changed and, if so, emits an event (think "I've seen something that's changed. Do you care about it?"). The event cascades through Juju. The unit agent becomes aware of it by always polling the controller agent as part of a reconciliation loop trying to reconcile the unit agent's local state to the remote state on the controller (i.e., the state in the controller's database). + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tmp/t/application.md b/tmp/t/application.md new file mode 100644 index 000000000..25f32d72b --- /dev/null +++ b/tmp/t/application.md @@ -0,0 +1,107 @@ +(application)= +# Application + +> See also: {ref}`How to manage applications ` + + +In Juju, an **application** is a running abstraction of a {ref}`charm ` in the Juju model. It is whatever software is defined by the charm. This could correspond to a traditional software package but it could also be less or more. + +An application is always hosted within a {ref}`model ` and consists of one or more {ref}`units `. + + +An application can have {ref}`resources `, a {ref}`configuration <5471md>`, the ability to form {ref}`relations (integrations) `, and {ref}`actions `. + + \ No newline at end of file diff --git a/tmp/t/base.md b/tmp/t/base.md new file mode 100644 index 000000000..e83a66b03 --- /dev/null +++ b/tmp/t/base.md @@ -0,0 +1,10 @@ +(base)= +# Base + +> Starting with Juju 3.1, a 'base' replaces the older notion of 'series'. + +In Juju, a **base** is a way to identify a particular operating system (OS) image for a Juju {ref}`machine `. + +This can be done via the name of the OS followed by the `@` symbol and the channel of the OS that you want to target, specified in terms of `` or, optionally, `/`. For example, `ubuntu@22.04` or `ubuntu@22.04/stable`. + + \ No newline at end of file diff --git a/tmp/t/best-practices-for-production-deployments-of-cos-lite.md b/tmp/t/best-practices-for-production-deployments-of-cos-lite.md new file mode 100644 index 000000000..fc1c2f274 --- /dev/null +++ b/tmp/t/best-practices-for-production-deployments-of-cos-lite.md @@ -0,0 +1,111 @@ +(best-practices-for-production-deployments-of-cos-lite)= +# Best practices for production deployments of COS Lite + + +**Contents** + +- {ref}`Juju compatibility <12012md>` +- {ref}`Topology <12012md>` + - {ref}`Deploy in isolation <12012md>` + - {ref}`COS Alerter <12012md>` + - {ref}`Avoid pulling data cross-model <12012md>` +- {ref}`Networking <12012md>` + - {ref}`Ingress <12012md>` + - {ref}`Egress <12012md>` + - {ref}`Controller routing <12012md>` +- {ref}`Storage <12012md>` + - {ref}`Set up distributed storage <12012md>` + - {ref}`Storage volume <12012md>` +- {ref}`Maintenance <12012md>` + - {ref}`Known Issues <12012md>` +- {ref}`Upgrading <12012md>` + + + + +# Juju compatibility + +COS Lite requires Juju 3.1 to function properly. It is able to observe applications that are on Juju 2.9, but COS Lite itself needs to be deployed on a model that is Juju 3.1+. To be able to set up cross-model, cross-controller relations with existing Juju controllers and models, we therefore recommend upgrading your existing controllers (with applications that are to be observed by COS Lite) either to the latest Juju 3 version (at the time of writing 3.1.5), or to the latest version in the 2.9 track (at the time of writing: 2.9.44). + +# Topology + +## Deploy in isolation + +COS Lite should at the very least be deployed in its own model, but preferably even on its own substrate with its own controller. This limits the blast radius + of anything malfunctioning in the workloads you observe or the observability stack itself. We **strongly** recommend using [a separate three-node Microk8s cluster](https://microk8s.io/docs/high-availability). + +## COS Alerter + +Apart from COS Lite itself, the [COS Alerter](https://github.com/canonical/cos-alerter) should be deployed on separate infrastructure, preferably on completely different hardware. The purpose of the alerter is to let operators know whenever the routing of notifications from COS Lite stops working, preventing a false sense of security. + +## Avoid pulling data cross-model + +Cross-model relations using the `prometheus_scrape` interface should be avoided. Instead, deploy a Grafana agent in each of the models you want to observe and let the agents be a fan-in point pushing the data to COS. This makes for a less error-prone networking topology that is easier to reason about, especially at scale. + +# Networking + +## Ingress + +MetalLB, or an equivalent load balancer, should be configured on the Kubernetes environment COS is running on. As part of the COS Lite bundle, Traefik is deployed and configured to provide network ingressing for the bundle components. Make sure the load balancer provides Traefik with **a static IP**, or some other identity that remains stable over time. + +## Egress + +Some charms require external connectivity for the COS Lite bundle to function correctly. + +As a common requirement, the environment should be able to reach: +* Charmhub; +* the Juju registry; +* Snapcraft. + +There are other charm-specific URLs that some charms access by default: +* https://objects.githubusercontent.com/, needed by [Loki](https://charmhub.io/loki-k8s#network-requirements-9); +* stats.grafana.org, needed by [Grafana](https://charmhub.io/grafana-k8s/docs/network-requirements) and [Grafana Agent](https://charmhub.io/grafana-agent-k8s#network-requirements-5). + +To disable the functionalities that require those URLs, please refer to linked docs for the relevant charms. + +## Controller routing + +If the network topology is anything other than flat, the Juju controllers will need to be bootstrapped with `--controller-external-ips`, `--controller-external-name`, or both, so that the controllers are able to communicate over routable identities for your cross--controller relations. For example: + +``` +juju bootstrap microk8s uk8s \ + --config controller-service-type=loadbalancer \ + --config controller-external-ips=[10.0.0.2] +``` + +Note that these config values can only be set at bootstrap time, and are read-only thereafter. + +# Storage + +## Set up distributed storage +```{note} + +Note: **Do not** use the [`hostpath-storage`](https://microk8s.io/docs/addon-hostpath-storage) microk8s addon in production: + * `PersistentVolumeClaims` created by the hostpath storage provisioner are bound to the local node, so it is *impossible to move them to a different node*. + * A hostpath volume can *grow beyond the capacity set in the volume claim manifest*. + +Instead, you could use the [`rook-ceph`](https://microk8s.io/docs/addon-rook-ceph) addon together with microceph. See the [microceph tutorial](https://charmhub.io/cos-lite/docs/tutorials/distributed-storage?channel=latest/edge). + +``` + +## Storage volume + +You should come up with an appropriate [storage overlay](https://github.com/canonical/cos-lite-bundle/blob/main/overlays/storage-small-overlay.yaml) for your use case. For example, a deployment that handles roughly: + +- 1M samples/min with 150 targets +- 100k loglines/min for about 150 targets + +has a growth rate of about 50GB per day under normal operations. So, if you want a retention interval of about two months, you’ll need 3TB of storage only for the telemetry. + +# Maintenance +Before restarting a Kubernetes node with COS applications on it, you should cordon and drain it so that the StatefulSets are moved to another node. This process will ensure the least amount of downtime. + +In the event that a node goes down unexpectedly and cannot be recovered, you can manually recover the COS units by force deleting the pod and any volumeattachments that existed on the inaccessible node. The pods will then be rescheduled to a working node. + +## Known issues +- High availability during maintenance is only possible on clusters utilizing distributed storage, such as MicroCeph. +- All of the COS applications use StatefulSets, so these pods will not self-heal and deploy to another node automatically. +- The juju controller needs to be up for COS pods to start, otherwise their charm container will fail, causing the pod to go into a crash loop. + +# Upgrading +Remember to `juju refresh` with `--trust`. If omitted, you would need to `juju trust X --scope=cluster`. \ No newline at end of file diff --git a/tmp/t/binary-containeragent.md b/tmp/t/binary-containeragent.md new file mode 100644 index 000000000..9f634c80e --- /dev/null +++ b/tmp/t/binary-containeragent.md @@ -0,0 +1,4 @@ +(binary-containeragent)= +# Binary `containeragent` + +In Juju, `containeragent` is a binary that implements {ref}`agent ` functionality for the {ref}`units ` in a Juju deployment on a Kubernetes cloud. \ No newline at end of file diff --git a/tmp/t/binary-jujuc.md b/tmp/t/binary-jujuc.md new file mode 100644 index 000000000..d400fc212 --- /dev/null +++ b/tmp/t/binary-jujuc.md @@ -0,0 +1,9 @@ +(binary-jujuc)= +# Binary 'jujuc' + +In Juju, **`jujuc`** is a binary that provides a collection of command-line tools that charms can use during their hook executions to interact with the Juju environment. It comes with your Juju installation. + + +
+ +> **Contributors:** @anvial \ No newline at end of file diff --git a/tmp/t/binary-jujud.md b/tmp/t/binary-jujud.md new file mode 100644 index 000000000..061e57ef6 --- /dev/null +++ b/tmp/t/binary-jujud.md @@ -0,0 +1,6 @@ +(binary-jujud)= +# Binary 'jujud' + +In Juju, `jujud` is the executable binary that is produced at each release and which implements {ref}`agent ` functionality for all of the entities in a Juju deployment on a machine cloud (model, machine, unit, controller) and also some of the entities in a Juju deployment on a Kubernetes cloud (model, controller). + + \ No newline at end of file diff --git a/tmp/t/binding.md b/tmp/t/binding.md new file mode 100644 index 000000000..616df0508 --- /dev/null +++ b/tmp/t/binding.md @@ -0,0 +1,6 @@ +(binding)= +# Binding + +> See also: {ref}``juju deploy --bind` <6488md>`, {ref}``juju bind` <6488md>` + +In Juju, {ref}`application ` {ref}`endpoints ` can be bound to {ref}`spaces `. By binding endpoints to different spaces, Juju users can isolate the traffic used by relation endpoints. \ No newline at end of file diff --git a/tmp/t/bootstrapping.md b/tmp/t/bootstrapping.md new file mode 100644 index 000000000..b2b3c6eac --- /dev/null +++ b/tmp/t/bootstrapping.md @@ -0,0 +1,35 @@ +(bootstrapping)= +# Bootstrapping + +In Juju, **bootstrapping** refers to the process whereby a Juju {ref}`client ` creates a {ref}`controller ` on a specific {ref}`cloud `. + +A controller is needed to perform any further Juju operations, such as deploying an application. + +## Bootstrapping on a Kubernetes cloud + +![JujuOnKubernetesBootstrapProcess|690x600](upload://qDmewbwPsyW7NZ6EKPdaVNJZiwG.png) +
*Bootstrapping a controller on a Kubernetes cloud: The process.*
+ + +![JujuOnKubernetesBoostrapResult|690x680](upload://dEGK7HisD0VlcG7dJwG5l7HtfkS.jpeg) + + +
*Bootstrapping a controller on a Kubernetes cloud: The result.*
+ + +## Bootstrapping on a machine cloud + +![JujuOnMachinesBootstrapProcess|690x662](upload://ujh5UkY4e7EDPZtQfwLK8l5Mzat.png) +
*Bootstrapping a controller on a machine cloud: The process.*
+ +> See more: [Source code](https://github.com/juju/juju/blob/3.4/cmd/jujud/agent/bootstrap.go), {ref}`How to create a controller <6209md>` + +![JujuOnMachinesBootstrapResult|690x977](upload://aOuaqnjl1hhEjrhzo5UyFkvl93x.jpeg) + +
*Bootstrapping a controller on a machine cloud: The result. (Note: The machine, model, unit, and controller agent are actually all part of the same {ref}``jujud` ` process and refer in fact to trees of workers with machine, model, unit and, respectively, controller responsibility.)*
+ + + +
+ +> **Contributors:** @hmlanigan, @simonrichardson, @tmihoc \ No newline at end of file diff --git a/tmp/t/bundle.md b/tmp/t/bundle.md new file mode 100644 index 000000000..e2cb2fd7a --- /dev/null +++ b/tmp/t/bundle.md @@ -0,0 +1,22 @@ +(bundle)= +# Bundle + +> See also: {ref}`How to manage charms or bundles ` +> +> See more: [SDK | Bundle](https://juju.is/docs/sdk/bundle) + +In Juju, a **bundle** is a collection of {ref}`charms ` which have been carefully combined and configured in order to automate a multi-charm solution. + +For example, a bundle may include the `wordpress` charm, the `mysql` charm, and the relation between them. + +The operations are transparent to Juju and so the deployment can continue to be managed by Juju as if everything was performed manually (what you see in `juju status` is applications, relations, etc.; that is, not the bundle entity, but its contents). + +Bundles can be of two kinds, **regular** and **overlay**. + +- An **overlay bundle** is a local bundle you pass to `juju deploy ` via `--overlay .yaml` if you want to customise an upstream charm / bundle (usually the latter, also known as a **base bundle**) for your own needs without modifying the existing charm / bundle directly. For example, you may wish to add extra applications, set custom machine constraints or modify the number of units being deployed. They are especially useful for keeping configuration local, while being able to make use of public bundles. It is also necessary in cases where certain bundle properties (e.g. offers, exposed endpoints) are deployment specific and can _only_ be provided by the bundle's user. +- A **regular bundle** is any bundle that is not an overlay. + + +Whether regular or overlay, a bundle is fundamentally just a YAML file that contains all the applications, configurations, relations, etc., that you want your deployment to have. + +> See more: {ref}`File `.yaml` ` \ No newline at end of file diff --git a/tmp/t/canonical-observability-stack.md b/tmp/t/canonical-observability-stack.md new file mode 100644 index 000000000..6d721bb85 --- /dev/null +++ b/tmp/t/canonical-observability-stack.md @@ -0,0 +1,189 @@ +(canonical-observability-stack)= +# Canonical Observability Stack + +Highly-integrated, low-operations observability stack powered by [Juju](https://juju.is/) and [Microk8s](https://microk8s.io/). + +The Canonical Observability Stack ([COS Lite](https://charmhub.io/topics/canonical-observability-stack/editions/lite)) gathers, processes, visualizes, and alerts on telemetry signals generated by workloads running both within, and outside of, Juju. + +By leveraging the topology model of Juju to contextualize the data, and charm relations to automate configuration and integration, it provides a low-ops observability suite based on best-in-class, open-source observability tools. + +For Site-Reliability Engineers, Canonical Observability Stack provides a turn-key, out-of-the-box solution for improved day 2 operational insight. + + +## In this documentation + +| | | +|-|-| +| {ref}`Tutorial `
Get started - a hands-on introduction for new users deploying COS.
| {ref}`How-to guides `
Step-by-step guides covering key operations and common tasks | +| {ref}`Explanation `
Concepts - discussion and clarification of key topics | {ref}`Reference `
Technical information - specifications, APIs, architecture | + +## Project and community + +The Canonical Observability Stack is a member of the Ubuntu family. It’s an open source project that warmly welcomes community projects, contributions, suggestions, fixes and constructive feedback. + +* [Read our Code of conduct](https://ubuntu.com/community/code-of-conduct) +* [View our roadmap](https://github.com/orgs/canonical/projects/11/views/1) +* [Join the Discourse community forum](https://discourse.charmhub.io/c/charm/41) +* [Join the Matrix community chat](https://matrix.to/#/#cos:ubuntu.com) +* [Contribute on GitHub](https://github.com/canonical/cos-lite-bundle) + +Thinking about using the Canonical Observability Stack for your next project? [Get in touch!](https://discourse.charmhub.io/c/charm/41) + +## Navigation + + +```{dropdown} Navigation + +|Level|Path|Navlink| +|--|--|--| +| 1 | overview | {ref}`Home ` | +| 1 | tutorials | {ref}`Tutorial ` | +| 2 | tutorials/install-microk8s | {ref}`Getting started on MicroK8s ` | +| 2 | tutorials/sync-alert-rules-from-git | {ref}`Sync alert rules from Git ` | +| 2 | tutorials/instrumenting-machine-charms | {ref}`Instrumenting machine charms ` | +| 2 | tutorials/distributed-storage | {ref}`Set up distributed storage ` | +| 1 | how-to | {ref}`How-to ` | +| 2 | how-to/configure-scrape-jobs | {ref}`Configure Prometheus scrape jobs ` | +| 2 | how-to/metrics-endpoint | {ref}`Expose a metrics endpoint ` | +| 2 | how-to/add-alert-rules | {ref}`Add Alert Rules ` | +| 2 | how-to/troubleshoot-gateway-address-unavailable | {ref}`Troubleshoot Traefik "Gateway address unavailable" ` | +| 2 | how-to/troubleshoot-integrations | {ref}`Troubleshoot integrations ` +| 2 | how-to/migrate-from-lma | {ref}`Migrate from LMA to COS ` | +| 2 | how-to/add-distributed-tracing | {ref}`Add distributed tracing support with `tempo` ` | +| 2 | how-to/integrate-cos-lite-with-uncharmed-applications | {ref}`Integrate COS Lite with uncharmed applications ` | +| 2 | how-to/fix-socket-too-many-open-files | {ref}`Fix `socket: too many open files` ` | +| 2 | how-to/deploy-minio-for-s3-testing | {ref}`Deploy `minio` for s3 testing ` | +| 2 | how-to/migrate | {ref}`Migrate to COS (HA) ` | +| 1 | explanation | {ref}`Explanation ` | +| 2 | editions/lite | {ref}`COS Lite ` | +| 2 | design-goals | {ref}`Design Goals ` | +| 2 | juju-topology | {ref}`Juju topology ` | +| 2 | what-is-observability | [What is observability?](https://ubuntu.com/observability/what-is-observability) +| 2 | model-driven-observability-tag | [Model-Driven Observability](https://ubuntu.com/blog/tag/model-driven-observability) +| 2 | explanation/tls | {ref}`COS and TLS ` | +| 2 | explanation/ingress | {ref}`Charmed Ingress ` | +| 2 | explanation/telemetry-labels | {ref}`Telemetry labels ` | +| 2 | explanation/topology-labels | {ref}`Topology labels ` | +| 2 | explanation/logging | {ref}`Logging architecture ` | +| 2 | explanation/flow | {ref}`Telemetry flow ` | +| 2 | explanation/data-integrity | [Data integrity](https://discourse.charmhub.io/t/cos-lite-docs-data-integrity/15892) | +| 1 | reference | {ref}`Reference ` | +| 2| reference/solution-matrix | {ref}`Solution matrix ` | +| 2| reference/bundle-topology | {ref}`Bundle topology ` | +| 2 | reference/best-practices | {ref}`Deployment Best Practices ` | +| 2| reference/security | {ref}`Cryptographic documentation for COS-Lite charms ` | +| 2 | reference/performance | Performance | +| 3 | reference/performance/on-4cpu-8gb-ssd | {ref}`on `4cpu-8gb-ssd` ` | +| 3 | reference/performance/on-8cpu-16gb-ssd | {ref}`on `8cpu-16gb-ssd` ` | +| 2 | reference/kubernetes-charms | Kubernetes Charms | +| 3 | reference/traefik-k8s | [Traefik K8s](https://charmhub.io/traefik-k8s) | +| 3 | reference/alertmanager-k8s | [Alertmanager K8s](https://charmhub.io/alertmanager-k8s) | +| 3 | reference/prometheus-k8s | [Prometheus K8s](https://charmhub.io/prometheus-k8s) | +| 3 | reference/prometheus-scrape-target-k8s | [Scrape Target K8s](https://charmhub.io/prometheus-scrape-target-k8s) | +| 3 | reference/prometheus-scrape-config-k8s | [Scrape Config K8s](https://charmhub.io/prometheus-scrape-config-k8s) | +| 3 | reference/loki-k8s | [Loki K8s](https://charmhub.io/loki-k8s) | +| 3 | reference/grafana-k8s | [Grafana K8s](https://charmhub.io/grafana-k8s) | +| 3 | reference/grafana-agent-k8s | [Grafana Agent K8s](https://charmhub.io/grafana-agent-k8s) | +| 3 | reference/catalogue-k8s | [Catalogue K8s](https://charmhub.io/catalogue-k8s) | +| 3 | reference/mimir-k8s | [Mimir K8s](https://charmhub.io/mimir-k8s) | +| 3 | reference/cos-configuration-k8s | [COS Config K8s](https://charmhub.io/cos-configuration-k8s) | +| 3 | reference/karma-k8s | [Karma K8s](https://charmhub.io/karma-k8s) | +| 3 | reference/karma-alertmanager-proxy-k8s | [Karma Alertmanager Proxy K8s](https://charmhub.io/karma-alertmanager-proxy-k8s) | +| 3 | reference/juju-topology | {ref}`Juju Topology labels ` | + +| 2 | reference/machine-charms | Machine Charms | +| 3 | reference/grafana-agent | [Grafana Agent](https://charmhub.io/grafana-agent) | +| 3 | reference/cos-proxy | [COS Proxy](https://charmhub.io/cos-proxy) | + +``` + +## Redirects + +```{dropdown} Mapping table + +| Path | Location | +|--|--| +| /topics/canonical-observability-stack/editions/ha | /topics/canonical-observability-stack/editions/standard | +| /topics/canonical-observability-stack/on MicroK8s | /topics/canonical-observability-stack/install/microk8s | +| /topics/canonical-observability-stack/on%20MicroK8s | /topics/canonical-observability-stack/install/microk8s | +| /topics/canonical-observability-stack/install/microk8s | /topics/canonical-observability-stack/tutorials/install-microk8s + +``` + +------------------------- + +0x12b | 2022-01-26 13:08:44 UTC | #4 + +Updated article to reflect the renaming of LMA Light to COS Lite + +------------------------- + +erik-lonroth | 2022-02-01 08:44:26 UTC | #5 + +This is very interesting. Would you consider running a demo in the community workshop on this? @tmihoc @hallback @anvial @mmrezaie @emcp would perhaps also be interested. + +A questions is how Nagios comes in here, or if it isn't? I'd love to know what I should include in my own charms to quickly get support from your stack here. What interfaces/relations I should provide etc. + +------------------------- + +michele-mancioppi | 2022-02-01 20:30:47 UTC | #6 + +I definitely think we can arrange a community demo :slight_smile: + +About Nagios: Nagios *itself* is not in the picture. Alerts are generated by Prometheus or Loki. The NRPE checks embedded in current charms are supported through an adaptor charm, the [cos-proxy operator](https://charmhub.io/cos-proxy), that implements the nrpe-external-master and similar LMA relations, including many of those supported by the Prometheus 2 charm. It runs a Prometheus nrpe-exporter and we will then define rules in Prometheus to raise alerts when checks go bad. The alerts are routed to Alertmanager and, from there, pretty much wherever you want :-) + +In general our goal is very much to provide a smooth transition for charms supporting previous relations, as well as provide more consistent relation interfaces going forward. + +------------------------- + +erik-lonroth | 2022-02-02 00:53:55 UTC | #7 + +This is super interesting! + +How about you run the show in two weeks? + +I would love to learn how to work with this as I can see many scenarios where it will work. + +Does it require me to know K8? + +------------------------- + +michele-mancioppi | 2022-02-02 04:58:24 UTC | #8 + +We’ll work with the folks that organise the community hours to see which slot works. + +In terms of having to know K8s: not really. We are developing COS as an appliance, which you should not have much to fumble with, and the abstractions we are exposing to the Juju admin do not seem leaky. But it would actually be an excellent datapoint to get the opinion of someone on that who is not deeply marinated in Kubernetes the way I am :-) + +------------------------- + +erik-lonroth | 2022-02-03 21:33:57 UTC | #9 + +[quote="michele-mancioppi, post:8, topic:5132"] +But it would actually be an excellent datapoint to get the opinion of someone on that who is not deeply marinated in Kubernetes the way I am +[/quote] + +Why don't you let us try it in the community with your guidance? + +If we succeed = 😀 + +If we fail = 😄 + +------------------------- + +michele-mancioppi | 2022-02-04 13:10:00 UTC | #10 + +We have not been yet diligent to a sufficient degree in documenting the various moving parts :slight_smile: + +But we are developing in the open and are looking forward to feedback, so please by all means do: https://charmhub.io/cos-lite + +------------------------- + +pedroleaoc | 2022-04-07 08:33:18 UTC | #11 + + + +------------------------- + +0x12b | 2023-11-17 12:26:37 UTC | #12 + +Added link to how-to article on resolving ulimit issues. \ No newline at end of file diff --git a/tmp/t/channel.md b/tmp/t/channel.md new file mode 100644 index 000000000..6872f2319 --- /dev/null +++ b/tmp/t/channel.md @@ -0,0 +1,76 @@ +(channel)= +# Channel + +> See also: [`juju deploy --channel`](https://juju.is/docs/olm/juju-deploy) + +In Charmcraft, a **channel** is a way to use a charm in a particular stage of development. + +```{important} + +The notion of 'channel' in charms is entirely parallel to the notion of 'channel' in snaps / the *craft world more generally. +> See more: [Snapcraft | Channels](https://snapcraft.io/docs/channels) + +``` + +**Contents:** + +- [Components](#heading--components) + +

Components

+ +A charm channel consists of three pieces, in this order: `//`. + + +- [Track](#heading--track) +- [Risk](#heading--risk) +- [Branch](#heading--branch) + + + +

Track

+ +> See also: {ref}`How to create a track for your charm ` + +The `` is a way to collect multiple supported releases of your charm under the same name. + +When deploying a charm, specifying a track is optional; if you don't specify any, the default option is the `latest`. + +To ensure consistency between tracks of the same charm, tracks must comply with a [guardrail](#heading--track-guardrail). + + + +

Track guardrail

+> See also: {ref}`How to request a track guardrail <6562md>` + + +A **track guardrail** is a regex generated by a Charmhub admin at the request of a charm author whose purpose is to ensure that any new track of the charm complies with the specific pattern selected by the charm author for the charm, usually in conformity with the pattern established by the upstream workload (e.g., no numbers, cf, e.g., [OpenStack](https://docs.openstack.org/charm-guide/latest/project/charm-delivery.html); numbers in the major.minor format; just integers; etc.) + + + + + + +

Risk

+ + +The `` refers to one of the following risk levels: + +- **stable**: (default) This is the latest, tested, working stable version of the charm. +- **candidate**: A release candidate. There is high confidence this will work fine, but there may be minor bugs. +- **beta**: A beta testing milestone release. +- **edge**: The very latest version - expect bugs! + +

Branch

+ +Finally, the `` is an optional finer subdivision of a channel for a published charm that allows for the creation of short-lived sequences of charms (guaranteed for only 30 days without modification) that can be pushed on demand by charm developers to help with fixes or temporary experimentation. Note that, if you use `--channel` to specify a branch, you must specify a track and a risk level as well. + + + \ No newline at end of file diff --git a/tmp/t/charm-development-best-practices.md b/tmp/t/charm-development-best-practices.md new file mode 100644 index 000000000..e37652076 --- /dev/null +++ b/tmp/t/charm-development-best-practices.md @@ -0,0 +1,609 @@ +(charm-development-best-practices)= +# Charm development best practices + +This document describes the current best practices around developing and contributing to a charm. + +**Contents:** +- [Conventions](#heading--conventions) + - [Programming languages and frameworks](#heading--programming-languages-and-frameworks) + - [Naming](#heading--naming) + - [State](#heading--state) + - [Revisions](#heading--revisions) + - [Resources](#heading--resources) + - [Integrations](#heading--integrations) + - [Application and unit statuses](#heading--application-and-unit-statuses) + - [Logging](#heading--logging) + - [Templating](#heading--templating) + - [Frequency](#heading--frequency) + - [Sensitive information](#heading--sensitive-information) + - [Charm configuration option description](#heading--charm-configuration-option-description) + - [When to use Python or Shell](#heading--when-to-use-python-or-shell) +- [Documentation](#heading--documentation) +- [Custom events](#heading--custom-events) +- [Backward compatibility](#heading--backward-compatibility) +- [Dependency management](#heading--dependency-management) +- [Code style](#heading--code-style) + - [Error Handling](#heading--error-handling) + - [Clarity](#heading--clarity) + - [User experience / UX](#heading--user-experience---ux) + - [Event handler visibility](#heading--event-handler-visibility) + - [Subprocess calls within Python](#heading--subprocess-calls-within-python) + - [Linting](#heading--linting) + - [Docstrings](#heading--docstrings) + - [Class layout](#heading--class-layout) + - [String formatting](#heading--string-formatting) + - [Type hints](#heading--type-hints) +- [Patterns](#heading--patterns) + - [Fetching network information](#heading--fetching-network-information) + - [Using the bind address](#heading--using-the-bind-address) + - [Using FQDN](#heading--using-fqdn) + - [Using the ingress URL](#heading--using-the-ingress-url) + - [Random values](#heading--random-values) +- [Testing](#heading--testing) +- [Unit tests](#heading--unit-tests) + - [Functional tests](#heading--functional-tests) + - [Integration tests](#heading--integration-tests) +- [Recommended tooling](#heading--recommended-tooling) + - [Continuous integration](#heading--continuous-integration) + - [Linters](#heading--linters) +- [Common integrations](#heading--common-integrations) + - [Observability](#heading--observability) + - [Metrics](#heading--metrics) + - [Logs](#heading--logs) + - [Alert rules](#heading--alert-rules) + - [Grafana Dashboards](#heading--grafana-dashboards) +- [Security considerations](#heading--security-considerations) +- [Example repositories](#heading--example-repositories) + +

Conventions

+ +

Programming languages and frameworks

+ +The primary programming language charms are written in Python, and the primary framework for developing charms is the [Python Operator Framework](https://github.com/canonical/operator), or ops. + +The recommended way to import the library is to write `import ops` and use it similar to: + +```python +import ops +class MyCharm(ops.CharmBase): + ... + def _my_event_handler(self, event: ops.EventBase): # or a more specific type + ... + self.unit.status = ops.ActiveStatus() +... +if __name__ == "__main__": + ops.main(MyCharm) +``` + +

Naming

+ +Use clear and consequent naming. For example, prometheus includes multiple charms cover different scenarios: + +* Running on bare metal as a machine charm, under the name prometheus +* Running in kubernetes as a k8s charm, under the name prometheus-k8s + +> See more: {ref}`Charm naming guidelines ` + + +When naming configuration items or actions, prefer lowercase alphanumeric names, separated with dashes if required. For example `timeout` or `enable-feature`. For charms that have already standardized on underscores, it is not necessary to change them, and it is better to be consistent within a charm then to have some values be dashes and some be underscores. + +

State

+ +Write your charm to be stateless. If a charm needs to track state between invocations it should be done as described in the guide on [uses and limitations of stored state](https://juju.is/docs/sdk/stored-state-uses-limitations). + +For sharing state between units of the same application, use [peer relation data bags](https://juju.is/docs/sdk/relations#heading--peer-relation-example). + +Do not track the emission of events, or elements relating to the charm’s lifecycle, in a state. Where possible, construct this information by accessing the model, i.e. self.model, and the charm’s relations (peer relations or otherwise). + +

Revisions

+ +**If your charm's workload is delivered by a snap:** Pin the snap revision to the charm revision by hard-coding it in the charm. Examples: + +- [`mysql-router-operator`](https://github.com/canonical/mysql-router-operator/blob/5d89fb6acd08ec3ea2e65356fad520ad5d9862d9/src/snap.py#L25) (the snap revision is hard-coded directly; note that this charm also keeps it in sync with the [workload version](https://github.com/canonical/mysql-router-operator/blob/5d89fb6acd08ec3ea2e65356fad520ad5d9862d9/workload_version) -- something database charms like to do because, to minimize risk and cost, they favor in-place upgrades, and this helps them check compatibility prior to the upgrade) +- [`postgresql-operator`](https://github.com/canonical/postgresql-operator/blob/37570c761deffd11e0e83a324fc256fc6e174adb/src/constants.py#L38) (the snap revision is hard-coded in a format suitable for when you have multiple workload snaps; because the charm is a multi-architecture charm, the revision is also pinned to a particular architecture, and will be selected depending on [the current architecture](https://github.com/canonical/postgresql-operator/blob/37570c761deffd11e0e83a324fc256fc6e174adb/src/charm.py#L1347)) + +

Resources

+ +Resources can either be of the type oci-image or file. When providing binary files as resources, provide binaries for all CPU architectures your binary might end up being run on. An example of this can be found [here](https://github.com/canonical/prometheus-operator/blob/9fddf95fe29d3a63f8c131f63d7e93d98257d179/metadata.yaml#L37). + +Implement the usage of these resources in such a way that the user may build a binary for their architecture of choice and supply it themselves. An example of this can be found [here](https://github.com/canonical/prometheus-operator/blob/9fddf95fe29d3a63f8c131f63d7e93d98257d179/lib/charms/prometheus_k8s/v0/prometheus_scrape.py#L1846-L1857). + +

Integrations

+ + +Use [Charm Libraries](https://juju.is/docs/sdk/libraries) to distribute code that simplifies implementing any integration for people who wish to integrate with your application. Name charm libraries after the integration interfaces they manage ([example](https://charmhub.io/prometheus-k8s/libraries/prometheus_scrape)). + +Implement a separate class for each side of the relation in the same library, for instance: + +```python +class MetricsEndpointProvider(ops.Object): + +# … + +class MetricsEndpointRequirer(ops.Object): + +# … +``` + +These classes should do whatever is necessary to handle any relation events specific to the relation interface you are implementing, throughout the lifecycle of the application. By passing the charm object into the constructor to either the Provider or Requirer, you can gain access to the on attribute of the charm and register event handlers on behalf of the charm, as required. + +

Application and unit statuses

+ + +Only make changes to the charm’s application or unit status directly within an event handler. + +An example: + +```python +class MyCharm(ops.CharmBase): + + # This is an event handler, and can therefore set status + def _on_config_changed(self, event): + if self._some_helper(): + self.unit.status = ops.ActiveStatus() + + # This is a helper method, not an event handler, so don't set status here + def _some_helper(self): + # do stuff + return True +``` + +Libraries should never mutate the status of a unit or application. Instead, use return values, or raise exceptions and let them bubble back up to the charm for the charm author to handle as they see fit. + +In cases where the library has a suggested default status to be raised, use a custom exception with a .status property containing the suggested charm status as shown [here](https://github.com/juju-solutions/resource-oci-image/blob/fca2ff473e96db170811b81ffe70505ac70612e8/oci_image.py#L57) or [here](https://github.com/canonical/kubeflow-dashboard-operator/blob/2e96dcea52ce6995b49ab439c0d5c04ead22c08c/src/charm.py#L28). The calling charm can then choose to accept the default by setting self.unit.status to raised_exception.status or do something else. + +

Logging

+ +

Templating

+ +Use the default Python logging module. The default charmcraft init template will set this up for you. Do not build strings for the logger. This avoids the string formatting if it's not needed at the given log level. + +Prefer + +```python +logger.info("something %s", var) +``` + +over + +```python +logger.info("something {}".format(var)) + +# or + +logger.info(f"something {var}") +``` + +Due to logging features, using f-strings or str.format is a security risk (see [issue46200](https://bugs.python.org/issue46200)) when creating log messages and also causes the string formatting to be done even if the log level for the message is disabled. + +

Frequency

+ + +Avoid spurious logging, ensure that log messages are clear and meaningful and provide the information a user would require to rectify any issues. + +Avoid excess punctuation or capital letters. + +```python +logger.error("SOMETHING WRONG!!!") +``` + +is significantly less useful than + +```python +logger.error("configuration failed: '8' is not valid for field 'enable_debug'.") +``` + +

Sensitive information

+ + +Never log credentials or other sensitive information. If you really have to log something that could be considered sensitive, use the trace error level. + +

Charm configuration option description

+ +The description of configuration in config.yaml is a string type (scalar). YAML supports two types of formats for that: block scalar and flow scalar (more information in [YAML Multiline](https://yaml-multiline.info/)). Prefer to use the block style (using |) to keep new lines. Using > will replace new lines with spaces and make the result harder to read on Charmhub.io. + +

When to use Python or Shell

+ +Limit the use of shell scripts and commands as much as possible in favour of writing Python for charm code. There needs to be a good reason to use a shell command rather than Python. Examples where it could be reasonable to use a script include: + +* Extracting data from a machine or container which can't be obtained through Python +* Issuing commands to applications that do not have Python bindings (e.g., starting a process on a machine) + +

Documentation

+ +Documentation should be considered the user’s handbook for using a charmed application safely and successfully. + +It should apply to the charm, and not to the application that is being charmed. Assume that the user already has basic competency in the use of the application. Documentation should include: + +* on the [home page](https://docs.google.com/document/d/1O1COQz5SdHb4aQIEVmNioB1baUE0ZswDYqaVeM_6fM8/edit?usp=sharing): what this is, what it does, what problem it solves, who it’s useful for +* an [introductory tutorial](https://diataxis.fr/tutorials/) that gives the new user an experience of what it’s like to use the charm, and an insight into what they’ll be able to do with it - by the end of this, they should have deployed the charm and had a taste of success with it +* [how-to guides](https://diataxis.fr/how-to-guides/) that cover common tasks/problems/application cases +* [reference](https://diataxis.fr/reference/) detailing what knobs and controls the charm offers +* [guides that explain](https://diataxis.fr/explanation/) the bigger picture, advise on best practice, offer context + +A good rule of thumb when testing your documentation is to ask yourself whether it provides a means for “guaranteed getting started”. You only get one chance at a first impression, so your quick start should be rock solid. + +The front page of your documentation should not carry information about how to build, test or deploy the charm from the local filesystem: put this information in other documentation pages specific to the development of and contribution to your charm. This information can live as part of your [Charm Documentation](https://juju.is/docs/sdk/charm-documentation), or in the version control repository for your charm ([example](https://github.com/jnsgruk/kubernetes-dashboard-operator/blob/main/CONTRIBUTING.md)). + +If you’d like some feedback or advice on your Charm’s documentation, ask in our [Mattermost Charmhub Docs channel](https://chat.charmhub.io/charmhub/channels/docs). + +

Custom events

+ + +Charms should never define custom events themselves. They have no need for emitting events (custom or otherwise) for their own consumption, and as they lack consumers, they don’t need to emit any for others to consume either. Instead, custom events should only be defined in a library. + +

Backward compatibility

+ + +When authoring your charm, consider the target Python runtime. Kubernetes charms will have access to the default Python version on the Ubuntu version they are running. + +Your code should be compatible with the operating system and Juju versions it will be executed on. For example, if your charm is to be deployed with Juju 2.9, its Python code should be compatible with Python 3.5. + +Compatibility checks for Python 3.5 can be automated [in your CI](https://github.com/canonical/grafana-operator/pull/42) or using [mypy](https://github.com/canonical/alertmanager-operator/pull/47/files#diff-ef2cef9f88b4fe09ca3082140e67f5ad34fb65fb6e228f119d3812261ae51449). + +

Dependency management

+ + +External dependencies must be specified in a requirements.txt file. If your charm depends on other charm libraries, you should vendor and version the library you depend on (see the [prometheus-k8s-operator](https://github.com/canonical/prometheus-operator/tree/main/lib/charms)). This is the default behaviour when using charmcraft fetch-lib. For more information see the docs on [Charm Libraries](https://juju.is/docs/sdk/libraries). + +Including an external dependency in a charm is a significant choice. It can help with reducing the complexity and development cost. However, a poor dependency pick can lead to critical issues, such as security incidents around the software supply chain. The [Our Software Dependency Problem](https://research.swtch.com/deps) article describes how to assess dependencies for a project in more detail. + +

Code style

+ +

Error Handling

+ + +Use the following mapping of errors that can occur to the state the charm should enter: + +* Automatically recoverable error: the charm should go into maintenance status until the error is resolved and then back to active status. Examples of automatically recoverable errors are those where the operation that resulted in the error can be retried. +* Operator recoverable error: The charm should go into the blocked state until the operator resolves the error. An example is that a configuration option is invalid. +* Unexpected/unrecoverable error: the charm should enter the error state. The operator will need to file a bug and potentially downgrade to a previous version of the charm that doesn’t have the bug. + +The charm should not catch the parent Exception class and instead only catch specific exceptions. When the charm is in error state, the event that caused the error will be retried by juju until it can be processed without an error. More information about charm statuses is in the [juju documentation](https://juju.is/docs/sdk/constructs#heading--statuses). + +

Clarity

+ + +Charm authors should choose clarity over cleverness when writing code. A lot more time is spent reading code than writing it, opt for clear code that is easily maintained by anyone. For example, don't write nested, multi-line list comprehensions, and don't overuse itertools. + +

User experience / UX

+ +Charms should aim to keep the user experience of the operator as simple and obvious as possible. If it is harder to use your charm than to set up the application from scratch, why should the user even bother with your charm? + +Ensure that the application can be deployed without providing any further configuration options, e.g. + +```text +juju deploy foo +``` + +is preferable over + +```text +juju deploy foo --config something=value +``` + +This will not always be possible, but will provide a better user experience where applicable. Also consider if any of your configuration items could instead be automatically derived from a relation. + +A key consideration here is which of your application’s configuration options you should initially expose. If your chosen application has many config options, it may be prudent to provide access to a select few, and add support for more as the need arises. + +For very complex applications, consider providing “configuration profiles” which can group values for large configs together. For example, "profile: large" that tweaks multiple options under the hood to optimise for larger deployments, or "profile: ci" for limited resource usage during testing. + +

Event handler visibility

+ + +Charms should make event handlers private: _on_install, not on_install. There is no need for any other code to directly access the event handlers of a charm or charm library. + +

Subprocess calls within Python

+ + +For simple interactions with an application or service or when a high quality Python binding is not available, a Python binding may not be worth the maintenance overhead and shell/ subprocess should be used to perform the required operations on the application or service. + +For complex use cases where a high quality Python binding is available, using subprocess or the shell for interactions with an application or service will carry a higher maintenance burden than using the Python binding. In these cases, the Python binding for the application or service should be used. + +When using subprocess or the shell: + +* Log `exit_code` and `stderr` when errors occur. +* Use absolute paths to prevent security issues. +* Prefer subprocess over the shell +* Prefer array-based execution over building strings to execute + +For example: + +```python +import subprocess + +try: + # Comment to explain why subprocess is used. + result = subprocess.run( + # Array based execution. + ["/usr/bin/echo", "hello world"], + capture_output=True, + check=True, + ) + logger.debug("Command output: %s", result.stdout) +except subprocess.CalledProcessError as err: + logger.error("Command failed with code %i: %s", err.returncode, err.stderr) + raise + +``` + +

Linting

+ +Use linters to make sure the code has a consistent style regardless of the author. An example configuration can be found in the [pyproject.toml the charmcraft init template](https://github.com/canonical/charmcraft/blob/main/charmcraft/templates/init-simple/pyproject.toml.j2). + +This config makes some decisions about code style on your behalf. At the time of writing, it configures type checking using Pyright, code formatting using Black, and uses ruff to keep imports tidy and to watch for common coding errors. + +In general, run these tools inside a tox environment named lint, and one called fmt alongside any testing environments required. See the [Recommended tooling](#heading--recommended-tooling) section for more details. + +

Docstrings

+ + +Charms should have docstrings. Use the [Google docstring](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings) format when writing docstrings for charms. To enforce this, use [ruff](https://github.com/charliermarsh/ruff) as part of our [linter suite](https://juju.is/docs/sdk/styleguide#heading--linters). See [this example](https://google.github.io/styleguide/pyguide.html#doc-function-raises) from the Google style guide. + +

Class layout

+ + +The class layout of a charm should be organised in the following order: + +* Constructor (inside which events are subscribed to, roughly in the order they would be activated) +* Factory methods (classmethods), if any +* Event handlers, placed in order that they’re subscribed to +* Public methods +* Private methods + +Further, the use of nested functions is discouraged, instead, use either private methods or module-level functions. Likewise, the use of static methods that could be functions defined near the class in the same module is also discouraged. + +

String formatting

+ +f-strings are the preferred way of including variables in a string. For example: + +```python +​​foo = "substring" + +# .format is not preferred + +bar = "string {}".format(foo) + +# string concatenation is not preferred + +bar = "string " + foo + +# f-strings are preferred + +bar = f"string {foo}" +``` + +The only exception to this is logging, where %-formatting should be used. See [above](https://docs.google.com/document/d/1H5W2oi7cuTaz86taTQiITv49jpJNKzzDZ2G25IQpE4w/edit#heading=h.84ez402ai5cq). + +Note: f-strings are supported as of Python 3.6. Charms that are based on pre-Bionic Ubuntu versions or libraries needing to support these versions will not have access to f-strings. + +

Type hints

+ +Declare type hints on function parameters, return values, and class and instance variables. + +Type hints should be checked during development and CI using [Pyright](https://microsoft.github.io/pyright/#/). Although there are other options, Pyright is the recommended one, as it is what is used in `ops` itself (see an [example Pyright config](https://github.com/canonical/alertmanager-k8s-operator/blob/main/pyproject.toml#L31)). More information on type hints can be found in [PEP 484](https://peps.python.org/pep-0484/) and related PEPs. + +This will help users know what functions expect as parameters and return and catch more bugs earlier. + +Note that there are some cases when type hints might be impractical, for example: + +* dictionaries with many nested dictionaries +* decorator functions + +

Patterns

+ +

Fetching network information

+ + +As a majority of workloads integrate through the means of network traffic, it is common that a charm needs to share its network address over any established relations, or use it as part of its own configuration. + +Depending on timing, routing, and topology, some approaches might make more sense than others. Likewise, charms in Kubernetes won’t be able to communicate their cluster FQDN externally, as this address won’t be routable outside of the cluster. + +Below you’ll find a couple of different alternatives. + +

Using the bind address

+ + +This alternative has the benefit of not relying on name resolution to work. Trying to get a bind_address too early after deployment might result in a None if the DHCP has yet to assign an address. + +```python +@property +def _address(self) -> Optional[str]: + binding = self.model.get_binding(self._peer_relation_name) + address = binding.network.bind_address + + return str(address) if address else None +``` + +

Using FQDN

+ + +This alternative has the benefit of being available immediately after the charm is deployed, which eliminates the possible race of the previous example. However, it will in most cases only work for deployments that share a DNS provider (for instance inside a Kubernetes cluster), while in a cross-substrate deployment it most likely won’t resolve. + +```python +import socket +... + + @property + def address(self) -> str: + """Unit's hostname.""" + return socket.getfqdn() +``` + + +

Using the ingress URL

+ + +This alternative has the benefit of working in most Kubernetes deployment scenarios, even if the opposite side of the relation is not within the same cluster, or even the same substrate. + +This does however add a dependency, as it requires an ingress to be available. In the example below, the [traefik](https://charmhub.io/traefik-k8s) ingress is used, falling back to the FQDN if it isn’t available. + +Further, keep in mind that unless using ingress-per-unit, the ingress url will not point to the individual unit, but based on the ingress strategy (i.e. per app, or the leader). + +```python +@property +def external_url(http://github.com/self) -> str: + try: + if ingress_url := self.ingress.url: + return ingress_url + except ModelError as e: + logger.error("Failed obtaining external url: %s. Shutting down?", e) + return f"http://{socket.getfqdn()}:{self._port}" +``` + +

Random values

+ + +While creating tests, sometimes you need to assign values to variables or parameters in order to simulate a user behaviour, for example. In this case, instead of using constants or fixed values, consider using random ones generated by secrets.token_hex(). This is preferred because: + +* If you use the same fixed values in your tests every time, your tests may pass even if there are underlying issues with your code. This can lead to false positives and make it difficult to identify and fix real issues in your code. +* Using random values generated by secrets.token_hex() can help to prevent collisions or conflicts between test data. +* In the case of sensitive data, if you use fixed values in your tests, there is a risk that may be exposed or leaked, especially if your tests are run in a shared environment. + +For example: + +```python +from secrets import token_hex + +email = token_hex(16) +``` + +

Testing

+ + +Charms should have tests to verify that they are functioning correctly. These tests should cover the behaviour of the charm both in isolation (unit tests) and when used with other charms (integration tests). Charm authors should use [tox](https://tox.wiki/en/latest/index.html) to run these automated tests. + +The unit and integration tests should be run on the same minor Python version as is shipped with the OS as configured under the charmcraft.yaml bases.run-on key. With tox, for Ubuntu 22.04, this can be done using: + +```text +[testenv] + +basepython = python3.10 +``` + +

Unit tests

+ + +Unit tests are written using the unittest library shipped with Python or [pytest](https://pypi.org/project/pytest/). To facilitate unit testing of charms, use the testing harness specifically designed for charms which is available in [`ops.testing`](https://ops.readthedocs.io/en/latest/#module-ops.testing). An example of charm unit tests can be found [here](https://github.com/canonical/prometheus-operator/blob/main/tests/unit/test_charm.py). + +

Functional tests

+ + +Functional tests in charms often take the form of integration-, performance- and/or end-to-end tests. + +Use the [pytest](https://pytest.org/) library for integration and end-to-end tests. [Pytest-operator](https://github.com/charmed-kubernetes/pytest-operator) is a testing library for interacting with Juju and your charm in integration tests. Examples of integration tests for a charm can be found in the [prometheus-k8-operator repo](https://github.com/canonical/prometheus-operator/blob/main/tests/integration/test_charm.py). + +

Integration tests

+ + +Integration tests ensure that the charm operates as expected when deployed by a user. Integration tests should cover: + +* Charm actions +* Charm integrations +* Charm configurations +* That the workload is up and running, and responsive + +When writing an integration test, it is not sufficient to simply check that Juju reports that running the action was successful. Additional checks need to be executed to ensure that whatever the action was intended to achieve worked. + + + +

Continuous integration

+ + +The quality assurance pipeline of a charm should be automated using a continuous integration (CI) system. The CI should be configured to use the same version of Ubuntu as configured under the charmcraft.yaml bases.run-on key. + +For repositories on GitHub, use the [actions-operator](https://github.com/charmed-kubernetes/actions-operator), which will take care of setting up all dependencies needed to be able to run charms in a CI workflow. You can see an example configuration for linting and testing a charm using Github Actions [here](https://github.com/jnsgruk/zinc-k8s-operator/blob/main/.github/workflows/pull-request.yaml). + +The [charming-actions repo](https://github.com/canonical/charming-actions) provides GitHub actions for common workflows, such as publishing a charm or library to charmhub. + +The automation should also allow the maintainers to easily see whether the tests failed or passed for any available commit. Provide enough data for the reader to be able to take action, i.e. dumps from juju status, juju debug-log, kubectl describe and similar. To have this done for you, you may integrate [charm-logdump-action](https://github.com/canonical/charm-logdump-action) into your CI workflow. + +

Linters

+ + +At the time of writing, linting modules commonly used by charm authors include [black](https://github.com/psf/black), [ruff](https://github.com/charliermarsh/ruff), and [codespell](https://github.com/codespell-project/codespell). [bandit](https://bandit.readthedocs.io/en/latest/) can be used to statically check for common security issues. [Pyright](https://microsoft.github.io/pyright/#/) is recommended for static type checking (though MyPy can be used as well)., + +

Common integrations

+ +

Observability

+ +Charms need to be observable, meaning that they need to allow the Juju administrator to reason about their internal state and health from the outside. This means that charm authors need to ensure that their charms expose appropriate telemetry, alert rules and dashboards. + +

Metrics

+ + +Metrics should be provided in a format compatible with Prometheus. This means that the metrics either should be provided using the [prometheus_remote_write](https://github.com/canonical/charm-relation-interfaces/tree/main/interfaces/prometheus_remote_write/v0) or the [prometheus_scrape](https://github.com/canonical/charm-relation-interfaces/tree/main/interfaces/prometheus_scrape/v0) relation interface. + +Some charm workloads have native capabilities of exposing metrics, while others might rely on external tooling in the form of [exporters](https://prometheus.io/docs/instrumenting/exporters/). For Kubernetes charms, these exporters should be placed in a separate container in the same pod as the workload itself. + +

Logs

+ +Any logs relevant for the charm should also be forwarded to Loki over the loki_push_api relation interface. For Kubernetes charms, this is usually accomplished using the [loki_push_api](https://charmhub.io/loki-k8s/libraries/loki_push_api) library, while machine charms will want to integrate with the [Grafana Agent subordinate charm](https://charmhub.io/grafana-agent). + +

Alert rules

+ + +Based on the telemetry exposed above, charms should also include opinionated, generic, and robust, alert rules. See [the how-to article on CharmHub](https://charmhub.io/topics/canonical-observability-stack/how-to/add-alert-rules) for more information. + +

Grafana Dashboards

+ + +Just as with alert rules, charms should be shipped with good, opinionated Grafana dashboards. The goal of these dashboards should be to provide an at-a-glance image of the health and performance of the charm. See [the grafana_dashboard library](https://charmhub.io/grafana-k8s/libraries/grafana_dashboard) for more information. + +

Security considerations

+ +Use Juju secrets to share secrets between units or applications. + +Do not log secret or private information: no passwords, no private keys, and even be careful with logging “unexpected” keys in json or yaml dictionaries. Logs are likely to be pasted to public pastebin sites in the process of troubleshooting problems. + +If you need to generate a password or secret token, prefer the Python `secrets` library over the `random` library or writing your own tool. 16 bytes of random data (32 hex chars) is a reasonable minimum; longer may be useful, but 32 bytes (64 hex chars) is probably a reasonable upper limit for “tokens”. + +Enforce a ‘chain of trust’ for all executable content: Ubuntu’s apt configuration defaults to a Merkle-tree of `sha-512` hashes, rooted with GPG keys. This is preferred. Simplestreams can be used with CA-verified HTTPS transfers and `sha-512` hashes. This is acceptable but much more permissive. If pulling content from third-party sites, consider embedding `sha-384` or `sha-512` hashes into the charm, and verifying the hash before operating on the file in any way. Verifying GPG signatures would work but is more challenging to do correctly. Use `gpgv` for GPG signature verification. + +Make use of standard Unix user accounts to the extent that it is possible: programs should only run as root when absolutely necessary. Beware of race conditions: it is safer to create or modify files as the target account with the correct permissions in comparison to creating or modifying files as root and then changing ownership or permissions. + +Consider using `systemd` security features: + +* Seccomp `SystemCallArchitectures=native` and `SystemCallFilter=` +* `User=` and `Group=` are often safer than program’s built-in privilege dropping +* `CapabilityBoundingSet=` can limit the capabilities a service can use +* `AmbientCapabilities=` can provide limited capabilities without requiring root – this can be helpful for eg `CAP_NET_BIND_SERVICE`. (Many capabilities can be leveraged to full root, so it’s not perfect. Binding low ports, for when systemd socket-activation doesn’t work, is probably the best use case.) +* Systemd’s various `ProtectHome=` or `ProtectSystem=` may not play nicely with AppArmor policies. + +Consider writing AppArmor profiles for services: Juju ‘owns’ the configuration of services, so there should be minimal conflict with local configurations. + +

Example repositories

+ +There are a number of sample repositories you could use for inspiration and a demonstration of good practice. + +Kubernetes charms: + +* [zinc-k8s](https://github.com/jnsgruk/zinc-k8s-operator) +* [loki-k8s](https://github.com/canonical/loki-k8s-operator) + +Machine charms: + +* [parca](https://github.com/jnsgruk/parca-operator) +* [mongodb](https://github.com/canonical/mongodb-operator) +* [postgresql](https://github.com/canonical/postgresql-operator) + + + + +
+ +**Contributors:** @0x12b , @benhoyt , @carlcsaposs , @jameinel , @jdkandersson , @jnsgruk , @merkata, @tony-meyer , @tmihoc \ No newline at end of file diff --git a/tmp/t/charm-environment-variables.md b/tmp/t/charm-environment-variables.md new file mode 100644 index 000000000..7dd64b2ad --- /dev/null +++ b/tmp/t/charm-environment-variables.md @@ -0,0 +1,176 @@ +(charm-environment-variables)= +# Charm environment variables + +When a charm runs, it will gather all sorts of information from its execution context. These are environment variables. They are used to parametrise the charm's runtime. + +> Charms written with the operator framework benefit from the various abstraction levels on top of the environment variables, so charm authors rarely (if at all) need to work with them directly. + + + +Depending on the event type, there might be some differences in what environment variables are set. For example, in a `pebble-ready` event they would be as below, where the `JUJU_WORKLOAD_NAME` environment variable is actually unique to this event. + +```python +{ + 'APT_LISTCHANGES_FRONTEND': 'none', + 'CHARM_DIR': '/var/lib/juju/agents/unit-bare-0/charm', + 'CLOUD_API_VERSION': '1.23.0', + 'DEBIAN_FRONTEND': 'noninteractive', + 'JUJU_AGENT_SOCKET_ADDRESS': '@/var/lib/juju/agents/unit-bare-0/agent.socket', + 'JUJU_AGENT_SOCKET_NETWORK': 'unix', + 'JUJU_API_ADDRESSES': '10.152.183.4:17070 ' + 'controller-service.controller-charm-dev.svc.cluster.local:17070', + 'JUJU_AVAILABILITY_ZONE': '', + 'JUJU_CHARM_DIR': '/var/lib/juju/agents/unit-bare-0/charm', + 'JUJU_CHARM_FTP_PROXY': '', + 'JUJU_CHARM_HTTPS_PROXY': '', + 'JUJU_CHARM_HTTP_PROXY': '', + 'JUJU_CHARM_NO_PROXY': '127.0.0.1,localhost,::1', + 'JUJU_CONTEXT_ID': 'bare/0-workload-pebble-ready-1918120275707858680', + 'JUJU_DISPATCH_PATH': 'hooks/workload-pebble-ready', + 'JUJU_HOOK_NAME': 'workload-pebble-ready', + 'JUJU_MACHINE_ID': '', + 'JUJU_METER_INFO': 'not set', + 'JUJU_METER_STATUS': 'AMBER', + 'JUJU_MODEL_NAME': 'welcome', + 'JUJU_MODEL_UUID': 'cdac5656-2423-4388-8f30-41854b4cca7d', + 'JUJU_PRINCIPAL_UNIT': '', + 'JUJU_SLA': 'unsupported', + 'JUJU_UNIT_NAME': 'bare/0', + 'JUJU_VERSION': '2.9.29', + 'JUJU_WORKLOAD_NAME': 'workload', + 'KUBERNETES_PORT': 'tcp://10.152.183.1:443', + 'KUBERNETES_PORT_443_TCP': 'tcp://10.152.183.1:443', + 'KUBERNETES_PORT_443_TCP_ADDR': '10.152.183.1', + 'KUBERNETES_PORT_443_TCP_PORT': '443', + 'KUBERNETES_PORT_443_TCP_PROTO': 'tcp', + 'KUBERNETES_SERVICE_HOST': '10.152.183.1', + 'KUBERNETES_SERVICE_PORT': '443', + 'KUBERNETES_SERVICE_PORT_HTTPS': '443', + 'LANG': 'C.UTF-8', + 'OPERATOR_DISPATCH': '1', + 'PATH': '/var/lib/juju/tools/unit-bare-0:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/charm/bin', + 'PWD': '/var/lib/juju/agents/unit-bare-0/charm', + 'PYTHONPATH': 'lib:venv', + 'TERM': 'tmux-256color', +} +``` + +The environment variables for a specific event can be obtained by running `juju debug-hooks ` and waiting for the desired event to fire. If the next prompt looks as below + + root@database-0:/var/lib/juju# + +this means that we are still waiting for an event to occur. As soon as that happens, the prompt will look similar to the below + + root@database-0:/var/lib/juju/agents/unit-database-0/charm# + +and this means we're inside the charm execution context. At this point, typing `printenv` will print out the environment variables. + +## Event variables + +### Core hooks +| `JUJU_HOOK_NAME` | `JUJU_DISPATCH_PATH` | Notes | +|-----------------------------------------------------------------------------------------------------|-----------------------------------|--------------------------------------------------------| +| [`'workload-pebble-ready'`](https://discourse.charmhub.io/t/container-name-pebble-ready-event/6468) | `'hooks/workload-pebble-ready'` | This is the only event that sets `JUJU_WORKLOAD_NAME`. | +| [`'update-status'`](https://discourse.charmhub.io/t/update-status-event/6484) | `'hooks/update-status'` | | +| [`'install'`](https://discourse.charmhub.io/t/install-event/6469) | `'hooks/install'` | | +| [`'leader-elected'`](https://discourse.charmhub.io/t/leader-elected-event/6470) | `'hooks/leader-elected'` | | +| [`'leader-settings-changed'`](https://discourse.charmhub.io/t/leader-settings-changed-event/6471) | `'hooks/leader-settings-changed'` | | +| [`'start'`](https://discourse.charmhub.io/t/start-event/6482) | `'hooks/start'` | | +| [`'stop'`](https://discourse.charmhub.io/t/stop-event/6483) | `'hooks/stop'` | | +| [`'remove'`](https://discourse.charmhub.io/t/remove-event/6481) | `'hooks/remove'` | | +| [`'config-changed'`](https://discourse.charmhub.io/t/config-changed-event/6465) | `'hooks/config-changed'` | | +| [`'upgrade-charm'`](https://discourse.charmhub.io/t/upgrade-charm-event/6485) | `'hooks/upgrade-charm'` | | + + +### Relation hooks +Only relation hooks set `JUJU_RELATION`, `JUJU_RELATION_ID`, `JUJU_REMOTE_UNIT` and `JUJU_REMOTE_APP`. + +| `JUJU_HOOK_NAME` | `JUJU_DISPATCH_PATH` | Notes | +|------------------------------------------------------------------------------------------------------|------------------------------|---------------------------------------------------------| +| [`'-relation-created'`](https://discourse.charmhub.io/t/relation-name-relation-created-event/6476) | `'hooks/-relation-created'` | JUJU_REMOTE_UNIT is set but is empty | +| [`'-relation-joined'`](https://discourse.charmhub.io/t/relation-name-relation-joined-event/6478) | `'hooks/-relation-joined'` | | +| [`'-relation-changed'`](https://discourse.charmhub.io/t/relation-name-relation-changed-event/6475) | `'hooks/-relation-changed'` | | +| [`'-relation-departed'`](https://discourse.charmhub.io/t/relation-name-relation-departed-event/6477) | `'hooks/-relation-departed'` | This is the only event that sets `JUJU_DEPARTING_UNIT`. | +| [`'-relation-broken'`](https://discourse.charmhub.io/t/relation-name-relation-broken-event/6474) | `'hooks/-relation-broken'` | JUJU_REMOTE_UNIT is set but is empty | + +### Storage hooks +Only storage hooks set `JUJU_STORAGE_LOCATION`, `JUJU_STORAGE_KIND` and `JUJU_STORAGE_ID`. + +| `JUJU_HOOK_NAME` | `JUJU_DISPATCH_PATH` | Notes | +|---------------------------------------------------------------------------------------------------------|-----------------------------------|-------| +| [`'-storage-attached'`](https://discourse.charmhub.io/t/storage-name-storage-attached-event/6479) | `'hooks/-storage-attached'` | | +| [`'-storage-detaching'`](https://discourse.charmhub.io/t/storage-name-storage-detached-event/6480) | `'hooks/-storage-detaching'` | | + +### Actions +Only [actions](https://discourse.charmhub.io/t/action-name-action-event/6466) set `JUJU_ACTION_NAME`, `JUJU_ACTION_TAG` and `JUJU_ACTION_UUID`. + +- `JUJU_HOOK_NAME` set, but is empty. +- `JUJU_DISPATCH_PATH` looks like this: `actions/do-something` +- The special `juju-run` action can be used to print out environment variables for its own context: `juju run --unit my-app/0 -- env` + +### Secrets +All [secrets hooks](https://discourse.charmhub.io/t/secret-events/7191) set `JUJU_SECRET_ID` and `JUJU_SECRET_LABEL`. Only `secret-expired`, `secret-rotate` and `secret-remove` also set `JUJU_SECRET_REVISION`. + +```{note} + +Do note that the `JUJU_SECRET_LABEL` will be set to some non-empty value only if the secret at hand *has* a label at the time the event is emitted by the agent. + +``` + +| `JUJU_HOOK_NAME` | Notes | +|---------------------------------------------------------------------------------------------------------|-------| +| [`'secret-changed'`](https://discourse.charmhub.io/t/7193) | This is the only secrets hook that does not set `JUJU_SECRET_REVISION` | +| [`'secret-expired'`](https://discourse.charmhub.io/t/7192) | | +| [`'secret-remove'`](https://discourse.charmhub.io/t/7195) | | +| [`'secret-rotate'`](https://discourse.charmhub.io/t/7194) | | + +### List of charm environment variables + +> [Source (the `HookVars` function)](https://github.com/juju/juju/blob/c3de749971d5abcdcb01ec29f290a45f2fb2493d/worker/uniter/runner/context/context.go) + +- CHARM_DIR +- CLOUD_API_VERSION +- JUJU_ACTION_NAME +- JUJU_ACTION_TAG +- JUJU_ACTION_UUID +- JUJU_AGENT_CA_CERT +- JUJU_AGENT_SOCKET_ADDRESS +- JUJU_AGENT_SOCKET_NETWORK +- JUJU_API_ADDRESSES +- JUJU_AVAILABILITY_ZONE +- JUJU_CHARM_DIR +- JUJU_CHARM_FTP_PROXY +- JUJU_CHARM_HTTPS_PROXY +- JUJU_CHARM_HTTP_PROXY +- JUJU_CHARM_NO_PROXY +- JUJU_CONTEXT_ID +- JUJU_DEPARTING_UNIT +- JUJU_HOOK_NAME +- JUJU_MACHINE_ID +- JUJU_METER_INFO +- JUJU_METER_STATUS +- JUJU_MODEL_NAME +- JUJU_MODEL_UUID +- JUJU_NOTICE_ID +- JUJU_NOTICE_KEY +- JUJU_NOTICE_TYPE +- JUJU_PRINCIPAL_UNIT +- JUJU_RELATION +- JUJU_RELATION_ID +- JUJU_REMOTE_APP +- JUJU_REMOTE_UNIT +- JUJU_SECRET_ID +- JUJU_SECRET_LABEL +- JUJU_SECRET_REVISION +- JUJU_SLA +- JUJU_STORAGE_ID +- JUJU_STORAGE_KIND +- JUJU_STORAGE_LOCATION +- JUJU_TARGET_BASE +- JUJU_TARGET_SERIES (deprecated in Juju 3, to be removed in Juju 4; please use JUJU_TARGET_BASE instead) +- JUJU_UNIT_NAME +- JUJU_VERSION + +
+ +**Contributors:** @jack-shaw, @nvinuesa, @ppasotti, @sed-i , @tmihoc \ No newline at end of file diff --git a/tmp/t/charm-lifecycle.md b/tmp/t/charm-lifecycle.md new file mode 100644 index 000000000..499711d53 --- /dev/null +++ b/tmp/t/charm-lifecycle.md @@ -0,0 +1,161 @@ +(charm-lifecycle)= +# Charm lifecycle + +> See also: {ref}`Exploring event emission sequences with jhack tail ` + + +This document is about the lifecycle of a charm, specifically the Juju events that are used to keep track of it. These events are relayed to charm code by the Operator Framework in specific sequences depending on what's going on in the Juju model. + +It is common wisdom that event ordering should not be generally relied upon when coding a charm, to ensure resilience. It can be however useful to understand the logic behind the timing of events, so as to avoid common mistakes and have a better picture of what is happening in your charm. In this document we'll learn how: + +* A charm's lifecycle can be seen to consist of three **phases**, each one with characteristic events and sequences thereof. The fuzziest of the three being the Operation phase, where pretty much anything can happen short of setup events. +* Not all events can be reliably be assumed to occur in specific temporal orders, but some can. + +In this document we will *not* learn: + +* What each event means or is typically used to represent about a workload status. For that see [the SDK docs](https://juju.is/docs/sdk/events). +* What event cascades are triggered by a human administrator running commands through the Juju CLI. For that see [this other doc](https://discourse.charmhub.io/t/core-lifecycle-events/4455/3). + +```{note} + +The graphs are screenshots of mermaid sources currently available [here](https://github.com/PietroPasotti/charm-events), pending mermaid support to be available on discourse. + +``` + +**Contents:** +- [The graph](#heading--the-graph) + - [Legend](#heading--legend) +- [Other events](#heading--other-events) +- [Notes on the setup phase](#heading--notes-on-the-setup-phase) +- [Notes on the operation phase](#heading--notes-on-the-operation-phase) +- [Notes on the teardown phase](#heading--notes-on-the-teardown-phase) +- [Caveats](#heading--caveats) +- [Deprecation notices](#heading--deprecation-notices) +- [Event semantics and data](#heading--event-semantics-and-data) +- [Appendices](#heading--appendices) + - [Appendix 1: scenario example](#heading--appendix-1-scenario-example) + - [ Appendix 2: deferring an event](#heading---appendix-2-deferring-an-event) + + +

The graph

+ +![image|690x713](upload://aFpDVI0qN94mmLTY4nSdsrMPuKH.png) + +

Legend

+ +* `(start)` and `(end)` are 'meta' nodes and represent the beginning and end of the lifecycle of a Charm/juju unit. All other nodes represent hooks (events) that can occur during said lifecycle. +* Hard arrows represent strict temporal ordering which is enforced by the Juju state machine and respected by the Operator Framework, which mediates between the Juju controller and the Charm code. +* Dotted arrows represent a 1:1 relationship between relation events, explained in more detail down in the Operation section. +* The large yellow boxes represent broad phases in the lifecycle. You can read the graph as follows: when you fire up a unit, there is first a setup phase, when that is done the unit enters a operation phase, and when the unit goes there will be a sequence of teardown events. Generally speaking, this guarantees some sort of ordering of the events: events that are unique to the teardown phase can be guaranteed not to be fired during the setup phase. So a {ref}``stop`] will never be fired before a [`start`]. +* The colours of the event nodes represent a logical but practically meaningless grouping of the events. + * green for leadership events + * red for storage events + * purple for relation events + * blue for generic lifecycle events + +

Workload and substrate-specific events

+ +Note the `[workload events] (k8s only)` node in the operation phase. That represents all events meant to communicate information about the workload container on kubernetes charms. At the time of writing the only such events are: + +* [`*-pebble-ready` ` +* {ref}``*-pebble-custom-notice` ` +* [`*-pebble-check-failed`](https://ops.readthedocs.io/en/latest/#ops.PebbleCheckFailedEvent) +* [`*-pebble-check-recovered`](https://ops.readthedocs.io/en/latest/#ops.PebbleCheckRecoveredEvent) + +All of these can fire at any time whatsoever during the lifecycle of a charm. + +Similarly, the `{ref}`pre/post]-series-upgrade (lxd only)` events can only occur on machine charms at any time during the operation phase. + +

Notes on the setup phase

+* The only events that are guaranteed to always occur during Setup are [`start`], [`config-changed`] and [`install`]. The other events only happen if the charm happens to have (peer) relations at install time (e.g. if a charm that already is related to another gets scaled up) or it has storage. Same goes for leadership events. For that reason they are styled with dashed borders. +* [`config-changed`] occurs between [`install`] and [`start`] regardless of whether any leadership (or relation) event fires. +* Any [`*-relation-created`] event can occur at Setup time, but if X is a peer relation, then `X-relation-created` can **only** occur at Setup, while for non-peer relations, they can occur also during Operation. The reason for this is that a peer relation cannot be created or destroyed 'manually' at arbitrary times, they either exist or not, and if they do exist, then we know it from the start. + +

Notes on the operation phase

+ +* [`update-status` ` is fired automatically and periodically, at a configurable regular interval (default is 5m) which can be configured by `juju model-config update-status-hook-interval`. +* {ref}``collect-metrics` ` is fired automatically and periodically in older juju versions, at a regular interval of 5m, AND whenever the user runs `juju collect-metrics`. +* [`leader-elected`] and [`leader-settings-changed`] only fire on the leader unit and the non-leader unit(s) respectively, just like at startup. +* There is a square of symmetries between the `*-relation-[joined/departed/created/broken]` events: + * Temporal ordering: a `X-relation-joined` cannot *follow* a `X-relation-departed` for the same relation ID. Same goes for [`*-relation-created`] and [`*-relation-broken`], as well as [`*-relation-created`] and [`*-relation-changed`]. + * Ownership: `joined/departed` are unit-level events: they fire when an application has a (peer) relation and a new unit joins or leaves. All units (including the newly created or leaving unit), will receive the event. `created/broken` are relation-level events, in that they fire when two applications become related or a relation is removed (e.g. via `juju remove-relation` or because an application is destroyed). + * Number: there is a 1:1 relationship between `joined/departed` and `created/broken`: when a unit joins a relation with X other units, X [`*-relation-joined`] events will be fired. When a unit leaves, all units will receive a [`*-relation-departed`] event (so X of them are fired). Same goes for `created/broken` when two applications are related or a relationship is broken. Find in appendix 1 a somewhat more elaborate example. +* Technically speaking all events in this box are optional, but I did not style them with dashed borders to avoid clutter. If the charm shuts down immediately after start, it could happen that no operation event is fired. +* A `X-relation-joined` event is always followed up (immediately after) by a `X-relation-changed` event. But any number of [`*-relation-changed`] events can be fired at any time during operation, and they need not be preceded by a [`*-relation-joined`] event. +* There are more temporal orderings than the one displayed here; event chains can be initiated by human operation as detailed [in the SDK docs](https://juju.is/docs/sdk/events) and [the leadership docs](https://juju.is/docs/sdk/leadership). For example, it is guaranteed that a [`leader-elected`] is always followed by a [`settings-changed`], and that if you remove the leader unit, you should get [`*-relation-departed`] and a [`leader-settings-changed`] on the remaining units (although no specific ordering can be guaranteed [cfr this bug...](https://bugs.launchpad.net/juju/+bug/1964582)). +* Secret events (in purple) can technically occur at any time, provided your charm either has created a secret, or observes a secret that some other charm has created. Only the owner of a secret can receive `secret-rotate` and `secret-expire` for that secret, and only an observer of a secret can receive `secret-changed` and `secret-removed`. + +

Notes on the teardown phase

+ +* Both relation and storage events are guaranteed to fire before [`stop`]/[`remove`] if they will fire at all. They are optional, in that a departing unit (or application) might have no storage or relations. +* [`*-relation-broken`] events in the Teardown phase are fired in case an application is being torn down. These events can also occur at Operation time, if the relation is removed by e.g. a charm or a controller. +* The entire teardown phase is **skipped** if the cloud is killed. The next event the charm will see in this case would be a `start` event. This would happen, for example, on `microk8s stop; microk8s start`. + +

Caveats

+ +* Events can be deferred by charm code by calling `Event.defer()`. That means that the event is put in a queue of deferred events which will get flushed by the operator framework as soon as the next event comes in, and *before* firing that new event in turn. See Appendix 2 for a visual representation. What this means in practice is that deferring an event can break the temporal ordering of the events as outlined in this graph; `defer()`ring an event twice will break the ordering guarantees we outlined here. Cf. the appendix for an UML-y representation. Cfr [this document on defer](https://discourse.charmhub.io/t/deferring-events-details-and-dilemmas/5930) for more. +* The events in the Operation phase can interleave in arbitrary ways. For this reason it's essential that hook handlers make *no assumptions* about each other -- each handler should check its preconditions independently and operate under the assumption that the relative ordering is totally arbitrary -- except relation events, which have some partial ordering as explained above. + +

Deprecation notices

+ +* `leader-deposed` is a juju hook that was planned but never actually implemented. You may see a WARNING mentioning it in the `juju debug-log` but you can ignore it. +* [`collect-metrics`](https://discourse.charmhub.io/t/charm-hooks/1040#heading--collect-metrics) is no longer being fired in recent juju versions. + +

Event semantics and data

+ +This document is only about the timing of the events; for the 'meaning' of the events, other sources are more appropriate; e.g. [juju-events](https://juju.is/docs/sdk/events). +For the data attached to an event, one should refer to the docstrings in the ops.charm.HookEvent subclass that the event you're expecting in your handler inherits from. + + +

Appendices

+ +

Appendix 1: scenario example

+ + +This is a representation of the relation events a deployment will receive in a simple scenario that goes as follows: +* We start with two unrelated applications, `applicationA` and `applicationB`, with one unit each. +* `applicationA` and `applicationB` become related via a relation called `R`. +* `applicationA` is scaled up to 2 units. +* `applicationA` is scaled down to 1 unit. +* `applicationA` touches the `R` databag (e.g. during an `update-status` hook, or as a result of a `config-changed`, an action, a custom event...). +* The relation `R` is removed. + +Note that many event sequences are marked as 'par' for *parallel*, which means that the events can be dispatched to the units arbitrarily interleaved. + +![image|690x578](upload://22fYgoRypCRsfvRNCWcGS6xLFvr.png) +![image|690x554](upload://72j3paIuuzJcj7yAW2z96W5ygR9.png) + + +

Appendix 2: deferring an event

+ > {ref}`jhack tail ` offers functionality to visualize the deferral status of events in real time. + +This is the 'normal' way of using `defer()`: an event `event1` comes in but we are not ready to process it; we `defer()` it; when `event2` comes in, the operator framework will first flush the queue and fire `event1`, then fire `event2`. The ordering is preserved: `event1` is consumed before `event2` by the charm. + +![image|618x623](upload://iAF9tW9cwlMucHF2INYmhGlyhGe.png) + +Suppose now that the charm defers `event1` again; then `event2` will be processed by the charm before `event1` is. `event1` will only be fired again once another event, `event3`, comes in in turn. +The result is that the events are consumed in the order: `2-1-3`. Beware. + +![image|568x804](upload://56Di1wIWMM0Z7gGr7a8RNjnpiJs.png) + + +[`install`]: https://juju.is/docs/sdk/events#heading--install +[`start`]: https://juju.is/docs/sdk/events#heading--start +[`stop`]: https://juju.is/docs/sdk/events#heading--stop +[`remove`]: https://juju.is/docs/sdk/events#heading--remove +[`*-pebble-ready`]: https://discourse.charmhub.io/t/event-container-pebble-ready/6468 +[`config-changed`]: https://juju.is/docs/sdk/events#heading--config-changed +[`update-status`]: https://juju.is/docs/sdk/events#heading--update-status +[`collect-metrics`]: https://discourse.charmhub.io/t/charm-hooks/1040#heading--collect-metrics +[`leader-settings-changed`]: https://discourse.charmhub.io/t/charm-hooks/1040 +[`upgrade-charm`]: https://juju.is/docs/sdk/events#heading--upgrade-charm +[`*-relation-created`]: https://juju.is/docs/sdk/relations#heading--relation-events +[`*-relation-joined`]: https://juju.is/docs/sdk/relations#heading--relation-events +[`*-relation-changed`]: https://juju.is/docs/sdk/relations#heading--relation-events +[`*-relation-broken`]: https://juju.is/docs/sdk/relations#heading--relation-events + +[`leader-elected`]: https://discourse.charmhub.io/t/leader-elected/5778 +[`*-relation-departed`]: https://discourse.charmhub.io/t/relation-departed/5943 + + +> Contributors: @ppasotti \ No newline at end of file diff --git a/tmp/t/charm-maturity.md b/tmp/t/charm-maturity.md new file mode 100644 index 000000000..ae039cd91 --- /dev/null +++ b/tmp/t/charm-maturity.md @@ -0,0 +1,26 @@ +(charm-maturity)= +# Charm maturity + +Charms are built for reuse. Having reuse in mind, the design and implementation of a charm needs to be independent from particular use cases or domains. But how can you ensure reuse? + +The best way to enable reuse is to start an open source project. Open source brings experts together; they can participate in the development and contribute their knowledge from their different backgrounds. In addition, the open source approach offers transparency and lets users and developers freely use, modify, analyse and redistribute the software. Read more about [reasons to publish your charm in our documentation](https://juju.is/docs/sdk/reasons-to-publish-your-charm-on-charmhub). + +- [Two stages of maturity](#heading--two-stages-of-maturity) +- [Requirements for public listing](#heading--requirements-for-public-listing) + +

Two stages of maturity

+ + +An open source project is the suitable foundation for reuse. However, providing a reusable charm is also a matter of maturity: high-quality software development and relevant capabilities for operating applications. Accordingly, the development of a charm follows a two-stage approach: + +1. {ref}`Stage 1: Important qualities `: A quality open source project, implements state-the-art documentation, testing and automation - this is the foundation for sharing and effective collaboration. + +2. {ref}`Stage 2: Important capabilities ` are about implementing the most relevant capabilities to ensure effective operations. + +For both stages, the reference documents above provide two parts: a first part explains the goals and a second part lists references to the documentation or example code for implementation. The points listed in the stages prioritise the development and explain how to implement the qualities and capabilities using the [Charm SDK](https://juju.is/docs/sdk). For general best practices for developing a charm, please read the [Charm development best practices](https://juju.is/docs/sdk/styleguide). + +

Requirements for public listing

+ +Everyone can publish charms to [https://charmhub.io/](https://charmhub.io/). Then, the charm can be accessed for deployments using Juju or via a web browser by its URL. If a charm is published in Charmhub.io and included in search results, the charm entry needs to be switched into the listed mode. To bring your charm into the listing, [reach out to the community](https://discourse.charmhub.io/c/charmhub-requests/46) to announce your charm and ask for a review by an experienced community member. + +The [Stage 1- Important qualities](https://juju.is/docs/sdk/charm-publication-checklist) reference is the requirement for switching a charm into the *listed* mode. The points listed in the first stage ensure a useful charm project is suitable for presentation on [https://charmhub.io/](https://charmhub.io/) and for testing by others. \ No newline at end of file diff --git a/tmp/t/charm-naming-guidelines.md b/tmp/t/charm-naming-guidelines.md new file mode 100644 index 000000000..dc0f405e7 --- /dev/null +++ b/tmp/t/charm-naming-guidelines.md @@ -0,0 +1,80 @@ +(charm-naming-guidelines)= +# Charm naming guidelines + +> See also: {ref}`Charm publication checklist ` + +When you create your charm, an important decision is how to name it. Below are some recommendations. + + + +## Naming charms + +Of course, there is a large number of existing charms already with all their variants on naming. Please check, if naming of existing Charms can be improved, it will help others to find your Charms. But more importantly, consider the following points for new charming efforts: + +| Required / Recommended | Background | Examples | +| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| Slug-oriented naming required: ASCII lowercase letters, numbers, and hyphens. | Ensure URL-safeness,
finding charms easier. | :stop_sign: `node_red`
:white_check_mark: `node-red` | +| Do not abbreviate application names. | Finding charms easier. | :stop_sign: `matterm`
:white_check_mark: `mattermost` | +| Do not consider camelcase naming. | Think of slugs, also not supported. |:stop_sign: `NodeRed`
:white_check_mark: `node-red` | +| No charm or operator suffix | It is about Charms
**Exception**: Bundles need to be distinguished from the Charm. | :stop_sign: `ghost-operator`
:white_check_mark: `ghost`
:stop_sign: `grafana-charm`
:white_check_mark: `grafana`
*However:*
:white_check_mark: `discourse`
:white_check_mark: `discourse-bundle` | +| No organisation as prefix | Hard to have it consistently across all charms,
Belongs to `charmcraft.yaml` | :stop_sign: apache-kafka
:white_check_mark: `kafka`
:stop_sign: `oracle-mysql`
:white_check_mark: `mysql` | +| No publisher name as prefix to the charm name. | Hard to have it consistently across all charms,
Belongs to `charmcraft.yaml` | :stop_sign: `mysql-charmers-mysql-server`
:white_check_mark: `mysql`
:stop_sign: `charmteam-kafka`
:white_check_mark: `kafka` | +| Avoid the purpose, use the name of the application instead. | Multiple charms for same purpose exist or will come. | :stop_sign: `general-cache`
:white_check_mark: `varnish` *(if the varnish OSS project represents the application)* | +| Do not use non-production identifiers, for public. | Not useful for the community of Charmhub users. | :stop_sign: `jenkins-test`
:stop_sign: `mysql-draft`
:stop_sign: `hadoop-internal`
:stop_sign: `kafka-demo` | +| Single charms: Avoid mix of app name and purpose. | Suffixes or prefixes shall be omitted and can be part of summary. | :stop_sign: `drupal-server`
:white_check_mark: `drupal`
*However:*
:white_check_mark: `kubeflow-dashboard`
:white_check_mark: `kubeflow-pipeline`
:white_check_mark: `kubeflow-volumes` | +| Multiple charms: Put the name before the function. | Sorting. | :stop_sign: `pipeline-kubeflow`
:white_check_mark: `kubeflow-pipeline` | +| Explain short names if used. | Three letter acronyms: prone to be confused. | | +| Exception: Use “-k8s” for kubernetes only charms | In general, do not use a suffix for platforms. Only a) for “kubernetes only” charms and b) another non-k8s-charm is around, use the suffix "-k8s” to distinguish the k8s-charm from the other. For a charm covering a workload that makes only sense for k8s and nowhere else, avoid the suffix. | :white_check_mark: `graylog-k8s` | + +## Maintainer contact + +Currently, the `charmcraft.yaml` supports the {ref}`maintainer of the charm <5364md>` being listed : + +`A list of maintainers in the format "First Last ` + +The naming of the maintainer has three main goals: +* Identify the right person to contact +* Visibility (for merits) of the publisher(s) or author(s) of the Charm +* Transparent responsibility for the published software. However, please note that the maintainer list does not necessarily denote the responsible party (copyright holder) of the published Charm implementation. + +The maintainer field in `charmcraft.yaml` must be filled out, either with + +1. ```{tip} +A maintaining individual contact +``` +2. ```{tip} +Multiple maintaining individual contacts if that applies *(preferred)*, +``` +3. ```{tip} +The maintaining organisation if that applies. +``` +4. ```{note} +Do not use fantasy / virtual team names as feedback has shown it causes confusion. +``` + +## Naming repositories + +Charms shall have a publicly accessible (git) repository. Opposed to the charm listing on Charmhub.io, a distinguishing element in the name of a repository helps to identify the right repository with the Charm implementation. + +The distinguishing element is the suffix “-operator”, because that aligns also with the names of the other parts in the Juju world, such as the [Charmed Operator Framework](https://juju.is/about) or the [Charmed Operator SDK](https://juju.is/docs/sdk). So, in summary, we have the following steps: + +1. ```{tip} +Name the repository like your charm (see naming rules above) +``` +2. ```{tip} +Add a suffix to it: -operator (because: Charmed Operator SDK) +``` +3. ```{tip} +Prefer suffix over prefix (:stop_sign: `operator-mydatabaseserver`) +``` +4. ```{caution} +Plural form only if there are in fact multiple charm implementations in the repository, do not mimic an (adverbial) genitive. +``` + +It is understood that many repositories already exist with the suffix `-charm` along to the repositories with `-operator`. Repository renaming can be hard in some cases. The naming guide defines the preferred naming. + +Of course, some organisations have their own naming conventions for their repositories which cannot be easily overruled. \ No newline at end of file diff --git a/tmp/t/charm-publication.md b/tmp/t/charm-publication.md new file mode 100644 index 000000000..f635bf7a6 --- /dev/null +++ b/tmp/t/charm-publication.md @@ -0,0 +1,19 @@ +(charm-publication)= +# Charm publication + +> See also: {ref}`How to publish your charm to Charmhub ` + +In the context of software, **publication** refers to making the software widely available in some form or other. In the context of a charm, this can refer, for example, to making the charm project open source on social coding platforms (Bitbucket, Github, Launchpad, Sourceforge, etc.) or publishing it to the official charm store -- {ref}`Charmhub `. + +> See more: {ref}`Reasons to publish your charm on Charmhub ` + + + \ No newline at end of file diff --git a/tmp/t/charm-relation-interfaces.md b/tmp/t/charm-relation-interfaces.md new file mode 100644 index 000000000..9e52329a2 --- /dev/null +++ b/tmp/t/charm-relation-interfaces.md @@ -0,0 +1,47 @@ +(charm-relation-interfaces)= +# Charm-relation-interfaces + +> Also see: +> - {ref}`Interface tests ` +> - {ref}`How to register an interface ` +> - {ref}`How to write interface tests ` + +[`charm-relation-interfaces`](https://github.com/canonical/charm-relation-interfaces) is a repository containing specifications, databag schemas and interface tests for Juju relation interfaces. In other words, it is the source of truth for data and behavior of providers and requirers of integrations. + +The purpose of this project is to provide uniformity in the landscape of all possible integrations and promote charm interoperability. + +Juju interfaces are untyped, which means that for juju to think two charms can be integrated all it looks at is whether the interface names of the two endpoints you're trying to connect are the same string. But it might be that the two charms have different, incompatible implementations of two different integrations that happen to have the same name. + +In order to prevent two separate charms from rolling their own integration with the same name, and prevent a sprawl of many subtly different interfaces with similar semantics and similar purposes, we introduced `charm-relation-interfaces`. + +## Using `charm-relation-interfaces` + +If you have a charm that provides a service, you should search `charm-relation-interfaces` (or directly charmhub in the future) and see if it exists already, or perhaps a similar one exists that lacks the semantics you need and can be extended to support it. + +Conversely, if the charm you are developing needs some service (a database, an ingress url, an authentication endpoint...) you should search `charm-relation-interfaces` to see if there is an interface you can use, and to find existing charms that provide it. + +There are three actors in play: + +* **the owner of the specification** of the interface, which also owns the tests that can be used to verify "does charm X 'really' support this interface?". This is the `charm-relation-interfaces` repo. +* **the owner of the implementation** of an interface. In practice, this often is the charm that owns the charm library with the reference implementation for an interface. +* **the interface user**: a charm that wants to use the interface (either as requirer or as provider). + +The interface user needs the implementation (typically, the provider also happens to be the owner and so it already has the implementation). This is addressed by `charmcraft fetch-lib`. + +The owner of the implementation needs the specification, to help check that the implementation is in fact compliant. + +## Repository structure + +For each interface, the charm-relation-interfaces repository hosts: +- the **specification**: a semi-formal definition of what the semantics of the interface is, and what its implementations are expected to do in terms of both the provider and the requirer +- a list of **reference charms**: these are the charms that implement this interface, typically, the owner of the charm library providing the original implementation. +- the **schema**: pydantic models unambiguously defining the accepted unit and application databag contents for provider and requirer. +- the **interface tests**: python tests that can be run to verify that a charm complies with the interface specification. + + +## Charm relation interfaces in Charmhub +In the future, Charmhub will have a searchable collection of integration interfaces. +Charmhub will, for all charms using the interface, verify that they implement it correctly (regardless of whether they use the 'official' implementation or they roll their own) in order to give the charm a happy checkmark on `charmhub.io`. In order to do that it will need to fetch the specification (from `charm-relation-interfaces`) *and* the charm repo, because we can't know what implementation they are using: we need the source code. + + +> Contributors: @ppasotti \ No newline at end of file diff --git a/tmp/t/charm-sdk-documentation.md b/tmp/t/charm-sdk-documentation.md new file mode 100644 index 000000000..10e9a6c1c --- /dev/null +++ b/tmp/t/charm-sdk-documentation.md @@ -0,0 +1,470 @@ +(charm-sdk-documentation)= +# Charm SDK Documentation + +```{important} + +The Charm SDK documentation presupposes familiarity with [Juju](https://juju.is/docs/juju). + +``` + +The Charm SDK is a toolkit for building charms. + +The SDK provides a Python library for developing and testing charms, `ops`, and a CLI tool for building, packaging and publishing charms, `charmcraft`. + +A charm can be developed in a variety of ways. However, the SDK provides useful abstractions and CLI commands so you can develop and share your charm better and faster. + +Whether you are a charm developer or a charm end user, with the Charm SDK you get a smoother experience. + +> For a collection of existing charms, see [Charmhub](https://charmhub.io/). To deploy and manage an existing or new charm, see + [Juju docs](https://juju.is/docs/olm). + +--- + +## In this documentation + +| | | +|------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| {ref}`Tutorials `
Get started - a hands-on introduction to the Charm SDK for new users
| {ref}`How-to guides `
Step-by-step guides covering key operations and common tasks | +| {ref}`Explanation `
Concepts - discussion and clarification of key topics | {ref}`Reference `
Technical information - specifications, APIs, architecture | + + + +--- + +## Project and community + +The Juju SDK is an open source project that warmly welcomes community projects, contributions, suggestions, fixes and constructive feedback. + +- Learn about the {ref}`Roadmap & Releases ` +- Read our [Code of Conduct](https://ubuntu.com/community/code-of-conduct) +- Join our [Matrix chat](https://matrix.to/#/#charmhub-juju:ubuntu.com) +- Join the [Discourse forum](https://discourse.charmhub.io/t/welcome-to-the-charmed-operator-community/8) to talk about [Juju](https://discourse.charmhub.io/tags/c/juju/6/community-workshop), [charms](https://discourse.charmhub.io/c/charm/41), [docs](https://discourse.charmhub.io/c/doc/22), or [to meet the community](https://discourse.charmhub.io/tag/community-workshop) +- Report a bug on [GitHub](https://github.com/canonical/operator/issues) (for code) or [GitHub](https://github.com/juju/docs/issues) (for docs) +- Contribute to the documentation on [Discourse](https://discourse.charmhub.io/t/documentation-guidelines-for-contributors/1245) +- Contribute to the code on [Github](https://github.com/canonical/operator) +- Visit the [Juju careers page](https://juju.is/careers) + + + + +

Navigation

+ +```{dropdown} Navigation + +| Level | Path | Navlink | +|-------|---------------------------------------------------------|-------------------------------------------------------------------------------------| +| 1 | | {ref}`SDK documentation ` | +| 1 | tutorials | {ref}`Tutorials ` | +| 2 | write-your-first-machine-charm | {ref}`Write your first machine charm ` | +| 2 | from-zero-to-hero-write-your-first-kubernetes-charm | {ref}`Write your first Kubernetes charm ` | +| 3 | study-your-application | {ref}`Study your application ` | +| 3 | set-up-your-development-environment | {ref}`Set up your development environment ` | +| 3 | create-a-minimal-kubernetes-charm | {ref}`Create a minimal Kubernetes charm ` | +| 3 | make-your-charm-configurable | {ref}`Make your charm configurable ` | +| 3 | expose-the-version-of-the-application-behind-your-charm | {ref}`Expose the version of the application behind your charm ` | +| 3 | integrate-your-charm-with-postgresql | {ref}`Integrate your charm with PostgreSQL ` | +| 3 | preserve-your-charms-data | {ref}`Preserve your charm’s data ` | +| 3 | expose-your-charms-operational-tasks-via-actions | {ref}`Expose your charm’s operational tasks via actions ` | +| 3 | observe-your-charm-with-cos-lite | {ref}`Observe your charm with COS Lite ` | +| 3 | write-unit-tests-for-your-charm | {ref}`Write unit tests for your charm ` | +| 3 | write-scenario-tests-for-your-charm | {ref}`Write scenario tests for your charm ` | +| 3 | write-integration-tests-for-your-charm | {ref}`Write integration tests for your charm ` | +| 3 | open-a-kubernetes-port-in-your-charm | {ref}`Open a Kubernetes port in your charm ` | +| 3 | publish-your-charm-on-charmhub | {ref}`Publish your charm on Charmhub ` | +| 2 | write-your-first-kubernetes-charm-for-a-flask-app | {ref}`Write your first Kubernetes charm for a Flask app ` | +| 2 | write-your-first-kubernetes-charm-for-a-django-app | {ref}`Write your first Kubernetes charm for a Django app ` | +| 2 | write-your-first-kubernetes-charm-for-a-fastapi-app | {ref}`Write your first Kubernetes charm for a FastAPI app ` | +| 2 | write-your-first-kubernetes-charm-for-a-go-app | {ref}`Write your first Kubernetes charm for a Go app ` | +| 1 | how-to | {ref}`How-to guides ` | +| 2 | build-a-12-factor-app-charm | {ref}`Build a 12-Factor app charm ` | +| 2 | build-and-own-a-charm-or-a-bundle | {ref}`Build and own a charm or a bundle ` | +| 2 | manage-bundles | {ref}`Manage bundles ` | +| 2 | | Set things up | +| 3 | dev-setup | {ref}`Set up your development environment ` | +| 3 | set-up-a-charm-project | {ref}`Set up a charm project ` | +| 3 | manage-extensions | {ref}`Manage extensions ` | +| 3 | manage-charmcraft | {ref}`Manage Charmcraft ` | +| 4 | install-charmcraft | {ref}`Install Charmcraft ` | +| 4 | charmcraft-config | {ref}`Configure Charmcraft ` | +| 4 | remote-env-auth | {ref}`Authenticate Charmcraft in remote environments ` | +| 4 | change-step-behavior-in-a-charm | {ref}`Change step behavior when creating a charm ` | +| 3 | include-extra-files-in-a-charm | {ref}`Include extra files in a charm ` | +| 2 | | Develop | +| 3 | logging | {ref}`Configure logging in a charm ` | +| 3 | resources | {ref}`Use charm resources ` | +| 3 | workloads | {ref}`Run workloads with a charm - machines ` | +| 3 | interact-with-pebble | {ref}`Run workloads with a charm - Kubernetes ` | +| 3 | set-the-charm-version | {ref}`Set the charm version ` | +| 3 | set-the-workload-version | {ref}`Set the workload version ` | +| 3 | actions | {ref}`Add an action to a charm ` | +| 3 | config | {ref}`Add a config option to a charm ` | +| 3 | use-storage-in-a-charm | {ref}`Use storage in a charm ` | +| 3 | add-a-secret-to-a-charm | {ref}`Use secrets in a charm ` | +| 3 | manage-libraries | {ref}`Use charm libraries ` | +| 4 | find-and-use-a-charm-library | {ref}`Find and use a charm library ` | +| 4 | create-and-publish-a-charm-library | {ref}`Create and publish a charm library ` | +| 4 | write-a-scenario-test-for-a-charm-library | {ref}`Write a scenario test for a charm library ` | +| 4 | document-your-charm-library | {ref}`Document a charm library ` | +| 3 | handle-leadership | {ref}`Handle leadership ` | +| 3 | implement-integrations-in-a-charm | {ref}`Add an integration to a charm ` | +| 3 | instrument-your-charm-with-tracing-telemetry | {ref}`Observe a charm ` | +| 3 | | Manage interfaces | +| 4 | register-an-interface | {ref}`Register an interface ` | +| 4 | write-interface-tests | {ref}`Write interface tests ` | +| 2 | | Test and debug | +| 3 | get-started-with-charm-testing | {ref}`Get started with charm testing ` | +| 3 | write-a-unit-test-for-a-charm | {ref}`Write a unit test for a charm ` | +| 3 | write-a-functional-test-for-a-charm-with-scenario | {ref}`Write a scenario test for a charm ` | +| 3 | write-integration-tests-for-a-charm | {ref}`Write integration tests for a charm ` | +| 3 | pack-a-charm | {ref}`Pack a charm ` | +| 3 | deploy-a-charm | {ref}`Deploy a charm ` | +| 3 | debug-a-charm | {ref}`Debug a charm ` | +| 3 | get-logs-from-a-kubernetes-charm | {ref}`Get logs from a Kubernetes charm ` | +| 2 | | Document | +| 3 | add-docs-to-your-charmhub-page | {ref}`Add docs to a charm on Charmhub ` | +| 3 | charm-documentation | {ref}`Document a charm: The README file ` | +| 2 | | Market | +| 3 | publishing | {ref}`Publish a charm ` | +| 3 | create-a-track-for-your-charm | {ref}`Create a track for your charm ` | +| 3 | create-an-icon-for-your-charm | {ref}`Create an icon for a charm ` | +| 2 | | Miscellaneous | +| 3 | | Align an old charm with charmcraft and ops | +| 4 | turn-a-hooks-based-charm-into-an-ops-charm | {ref}`Turn a hooks-based charm into an ops charm ` | +| 4 | pack-a-reactive-based-charm-with-charmcraft | {ref}`Pack a reactive-based charm with Charmcraft ` | +| 4 | pack-a-hooks-based-charm-with-charmcraft | {ref}`Pack a hooks-based charm with Charmcraft ` | +| 1 | reference | {ref}`Reference ` | +| 2 | bundle | {ref}`Bundle ` | +| 3 | bundle.yaml | {ref}`File `.yaml` ` | +| 2 | charm | {ref}`Charm ` | +| 3 | list-of-files-in-the-charm-project | {ref}`List of files in the charm project ` | +| 4 | contributing-md | {ref}`File 'CONTRIBUTING.md' ` | +| 4 | license | {ref}`File 'LICENSE' ` | +| 4 | readme-md | {ref}`File 'README.md' ` | +| 4 | actions-yaml | {ref}`File 'actions.yaml' ` | +| 4 | charmcraft-yaml | {ref}`File 'charmcraft.yaml' ` | +| 4 | config-yaml | {ref}`config.yaml ` | +| 4 | dispatch | {ref}`File 'dispatch' ` | +| 4 | icon-svg | {ref}`File 'icon.svg' ` | +| 4 | lxd-profile-yaml | {ref}`File 'lxd-profile.yaml' ` | +| 4 | manifest-yaml | {ref}`File 'manifest.yaml' ` | +| 4 | metadata-yaml | {ref}`File 'metadata.yaml' ` | +| 4 | pyproject-toml | {ref}`File 'pyproject.toml' ` | +| 4 | requirements-dev-txt | {ref}`File 'requirements-dev.txt' ` | +| 4 | requirements-txt | {ref}`File 'requirements.txt' ` | +| 4 | src-charm-py | {ref}`File 'src/charm.py' ` | +| 4 | tests-unit-test-charm-py | {ref}`File 'tests/unit/test_charm.py' ` | +| 4 | tests-integration-test-charm-py | {ref}`File 'tests/integration/test_charm.py' ` | +| 4 | tox-ini | {ref}`File 'tox.ini ` | +| 3 | the-juju-execution-flow-for-a-charm | {ref}`The Juju execution flow for a charm ` | +| 3 | charm-taxonomy | {ref}`Charm taxonomy ` | +| 4 | 12-factor-app-charm | {ref}`12-Factor app charm <12-factor-app-charm>` | +| 3 | charm-maturity | {ref}`Charm maturity ` | +| 4 | charm-maturity-stage-1 | {ref}`Charm maturity stage 1 ` | +| 4 | charm-maturity-stage-2 | {ref}`Charm maturity stage 2 ` | +| 3 | naming | {ref}`Charm naming guidelines ` | +| 3 | styleguide | {ref}`Charm development best practices ` | +| 2 | charmcraft | {ref}`Charmcraft ` | +| 3 | charmcraft-cli-commands | {ref}`List of Charmcraft CLI commands ` | +| 4 | charmcraft-analyse | {ref}``charmcraft analyse` ` | +| 4 | charmcraft-build | {ref}``charmcraft build` ` | +| 4 | charmcraft-clean | {ref}``charmcraft clean` ` | +| 4 | charmcraft-close | {ref}``charmcraft close` ` | +| 4 | charmcraft-create-lib | {ref}``charmcraft create-lib` ` | +| 4 | charmcraft-expand-extensions | {ref}``charmcraft expand-extensions` ` | +| 4 | charmcraft-fetch-lib | {ref}``charmcraft fetch-lib` ` | +| 4 | charmcraft-fetch-libs | {ref}``charmcraft fetch-libs` ` | +| 4 | charmcraft-init | {ref}``charmcraft init` ` | +| 4 | charmcraft-list-extensions | {ref}``charmcraft list-extensions` ` | +| 4 | charmcraft-list-lib | {ref}``charmcraft list-lib` ` | +| 4 | charmcraft-login | {ref}``charmcraft login` ` | +| 4 | charmcraft-logout | {ref}``charmcraft logout` ` | +| 4 | charmcraft-names | {ref}``charmcraft names` ` | +| 4 | charmcraft-pack | {ref}``charmcraft pack` ` | +| 4 | charmcraft-prime | {ref}``charmcraft prime` ` | +| 4 | charmcraft-promote-bundle | {ref}``charmcraft promote-bundle` ` | +| 4 | charmcraft-pull | {ref}``charmcraft pull` ` | +| 4 | charmcraft-publish-lib | {ref}``charmcraft publish-lib` ` | +| 4 | charmcraft-register | {ref}``charmcraft register` ` | +| 4 | charmcraft-register-bundle | {ref}``charmcraft register-bundle` ` | +| 4 | charmcraft-release | {ref}``charmcraft release` ` | +| 4 | charmcraft-remote-build | {ref}``charmcraft remote-build` ` | +| 4 | charmcraft-resources | {ref}``charmcraft resources` ` | +| 4 | charmcraft-resource-revisions | {ref}``charmcraft resource-revisions` ` | +| 4 | charmcraft-revisions | {ref}``charmcraft revisions` ` | +| 4 | charmcraft-set-resource-architectures | {ref}``charmcraft set-resource-architectures` ` | +| 4 | charmcraft-stage | {ref}``charmcraft stage` ` | +| 4 | charmcraft-status | {ref}``charmcraft status` ` | +| 4 | charmcraft-unregister | {ref}``charmcraft unregister` ` | +| 4 | charmcraft-upload | {ref}``charmcraft upload` ` | +| 4 | charmcraft-upload-resource | {ref}``charmcraft upload-resource` ` | +| 4 | charmcraft-version | {ref}``charmcraft version` ` | +| 4 | charmcraft-whoami | {ref}``charmcraft whoami` ` | +| 3 | charmcraft-extension-flask-framework | {ref}`Charmcraft extension ‘flask-framework’ ` | +| 3 | charmcraft-extension-django-framework | {ref}`Charmcraft extension 'django-framework' ` | +| 3 | charmcraft-extension-fastapi-framework | {ref}`Charmcraft extension 'fastapi-framework' ` | +| 3 | charmcraft-extension-go-framework | {ref}`Charmcraft extension 'go-framework' ` | +| 3 | charmcraft-deprecations | {ref}`Charmcraft deprecation notices ` | +| 3 | charmcraft-analyzers-and-linters | {ref}`Charmcraft analyzers and linters ` | +| 2 | charmhub | {ref}`Charmhub ` | +| 2 | charm-relation-interfaces | {ref}``charm-relation-interfaces` ` | +| 2 | event | {ref}`Event ` | +| 3 | list-of-events | {ref}`List of events ` | +| 4 | events | {ref}`Lifecycle events ` | +| 4 | secret-events | {ref}`Secret events ` | +| 4 | relation-events | {ref}`Relation events ` | +| 4 | storage-events | {ref}`Storage events ` | +| 3 | custom-event | {ref}`Custom event ` | +| 3 | charm-lifecycle | {ref}`Charm lifecycle ` | +| 2 | extension | {ref}`Extension ` | +| 2 | library | {ref}`Library ` | +| 2 | jhack | {ref}``jhack` ` | +| 3 | jhack-tail | {ref}``jhack tail` ` | +| 3 | jhack-show-relation | {ref}``jhack show-relation` ` | +| 3 | library-index | {ref}`Popular charm library index ` | +| 2 | ops | {ref}`Ops ` | +| 2 | pebble | {ref}`Pebble ` | +| 2 | profile | {ref}`Profile ` | +| 2 | promotion | {ref}`Promotion ` | +| 2 | publication | {ref}`Publication ` | +| 3 | reasons-to-publish-your-charm-on-charmhub | {ref}`Reasons to publish your charm on Charmhub ` | +| 2 | pytest-operator | {ref}``pytest-operator` ` | +| 2 | revision | {ref}`Revision ` | +| 2 | rockcraft | {ref}`Rockcraft ` | +| 3 | rockcraft-extension-flask-framework | {ref}`Rockcraft extension ‘flask-framework’ ` | +| 3 | rockcraft-extension-django-framework | {ref}`Rockcraft extension 'django-framework' ` | +| 3 | rockcraft-extension-fastapi-framework | {ref}`Rockcraft extension 'fastapi-framework' ` | +| 2 | scenario | {ref}`Scenario ` | +| 3 | scenario-context | {ref}`Context ` | +| 3 | scenario-event | {ref}`Event ` | +| 3 | scenario-state | {ref}`State ` | +| 2 | status | {ref}`Status ` | +| 2 | storage | {ref}`Storage ` | +| 2 | stored-state-uses-limitations | {ref}`StoredState: Uses, Limitations ` | +| 2 | testing | {ref}`Testing ` | +| 3 | interface-tests | {ref}`Interface tests ` | +| 2 | yaml-anchors-and-aliases | {ref}`YAML anchors and aliases ` | +| 1 | explanation | {ref}`Explanation ` | +| 2 | history | {ref}`Charming history ` | +| 2 | charmed-operators-vs-kubernetes-operators | {ref}`Charmed operators vs. Kubernetes operators ` | +| 2 | how-and-when-to-defer-events | {ref}`How and When to Defer Events ` | +| 2 | holistic-vs-delta-charms | {ref}`Holistic vs delta charms ` | +| 2 | talking-to-a-workload-control-flow-from-a-to-z | {ref}`Talking to a workload: control flow from A to Z ` | +| | roadmap | {ref}`Roadmap ` | +| | | Level 4+ items (currently not supported) | +| | events | {ref}`Lifecycle events ` | +| | collect-metrics-event | {ref}``collect-metrics` (deprecated) ` | +| | config-changed-event | {ref}``config-changed` ` | +| | install-event | {ref}``install` ` | +| | leader-elected-event | {ref}``leader-elected` ` | +| | leader-settings-changed-event | {ref}``leader-settings-changed` ` | +| | post-series-upgrade-event | {ref}``post-series-upgrade` ` | +| | pre-series-upgrade-event | {ref}``pre-series-upgrade` ` | +| | remove-event | {ref}``remove` ` | +| | start-event | {ref}``start` ` | +| | stop-event | {ref}``stop` ` | +| | update-status-event | {ref}``update-status` ` | +| | upgrade-charm-event | {ref}``upgrade-charm` ` | +| | secret-events | {ref}`Secret events ` | +| | event-secret-changed | {ref}``secret-changed` ` | +| | event-secret-expired | {ref}``secret-expired` ` | +| | event-secret-remove | {ref}``secret-remove` ` | +| | event-secret-rotate | {ref}``secret-rotate` ` | +| | relation-events | {ref}`Relation events ` | +| | relation-name-relation-broken-event | {ref}``-relation-broken` ` | +| | relation-name-relation-changed-event | {ref}``-relation-changed` ` | +| | relation-name-relation-created-event | {ref}``-relation-created` ` | +| | relation-name-relation-departed-event | {ref}``-relation-departed` ` | +| | relation-name-relation-joined-event | {ref}``-relation-joined` ` | +| | storage-events | {ref}`Storage events ` | +| | storage-name-storage-attached-event | {ref}``-storage-attached` ` | +| | storage-name-storage-detaching-event | {ref}``-storage-detaching` ` | +| | | Other events | +| | action-name-action-event | {ref}``-action` ` | +| | container-name-pebble-custom-notice-event | {ref}``-pebble-custom-notice` ` | +| | container-name-pebble-ready-event | {ref}``-pebble-ready` ` | +| | | Ops events | +| | events-collect-app-status-and-collect-unit-status | {ref}`Events 'collect-app-status' and 'collect-unit-status' ` | +| | | Charmcraft CLI commands | +| | | Files in the charm project | +| | | Charm maturity | +| | | Developer tools >> Tools for debugging | +| | | Ops | +| | ops-classes | {ref}`Ops classes ` | +| | api-reference | [API reference](https://ops.rtfd.io) | +| | | Publication | +| | | Testing | +| | | Unit testing | +| | | Integration testing | +| | | Other | +| | create-an-ubuntu-virtual-machine-with-multipass |     {ref}`Create an Ubuntu virtual machine with Multipass ` | +| | hello-world | {ref}`Hello, world ` | +| | setup | {ref}`Set-up ` | +| | | INTEGRATE WITH EXISTING MATERIAL | +| | libraries | {ref}`Libraries ` | + + +``` + +

Redirects

+ +```{dropdown} Mapping table + +| Location | Path | +|------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| /docs/sdk/charm-types | /docs/sdk/charm-types-by-destination-type | +| /docs/sdk/setting-up-charmcraft | /docs/sdk/install-charmcraft | +| /docs/sdk/bundles | /docs/sdk/publish-a-charm-bundle-to-charmhub | +| /docs/sdk/bundle-reference | /docs/sdk/manage-bundles | +| /docs/sdk/charmcraft-libraries | /docs/sdk/manage-libraries | +| /docs/sdk/event-hook | /docs/sdk/event | +| /docs/sdk/setup | /docs/sdk/tutorials | +| /docs/sdk/ | /docs/sdk | +| /docs/sdk/charm-types-by-deployment-type | /docs/sdk/charm-types | +| /docs/sdk/charm-types-by-development-type | /docs/sdk/charm-types | +| /docs/sdk/charm-pre-publication-checklist | /docs/sdk/charm-publication-checklist | +| /docs/sdk/initialise-your-charm | /docs/sdk/set-up-a-charm-project | +| /docs/sdk/metadata-reference | /docs/sdk/metadata-yaml | +| /docs/sdk/charm-anatomy | /docs/sdk/list-of-files-in-the-charm-project | +| /docs/sdk/relations | /docs/sdk/integration | +| /docs/sdk/the-lifecycle-of-charm-relations | /docs/sdk/the-lifecycle-of-charm-integrations | +| /docs/sdk/assumes | /docs/sdk/metadata-yaml | +| /docs/sdk/debugging | /docs/sdk/debug-a-charm | +| /docs/sdk/build-and-deploy-minimal-kubernetes-charm | /docs/sdk/from-zero-to-hero-write-your-first-kubernetes-charm | +| /docs/sdk/the-location-of-a-charm-library-inside-a-charm | /docs/sdk/library | +| /docs/sdk/charm-libraries | /docs/sdk/library | +| /docs/sdk/manage-charms | /docs/sdk/how-to | +| /docs/sdk/build-your-charm | /docs/sdk/pack-a-charm | +| /docs/sdk/deploy-your-charm | /docs/sdk/deploy-a-charm | +| /docs/sdk/testing | /docs/sdk/write-a-unit-test-for-a-charm | +| /docs/sdk/write-an-integration-test-for-a-charm | /docs/sdk/write-an-integration-test-for-a-charm | +| /docs/sdk/pack-your-reactive-based-charm-with-charmcraft | /docs/sdk/pack-a-reactive-based-charm-with-charmcraft | +| /docs/sdk/pack-your-hooks-based-charm-with-charmcraft | /docs/sdk/pack-a-hooks-based-charm-with-charmcraft | +| /docs/sdk/understand-your-application | /docs/sdk/study-your-application | +| /docs/sdk/charm-types | /docs/sdk/charm-taxonomy | +| /docs/sdk/charm-publication-checklist | /docs/sdk/charm-maturity-stage-1 | +| /docs/sdk/charm-evaluation-checklist | /docs/sdk/charm-maturity-stage-2 | +| /docs/sdk/build-and-deploy-minimal-machine-charm | /docs/sdk/write-your-first-machine-charm | +| /docs/sdk/run-tests | /docs/sdk/list-of-files-in-the-charm-project | +| /docs/sdk/tests--init--py | /docs/sdk/list-of-files-in-the-charm-project | +| /docs/sdk/tests-test-charm-py | /docs/sdk/tests-unit-test-charm-py | +| /docs/sdk/version | /docs/sdk/list-of-files-in-the-charm-project | +| /docs/sdk/write-an-integration-test-for-a-charm | /docs/sdk/write-integration-tests-for-a-charm | +| /docs/sdk/integration-testing-cookbook | /docs/sdk/write-integration-tests-for-a-charm | +| /docs/sdk/a-charms-life | /docs/sdk/charm-lifecycle | +| /docs/sdk/charmed-operators | /docs/sdk/charm | +| /docs/sdk/deferring-events-details-and-dilemmas | /docs/sdk/how-and-when-to-defer-events | +| /docs/sdk/create-a-charm-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/configure-a-charm-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/compare-a-bundle-to-a-model | /docs/sdk/manage-bundles | +| /docs/sdk/pack-a-charm-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/deploy-a-charm-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/publish-a-charm-bundle-to-charmhub | /docs/sdk/manage-bundles | +| /docs/sdk/configure-a-charm-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/set-charm-channels-within-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/set-charm-constraints-within-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/set-charm-options-within-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/integrate-a-local-charm-into-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/add-an-overlay-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/append-an-overlay-bundle-to-a-base-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/resolve-a-relative-path-inside-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/remove-an-application-from-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/replace-machines-in-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/modify-relations-inside-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/add-machine-specifications-to-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/bind-endpoints-within-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/specify-application-expose-parameters-within-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/use-charm-resources-in-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/recycle-machines-in-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/add-a-placement-directive-to-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/add-storage-directives-to-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/set-up-subordinate-charms-inside-a-bundle | /docs/sdk/manage-bundles | +| /docs/sdk/charm-bundles | /docs/sdk/bundle | +| /docs/sdk/kubernetes-vs-non-kubernetes-bundles | /docs/sdk/bundle.yaml | +| /docs/sdk/charmcraft-analyze | /docs/sdk/charmcraft-analyse | +| /docs/sdk/action | [https://juju.is/docs/juju/action](https://juju.is/docs/juju/action) | +| /docs/sdk/channel | [https://juju.is/docs/juju/channel](https://juju.is/docs/juju/channel) | +| /docs/sdk/configuration | [https://juju.is/docs/juju/configuration](https://juju.is/docs/juju/configuration) | +| /docs/sdk/hook | [https://juju.is/docs/juju/hook](https://juju.is/docs/juju/hook) | +| /docs/sdk/hook-tool | [https://juju.is/docs/juju/hook-tool](https://juju.is/docs/juju/hook-tool) | +| /docs/sdk/charm-environment-variables | {ref}`https://juju.is/docs/juju/charm-environment-variables <4449md>` | +| /docs/sdk/leadership-hook-tools | {ref}`https://juju.is/docs/juju/hook-tool <4449md>` | +| /docs/sdk/integration | [https://juju.is/docs/juju/relation](https://juju.is/docs/juju/relation) | +| /docs/sdk/the-lifecycle-of-charm-integrations | [https://juju.is/docs/juju/relation](https://juju.is/docs/juju/relation) | +| /docs/sdk/about-resources | [https://juju.is/docs/juju/charm-resource](https://juju.is/docs/juju/charm-resource) | +| /docs/sdk/secret | [https://juju.is/docs/juju/secret](https://juju.is/docs/juju/secret) | +| /docs/sdk/define-a-resource-for-your-charm | /docs/sdk/resources | +| /docs/sdk/associate-a-resource-to-your-charm | /docs/sdk/resources | +| /docs/sdk/publishing-resources | /docs/sdk/resources | +| /docs/sdk/attach-a-resource-to-a-charm-at-release-time | /docs/sdk/resources | +| /docs/sdk/access-a-resource-from-your-charm | /docs/sdk/resources | +| /docs/sdk/write-your-first-kubernetes-charm-using-the-paas-app-charmer | /docs/sdk/write-your-first-kubernetes-charm-for-a-flask-app | +| /docs/sdk/constructs | /docs/sdk/ops | +| /docs/sdk/framework | /docs/sdk/ops | +| /docs/sdk/leadership | /docs/sdk/ops | +| /docs/sdk/ops-model | /docs/sdk/ops | +| /docs/sdk/ops-pebble | /docs/sdk/ops | +| /docs/sdk/developer-tools | /docs/sdk/jhack | +| /docs/sdk/tools-for-debugging | /docs/sdk/jhack | +| /docs/sdk/paas-app-charmer | /docs/sdk/12-factor-app-charm | +| /docs/sdk/paas-charm | /docs/sdk/12-factor-app-charm | +| /docs/sdk/build-a-paas-charm | /docs/sdk/build-a-12-factor-app-charm | +| | | + +``` + + + \ No newline at end of file diff --git a/tmp/t/charm-taxonomy.md b/tmp/t/charm-taxonomy.md new file mode 100644 index 000000000..d3f18bade --- /dev/null +++ b/tmp/t/charm-taxonomy.md @@ -0,0 +1,227 @@ +(charm-taxonomy)= +# Charm taxonomy + +Juju has been around for some time. As a result, charm writing has gone through multiple frameworks and patterns. Here we describe the resulting charm taxonomy. + +**Contents:** + +- [Charm types, by substrate](#heading--charm-types-by-substrate) + - [Machine charms](#heading--machine-charms) + - [Roles](#heading--roles) + - [Principal charms](#heading--principal-charms) + - [Subordinate charms](#heading--subordinate-charms) + - [Patterns](#heading--patterns) + - [Proxy](#heading--proxy) + - [Kubernetes charms](#heading--kubernetes-charms) + - [Patterns](#heading--patterns) + - [Sidecar](#heading--sidecar) + - [Workload-less](#heading--workload-less) + - [Podspec](#heading--podspec) +- [Charm types, by generation](#heading--charm-types-by-generation) + - [Bare](#heading--bare) + - [Ops](#heading--ops) + - [Reactive](#heading--reactive) + +Firstly, we can differentiate charms by the substrate they are intended to run on; either machines or containers on Kubernetes. For machine charms, we can further draw a line between principal and subordinate charms. +In Kubernetes charms, we can identify a few fuzzy but useful categories of charms, depending on the type of the workload they manage. + +Finally, we can differentiate charms by the technology they are written with. Several generations of libraries have been used, the latest of which is `ops`. + + +

Charm types, by substrate

+ +There are 'machine charms', meant to be deployed on VMs, and 'Kubernetes charms', meant to be deployed on Kubernetes. + +

Machine charms

+ + +Juju’s beginnings were centered around simplifying the deployment of complex applications and services in a cloud-first world. At the time, many of those applications were run in virtual machines or on bare-metal servers, and deployments to these environments continue to enjoy first-class support. A machine charm can be deployed to a number of different underlying compute/storage resource providers: + +- Bare-metal (using [MAAS](https://maas.io/)) +- Virtual machine (using KVM in Openstack, EC2 in AWS, VMware Environment etc.) +- Container (using [LXD](https://linuxcontainers.org/lxd/introduction/) cluster) + +Examples of machine charms include: +* [Ubuntu](https://charmhub.io/ubuntu) +* [Vault](https://charmhub.io/vault) +* [Rsyslog](https://charmhub.io/rsyslog) + + +

Roles

+ +A machine charm can have the `subordinate` role. When a machine charm is not subordinate, it is said to be `principal`. The basic difference is that a subordinate charm is deployed on the same unit as the principal charm it is attached to, while a principal charm is deployed on a new unit (i.e. its own machine). + + + +
Principal charms
+ +All charms are principal charms, except those that are subordinate. + +
Subordinate charms
+ + + + +Subordinate charms were created to enable the development of charms that could be deployed alongside existing charms -- also known as principal charms -- to augment them with specific functionality. They are, in many ways, analogous to sidecar charms. + +A subordinate charm depends on the creation of a relationship to a principal charm, it is never deployed as a standalone application. This is best explained with an example: + +Consider the deployment of a large web application scaled to `n` replicas. Each instance of the charm comprises a `unit`, in Juju parlance, and an `App` is the sum of all units of a given charm with the same name. When a large web application is scaled to `n` replicas, `n` `units` will be started by Juju. + + +The administrators of the web application will likely want to collect logs from the application server. The developer of the application charm may not have included a mechanism for forwarding logs from the service that is compatible with the administrator’s specific environment. By using a subordinate charm such as [`rsyslog-forwarder`](https://jaas.ai/rsyslog-forwarder/trusty/1), the administrator can ensure that each deployed unit of their web application is automatically configured to forward logs using `rsyslog`. To do this, they must deploy the subordinate and `juju integrate` their web application to it. + +Subordinate charms are written in the same way as other charms, with the addition of an extra `subordinate` flag in the charm’s [metadata](https://juju.is/docs/sdk/metadata-reference). + +Examples of subordinate charms include: +- [Telegraf](https://charmhub.io/telegraf) +- [Canonical Livepatch](https://charmhub.io/canonical-livepatch) +- [Nrpe](https://charmhub.io/nrpe) + +

Patterns

+ +
Proxy
+ +Proxy machine charms (also sometimes called "integrator charms") are the analogues of workload-less kubernetes charms. Historically they have been developed to act as stand-ins for other applications, hence the name. +These are charms that do nothing or little in terms of software installation on the host VM, and whose job consists solely in interacting with Juju constructs. + +Examples of proxy charms include: +- [AWS Integrator](https://charmhub.io/aws-integrator) +- [Openstack Integrator](https://charmhub.io/openstack-integrator) +- [GCP Integrator](https://charmhub.io/gcp-integrator) + +

Kubernetes charms

+ + +The first charms were machine charms. +More recently, [Juju](https://juju.is/docs/juju) introduced support for charms on Kubernetes. Juju can bring the same benefits to applications deployed on Kubernetes, by placing operators alongside workloads to manage them throughout their lifecycle. When a [model](https://juju.is/docs/models) is created with Juju, a corresponding Kubernetes [namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) is created. When a charm is deployed on Kubernetes, it is deployed as a [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) that manages a set of [Pods](https://kubernetes.io/docs/concepts/workloads/pods/) running the specified application containers alongside a sidecar container containing the charm code. + +Examples of Kubernetes charms include: +* [Discourse K8s](https://charmhub.io/discourse-k8s) +* [Zinc K8s](https://charmhub.io/zinc-k8s) +* [Postgresql K8s](https://charmhub.io/postgresql-k8s) +* [Node-RED K8s](https://github.com/juanmanuel-tirado/nodered-charm-k8s) + + + +

Patterns

+ +Over the time, some patterns have emerged in Kubernetes charms. +A charm is an operator, so a natural categorization of charms is based on "what is that thing which the charm operates". +Some charms operate workloads running on a container; some charms operate Kubernetes resources, while some others operate `juju` resources. These are the `sidecar`, `workload-less` and `podspec` charm categories we describe in this section. + +```{note} + This categorization is fuzzy, since a charm can in principle do multiple of these things at once, or even "other" things. +``` + +```{note} + +Except for `podspec` (`pod` is a k8s construct), similar patterns could be identified in the machine charm space. + +``` + + +
Sidecar
+ + +By definition, the [sidecar pattern](https://www.learncloudnative.com/blog/2020-09-30-sidecar-container) is designed to allow the augmentation of workloads with additional capabilities and features. The pattern is implemented in this case by a small auxiliary container, located in the same Pod as your application, that provides operations functionality - this is exactly how Juju operates in other environments, and a well-established pattern in the Kubernetes community. + +By utilising this pattern, we ensure that there is always an operator right next to every unit of the workload, irrespective of how the application is scaled. The operator will always have direct access to shared memory, the same network namespace and the ability to manipulate the workload as required to keep it running smoothly. This approach simplifies the operation of upstream or third-party application images, enabling administrators to make changes at runtime to suit their environment should they wish, without the requirement to maintain a fleet of bespoke container images. + +For charm developers, these benefits are realised by utilising [Pebble](https://github.com/canonical/pebble) to manage workloads. Pebble is a lightweight, API-driven process supervisor designed for use with charms. Pebble enables charm developers to define how they want workloads to run, and provides an API that enables operations code to manage the workloads throughout their life. + + +Examples of sidecar charms include: +- [Traefik k8s](https://charmhub.io/traefik-k8s) + + +
Workload-less
+ +Charms are processes that exchange data. Sometimes you want to manipulate that data without having to rewrite or reconfigure the charms themselves; in that case you can implement a "charm-in-the-middle" pattern that sits in between and manipulates the relation data acting like a filter/proxy of some kind. +Other times you want to write a charm that manages a stack of other charmed applications (danger zone). Welcome to workload-less charms! + +Examples of workload-less charms include: +- [Traefik Route k8s](https://charmhub.io/traefik-route-k8s) + + +
Podspec
+ +Podspec charms create and manage Kubernetes resources that are used by other charms or applications running on the cloud. + +This pattern is discouraged because it is more difficult to implement correctly, as the resources one creates and manages via podspec charms practically avoid juju's control, and therefore sidestep its model. While one of the core tenets of juju is that juju could suddenly disappear and all of the cloud services should continue to work as they did before (all that juju does is help operating them, not keeping them alive), the idea is that charms "cooperate" with juju by not doing anything without going through juju first, to prevent juju's model from desyncing. + + +Examples of podspec charms include: +- [Redis k8s](https://charmhub.io/redis-k8s) + + +```{caution} + +Beginning with `juju` v.3.0, podspec charms are deprecated. + +``` + +

Charm types, by generation

+ + +Charm development has been going on for years, so naturally many attempts have been made at making the development easier. +The 'raw' API juju exposes can be interacted with directly, but most people will want to use (at least) the bash scripts that come by default with every charm deployment, called `hook tools`. If your charm only uses those, then you're writing a "bare" charm. + +If you fancy using a higher-level, object-oriented Python library to interact with the juju model, then you should be using `ops`. + +There exists another python framework that also wraps the hook tools but offers a different (less OOP, less idiomatic) interface, called `reactive`. This framework is deprecated and no longer maintained, mentioned here only for historical reasons. + + +

Ops

+ +These are charms developed using the {ref}`Ops ` framework. They represent the current recommended standard, which also integrates best with {ref}`Charmcraft `. + +Examples of Ops-based charms include: +- [LXD](https://charmhub.io/lxd) +- [Discourse K8s](https://charmhub.io/discourse-k8s) +- [Zinc K8s](https://charmhub.io/zinc-k8s) +- [Postgresql K8s](https://charmhub.io/postgresql-k8s) +- [Node-RED K8s](https://github.com/juanmanuel-tirado/nodered-charm-k8s) + + +

Reactive

+ +These are charms developed using the [reactive](https://charmsreactive.readthedocs.io/en/latest/) framework. + +Note that this is a framework that has now been superseded by Ops. + +```{note} + +Please do not use reactive. Use Ops. + +``` + +Examples of reactive-based charms include: +- [Containers Kubernetes Worker](https://charmhub.io/containers-kubernetes-worker) +- [Prometheus2](https://charmhub.io/prometheus2) +- [Telegraf](https://charmhub.io/telegraf) +- [Postgresql](https://charmhub.io/postgresql) +- [Node-RED](https://github.com/juanmanuel-tirado/nodered-charm) +- [Canonical Livepatch](https://charmhub.io/canonical-livepatch) + + +

Bare

+ +These are charms developed without the help of a framework, with all the hook invocations being coded manually. + +```{important} + +For this reason these charms are also called 'hooks-based', or 'hooks-only', charms. + +``` + +Examples of bare charms include: +- [this tiny bash charm](https://charmhub.io/tiny-bash), ideal for educational purposes +- [Mediawiki](https://charmhub.io/mediawiki) +- [Nrpe](https://charmhub.io/nrpe) + + +
+ +> **Contributors:** @mvlassis, @ppasotti, @tmihoc \ No newline at end of file diff --git a/tmp/t/charm.md b/tmp/t/charm.md new file mode 100644 index 000000000..228565319 --- /dev/null +++ b/tmp/t/charm.md @@ -0,0 +1,106 @@ +(charm)= +# Charm + +In Juju, a **charm** is an operator -- software that wraps an [application](https://juju.is/docs/juju/application) and that contains all of the instructions necessary for deploying, configuring, scaling, integrating, etc., the application +on any [cloud](https://juju.is/docs/juju/cloud) using [Juju](https://juju.is/docs/juju). + + + +> See more: [SDK | List of files in the charm project](https://juju.is/docs/sdk/list-of-files-in-the-charm-project), [SDK | The Juju execution flow for a charm](https://juju.is/docs/sdk/the-juju-execution-flow-for-a-charm) + +Charms are publicly available on [Charmhub](https://juju.is/docs/sdk/charmhub). + +Charms are currently of two kinds, depending on the target deployment substrate: + +1. **Machine charms:** Charms made to deploy on a bare-metal server, virtual machine, or system container. + - [Charmhub | Machine charms](https://charmhub.io/?base=vm&type=all) +1. **Kubernetes charms:** Charms built to deploy on Kubernetes. + - [Charmhub | Kubernetes charms](https://charmhub.io/?base=kubernetes&type=all) + - [Charmhub | example Kubernetes charm: PostgreSQL K8s](https://charmhub.io/postgresql-k8s) + +Charms are currently developed using [Charmcraft](https://juju.is/docs/sdk/charmcraft) and [Ops](https://juju.is/docs/sdk/ops). + +> See more: SDK https://juju.is/docs/sdk/charm-taxonomy , SDK https://juju.is/docs/sdk/charmed-operators-vs-kubernetes-operators + +While the quality of individual charms may vary, charms are intended to implement a general and comprehensive approach for operating applications. + +> See more: [SDK | Charm maturity](https://juju.is/docs/sdk/charm-maturity) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tmp/t/charmcraft-analyzers-and-linters.md b/tmp/t/charmcraft-analyzers-and-linters.md new file mode 100644 index 000000000..e7499193b --- /dev/null +++ b/tmp/t/charmcraft-analyzers-and-linters.md @@ -0,0 +1,98 @@ +(charmcraft-analyzers-and-linters)= +# Charmcraft analyzers and linters + +The following are the different checks that Charmcraft will run explicitly (when the user executes its `analyze` method) or implicitly (when packing charms). + +Any linter or analysis can be set in [the config](https://juju.is/docs/sdk/charmcraft-config) to be excluded from the normal execution. Also note that if any linter ends in error it will block the charm packing (you can pack it anyway using the `--force` option). + +You can read more about these checks in the [Charmcraft Analyze Specification](https://discourse.charmhub.io/t/proposal-charmcraft-analyze/4792). + +**Contents:** + - [Language attribute](#heading--language) + - [Framework attribute](#heading--framework) + - [Juju metadata linter](#heading--juju-metadata) + - [Juju actions linter](#heading--juju-actions) + - [Juju config linter](#heading--juju-config) + - [Charm entrypoint linter](#heading--entrypoint) + + +

Language attribute

+ + + +If through analysis, the charm can be detected as being a Python based charm, then language shall be set to `python`. If not, it shall be set to `unknown`. + +When working with Python, it is possible to only publish byte-code. By doing so, troubleshooting is a harder task. Charms with Python sources delivered are preferred. + +This attribute meets the requirements to be set to `python` when: + +- the charm has a text dispatch which executes a .py +- the charm has a .py entry point +- the entry point file is executable + +

Framework attribute

+ + +When using the Operator Framework, it is best to import it from a common path and not make customisation or package forks from it. If the Operator Framework is detected in the charm sources, this attribute's value shall be set to `operator`. If not, the charm may be using the Reactive Framework, and in this case the attribute value will be `reactive`. Else, it shall be set to `unknown`. + +This check hint meets the requirements for Operative Framework when: + +- language attribute is set to `python` +- the charm contains `venv/ops` +- the charm imports `ops` in the entry point + +The requirements for Reactive Framework are: + +...or the Reactive Framework is used, if the charm... + +- has a metadata.yaml with `name` in it +- has a `reactive/.py` file that imports `charms.reactive` +- has a file name that starts with `charms.reactive-` inside the `wheelhouse` directory + + +

Juju metadata linter

+ +This linter verifies that the `metadata.yaml` file exists and is sane. + +The charm is considered to have a valid metadata if the following checks are true: + +- the `metadata.yaml` is present +- it is a valid YAML file +- it has at least the following fields: `name`, `summary`, and `description` + + +

Juju actions linter

+ +*(new in 1.4)* + +This linter verifies that the `actions.yaml` file, if exists, is a valid YAML file. The file is optional. The file contents are not verified. + + +

Juju config linter

+ +*(new in 1.4)* + +This linter verifies that the `config.yaml` file, if exists, is valid. This file is optional. + +If the file exists, it is considered valid if the following checks are true: + +- it has the `options` key +- it is a dictionary +- each item inside has the mandatory `type` key + +Check how to [create config.yaml and configure charms](https://discourse.charmhub.io/t/creating-config-yaml-and-configuring-charms/1039) for more information. + + +

Charm entrypoint linter

+ +*(new in 2.1)* + +Check the entry point is correct. Note that even if most modern charms has a typical `src/charm.py` entry point, not all charms have one, as Juju has different ways to deliver its events. + +This linter validates that, if an entry point is called from the `dispatch` file, that entry point... + +- exists +- is a file +- is executable + +The entry point content is *not* validated. \ No newline at end of file diff --git a/tmp/t/charmcraft-charmcraft.md b/tmp/t/charmcraft-charmcraft.md new file mode 100644 index 000000000..c7df8a8a4 --- /dev/null +++ b/tmp/t/charmcraft-charmcraft.md @@ -0,0 +1,32 @@ +(charmcraft-charmcraft)= +# Charmcraft (`charmcraft`) + +> See also: {ref}`How to manage Charmcraft ` +> +> See more: {ref}`List of Charmcraft commands ` + +Charmcraft (`charmcraft`) is a command line tool used to simplify the creation, building, and publication of a charm. + + + +With Charmcraft you can: + +- Init a new charm file structure +- Build your operator into a charm for distribution +- Register your charm name on [Charmhub](https://charmhub.io/) +- Upload your charms to [Charmhub](https://charmhub.io/) +- Release your charms into channels + +You can use Charmcraft with charms written in any language. However, for ease of development and collaboration, we recommend you use it with charms written in Python using {ref}`Ops `. + + + + \ No newline at end of file diff --git a/tmp/t/charmcraft-deprecation-notices.md b/tmp/t/charmcraft-deprecation-notices.md new file mode 100644 index 000000000..9802416c3 --- /dev/null +++ b/tmp/t/charmcraft-deprecation-notices.md @@ -0,0 +1,164 @@ +(charmcraft-deprecation-notices)= +# Charmcraft deprecation notices + +This document contains a list of `charmcraft` deprecation notices and recommendations. + +**Contents:** + +- [DN01: Configuration keywords are now separated using dashes](#heading--dn01) +- [DN02: A charmcraft.yaml configuration file is now required](#heading--dn02) +- [DN03: Bases configuration is now required](#heading--dn03) +- [DN04: Use `charm-entrypoint` in charmcraft.yaml parts to define the entry point](#heading--dn04) +- [DN05: Use `charm-requirements` in charmcraft.yaml parts to define requirements](#heading--dn05) +- [DN06: The `build` command is deprecated, use `pack` instead.](#heading--dn06) + + +

DN01: Configuration keywords are now separated using dashes

+ + +*Introduced in charmcraft v1.1, enforced in v1.5.0.* + +The `charmcraft.yaml` file used to configure Charmcraft now uses dashes to separate multi-token words. Previously, underscores were used. + +This means that if you have a configuration like this: + +```yaml +charmhub: + api_url: https://someserver.com +``` + +...you would need to migrate it to: + +```yaml +charmhub: + api-url: https://someserver.com +``` + +

DN02: A charmcraft.yaml configuration file is now required

+ + + +*Introduced in charmcraft v1.1, enforced in v1.5.0* + +The `charmcraft.yaml` file will soon be required. The following configuration is assumed: +``` +... +type: charm +bases: + - build-on: + - name: "ubuntu" + channel: "20.04" + run-on: + - name: "ubuntu" + channel: "20.04" +``` + +For more information, check out https://juju.is/docs/sdk/charmcraft-config. + +

DN03: Bases configuration is now required

+ + + +*Introduced in charmcraft v1.1, enforced in v2.0.0.* + +The `charmcraft.yaml` file will soon require `bases` configuration. The following `bases` configuration is assumed: +``` +... +bases: + - build-on: + - name: "ubuntu" + channel: "20.04" + run-on: + - name: "ubuntu" + channel: "20.04" +``` +For more information, check out https://juju.is/docs/sdk/charmcraft-config. + +

DN04: Use `charm-entrypoint` in charmcraft.yaml parts to define the entry point

+ + +*Introduced in charmcraft v1.2, enforced in v2.0.0.* + +Non-default charm entry points should now be defined in the `charm` part in `charmcraft.yaml`. Use `charm-entrypoint` to list the path to the entry point executable, relative to the charm source directory. If not specified, the entry point is assumed to be `src/charm.py`. +``` +... +parts: + charm: + charm-entrypoint: "src/my_entrypoint.py" +... +``` + +

DN05: Use `charm-requirements` in charmcraft.yaml parts to define requirements

+ +*Introduced in charmcraft v1.2, enforced in v2.0.0.* + +Non-default requirement files should now be defined in the `charm` part in `charmcraft.yaml`. Use `charm-requirements` to list the requirement files containing python packages to install. If not specified, `requirements.txt` will be used if the file exists in the charm source directory. +``` +... +parts: + charm: + charm-requirements: ["reqs1.txt", "reqs2.txt"] +... +``` + + +

DN06: The `build` command is deprecated, use `pack` instead.

+ +*Introduced in charmcraft v1.5.0, enforced in v2.0.0.* + +The build command is deprecated (as this verb will have other meaning in the future). Use pack instead: +```text +$ charmcraft pack +Charms packed: + my-super-charm_ubuntu-20.04-amd64.charm +$ charmcraft help pack +Usage: + charmcraft pack [options] + +Summary: + Build and pack a charm operator package or a bundle. + + You can `juju deploy` the resulting `.charm` or bundle's `.zip` + file directly, or upload it to Charmhub with `charmcraft upload`. + + For the charm you must be inside a charm directory with a valid + `metadata.yaml`, `requirements.txt` including the `ops` package + for the Python operator framework, and an operator entrypoint, + usually `src/charm.py`. See `charmcraft init` to create a + template charm directory structure. + + For the bundle you must already have a `bundle.yaml` (can be + generated by Juju) and a README.md file. + +Options: + -h, --help: Show this help message and exit + -v, --verbose: Show debug information and be more verbose + -q, --quiet: Only show warnings and errors, not progress + -t, --trace: Show all information needed to trace internal + behaviour + -p, --project-dir: Specify the project's directory (defaults to + current) + --debug: Launch shell in build environment upon failure + --destructive-mode: Pack charm using current host which may result + in breaking changes to system configuration + -e, --entrypoint: The executable which is the operator entry + point; defaults to 'src/charm.py' + -r, --requirement: File(s) listing needed PyPI dependencies (can + be used multiple times); defaults to + 'requirements.txt' + --shell: Launch shell in build environment in lieu of + packing + --shell-after: Launch shell in build environment after packing + --bases-index: Index of 'bases' configuration to build (can be + used multiple times); defaults to all + --force: Force packing even after finding lint errors + +See also: + analyze + build + clean + init + version + +For a summary of all commands, run 'charmcraft help --all'. +``` \ No newline at end of file diff --git a/tmp/t/charmcraft-extension-django-framework.md b/tmp/t/charmcraft-extension-django-framework.md new file mode 100644 index 000000000..3574fdb58 --- /dev/null +++ b/tmp/t/charmcraft-extension-django-framework.md @@ -0,0 +1,225 @@ +(charmcraft-extension-django-framework)= +# Charmcraft extension 'django-framework' + +The `django-framework` Charmcraft {ref}`extension ` includes configuration options customised for a Django application. This document describes all the keys that a user may interact with. + +```{tip} + +**If you'd like to see the full contents contributed by this extension:**
See {ref}`How to manage extensions `. + +``` + +## Database requirement + +Django requires a database to function. When generating a new project, the default is to make use of [SQLite^](https://www.sqlite.org/). Using SQLite is not recommended for production, especially on Kubernetes deployments, because the database is not shared across units and any contents will be removed upon a new container being deployed. The `django-framework` extension therefore requires a database integration for every application, such as [PostgreSQL^](https://www.postgresql.org/) or [MySQL^](https://www.mysql.com/). See {ref}`the how-to guide ` for how to deploy a database and integrate the Django application with it. + +## `charmcraft.yaml` > `config` > `options` + +You can use the predefined options (run `charmcraft expand-extensions` for details) but also add your own, as needed. + +In the latter case, any option you define will be used to generate environment variables; a user-defined option `config-option-name` will generate an environment variable named `DJANGO_CONFIG_OPTION_NAME` where the option name is converted to upper case, dashes will be converted to underscores and the `DJANGO_` prefix will be added. + +In either case, you will be able to set it in the usual way by running `juju config