Lint Support and Role Updates

This commit is contained in:
Aaron 2023-03-03 03:53:02 -05:00 committed by GitHub
parent 9f1cd1e162
commit cd41914a23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 5035 additions and 274 deletions

18
.ansible-lint Normal file
View File

@ -0,0 +1,18 @@
---
profile: production
exclude_paths:
- .cache/
- .github/
- examples/
parseable: true
verbosity: 1
use_default_rules: true
skip_list:
- '204' # Allow string length greater than 160 chars
- 'no-changed-when' # False positives for running command shells
- 'yaml' # Disable YAML linting since it's done by yamllint
- 'empty-string-compare' # Allow compare to empty string

42
.editorconfig Normal file
View File

@ -0,0 +1,42 @@
# This file is the top-most EditorConfig file
root = true
# All Files
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# JSON Files
[*.{json,json5,webmanifest}]
indent_size = 2
# YAML Files
[*.{yml,yaml}]
indent_size = 2
# Markdown Files
[*.{md,mdx}]
indent_size = 2
trim_trailing_whitespace = false
# Web Files
[*.{htm,html,js,jsm,ts,tsx,cjs,cts,ctsx,mjs,mts,mtsx,css,sass,scss,less,pcss,svg,vue}]
indent_size = 2
# Bash Files
[*.sh]
indent_size = 2
end_of_line = lf
# Makefiles
[{Makefile,**.mk}]
indent_style = tab
# Python Files
[*.py]
indent_style = space
indent_size = 4

View File

@ -1,4 +1,7 @@
---
name: CI Tests name: CI Tests
# yamllint disable-line rule:truthy
on: on:
push: push:
branches: branches:

View File

@ -1,4 +1,7 @@
---
name: Full Integration Test name: Full Integration Test
# yamllint disable-line rule:truthy
on: on:
schedule: schedule:
- cron: '0 0 * * 0' - cron: '0 0 * * 0'

63
.github/workflows/lint.yaml vendored Normal file
View File

@ -0,0 +1,63 @@
---
name: Lint
# yamllint disable-line rule:truthy
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
lint:
name: Perform Linting
runs-on: ubuntu-latest
steps:
- name: Install shellcheck
run: |
wget -c https://github.com/koalaman/shellcheck/releases/download/v0.9.0/shellcheck-v0.9.0.linux.x86_64.tar.xz -O shellcheck.tar.xz && \
tar -xvf shellcheck.tar.xz && \
sudo mv ./shellcheck-v0.9.0/shellcheck /usr/bin/shellcheck && \
rm -rf shellcheck-v0.9.0
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: npm
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install pipenv
run: |
python -m pip install --upgrade pipenv wheel
- name: Install dependencies
run: make install
- name: Shell Linting
run: make ci-lint-shell
if: success() || failure()
- name: Markdown Linting
run: make ci-lint-markdown
if: success() || failure()
- name: Text Linting
run: make ci-lint-text
if: success() || failure()
- name: Yaml Linting
run: make ci-lint-yaml
if: success() || failure()
- name: Editorconfig Linting
run: make ci-lint-editorconfig
if: success() || failure()
- name: Ansible Linting
run: make ci-lint-ansible
if: success() || failure()

View File

@ -1,4 +1,7 @@
---
name: GitHub Release name: GitHub Release
# yamllint disable-line rule:truthy
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:

236
.gitignore vendored Normal file
View File

@ -0,0 +1,236 @@
# Project
# ---------------------------------------------------
*.pyc
.vscode
.idea
hosts
*.log
# MacOS
# ---------------------------------------------------
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows
# ---------------------------------------------------
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# MS Office
# ---------------------------------------------------
*.tmp
# Word temporary
~$*.doc*
# Word Auto Backup File
Backup of *.doc*
# Excel temporary
~$*.xls*
# Excel Backup File
*.xlk
# PowerPoint temporary
~$*.ppt*
# Visio autosave temporary files
*.~vsd*
# VS Code
# ---------------------------------------------------
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
*.app
.snapshots/*
# NodeJS
# ---------------------------------------------------
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

261
.markdownlint.yaml Normal file
View File

@ -0,0 +1,261 @@
---
# Example markdownlint YAML configuration with all properties set to their default value
# Default state for all rules
default: true
# Path to configuration file to extend
extends: null
# MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time
MD001: true
# MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading
MD002:
# Heading level
level: 1
# MD003/heading-style/header-style - Heading style
MD003:
# Heading style
style: "consistent"
# MD004/ul-style - Unordered list style
MD004:
# List style
style: "consistent"
# MD005/list-indent - Inconsistent indentation for list items at the same level
MD005: true
# MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line
MD006: true
# MD007/ul-indent - Unordered list indentation
MD007:
# Spaces for indent
indent: 2
# Whether to indent the first level of the list
start_indented: false
# Spaces for first level indent (when start_indented is set)
start_indent: 2
# MD009/no-trailing-spaces - Trailing spaces
MD009:
# Spaces for line break
br_spaces: 2
# Allow spaces for empty lines in list items
list_item_empty_lines: false
# Include unnecessary breaks
strict: false
# MD010/no-hard-tabs - Hard tabs
MD010:
# Include code blocks
code_blocks: true
# Fenced code languages to ignore
ignore_code_languages: []
# Number of spaces for each hard tab
spaces_per_tab: 2
# MD011/no-reversed-links - Reversed link syntax
MD011: true
# MD012/no-multiple-blanks - Multiple consecutive blank lines
MD012:
# Consecutive blank lines
maximum: 1
# MD013/line-length - Line length
MD013:
# Number of characters
line_length: 150
# Number of characters for headings
heading_line_length: 80
# Number of characters for code blocks
code_block_line_length: 100
# Include code blocks
code_blocks: true
# Include tables
tables: false
# Include headings
headings: true
# Include headings
headers: true
# Strict length checking
strict: false
# Stern length checking
stern: false
# MD014/commands-show-output - Dollar signs used before commands without showing output
MD014: false
# MD018/no-missing-space-atx - No space after hash on atx style heading
MD018: true
# MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading
MD019: true
# MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading
MD020: true
# MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading
MD021: true
# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines
MD022:
# Blank lines above heading
lines_above: 1
# Blank lines below heading
lines_below: 1
# MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line
MD023: true
# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
MD024: false
# MD025/single-title/single-h1 - Multiple top-level headings in the same document
MD025:
# Heading level
level: 1
# RegExp for matching title in front matter
front_matter_title: "^\\s*title\\s*[:=]"
# MD026/no-trailing-punctuation - Trailing punctuation in heading
MD026:
# Punctuation characters not allowed at end of headings
punctuation: ".,;:!。,;:!"
# MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol
MD027: true
# MD028/no-blanks-blockquote - Blank line inside blockquote
MD028: true
# MD029/ol-prefix - Ordered list item prefix
MD029:
# List style
style: "one_or_ordered"
# MD030/list-marker-space - Spaces after list markers
MD030:
# Spaces for single-line unordered list items
ul_single: 3
# Spaces for single-line ordered list items
ol_single: 2
# Spaces for multi-line unordered list items
ul_multi: 3
# Spaces for multi-line ordered list items
ol_multi: 2
# MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines
MD031:
# Include list items
list_items: true
# MD032/blanks-around-lists - Lists should be surrounded by blank lines
MD032: true
# MD033/no-inline-html - Inline HTML
MD033:
# Allowed elements
allowed_elements:
- div
- br
- hr
# MD034/no-bare-urls - Bare URL used
MD034: true
# MD035/hr-style - Horizontal rule style
MD035:
# Horizontal rule style
style: "consistent"
# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading
MD036: false
# MD037/no-space-in-emphasis - Spaces inside emphasis markers
MD037: true
# MD038/no-space-in-code - Spaces inside code span elements
MD038: true
# MD039/no-space-in-links - Spaces inside link text
MD039: true
# MD040/fenced-code-language - Fenced code blocks should have a language specified
MD040:
# List of languages
allowed_languages: []
# Require language only
language_only: false
# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading
MD041:
# Heading level
level: 1
# RegExp for matching title in front matter
front_matter_title: "^\\s*title\\s*[:=]"
# MD042/no-empty-links - No empty links
MD042: true
# MD043/required-headings/required-headers - Required heading structure
MD043:
# List of headings
headings:
- "*"
# List of headings
headers: []
# Match case of headings
match_case: false
# MD044/proper-names - Proper names should have the correct capitalization
MD044:
# List of proper names
names: []
# Include code blocks
code_blocks: true
# Include HTML elements
html_elements: true
# MD045/no-alt-text - Images should have alternate text (alt text)
MD045: true
# MD046/code-block-style - Code block style
MD046:
# Block style
style: "consistent"
# MD047/single-trailing-newline - Files should end with a single newline character
MD047: true
# MD048/code-fence-style - Code fence style
MD048:
# Code fence style
style: "consistent"
# MD049/emphasis-style - Emphasis style should be consistent
MD049:
# Emphasis style should be consistent
style: "consistent"
# MD050/strong-style - Strong style should be consistent
MD050:
# Strong style should be consistent
style: "consistent"
# MD051/link-fragments - Link fragments should be valid
MD051: false
# MD052/reference-links-images - Reference links and images should use a label that is defined
MD052: true
# MD053/link-image-reference-definitions - Link and image reference definitions should be needed
MD053:
# Ignored definitions
ignored_definitions: [
"//"
]

7
.shellcheckrc Normal file
View File

@ -0,0 +1,7 @@
# Allow opening any 'source'd file, even if not specified as input
external-sources=true
# some files are sourced which can make some areas appear unreachable
disable=SC2317
disable=SC2250
disable=SC2312

187
.textlintrc Normal file
View File

@ -0,0 +1,187 @@
{
"rules": {
"common-misspellings": true,
"no-todo": true,
"terminology": {
"defaultTerms": false,
"terms": [
"Grafana",
["GrafanaLabs", "Grafana Labs"],
["GrafanaCloud", "Grafana Cloud"],
"Mimir",
"Loki",
"Phlare",
"Tempo",
"Faro",
"Raintank",
"Prometheus",
"PromQL",
["(E|e)xamplars", "$1xemplars"],
["(D|d)atasource", "$1ata source"],
"CData",
"Google",
"Amazon",
"RedHat",
"Azure",
"Airbnb",
"Android",
"AppleScript",
"AppVeyor",
"AVA",
"BrowserStack",
"Browsersync",
"Codecov",
"CodePen",
"CodeSandbox",
"DefinitelyTyped",
"EditorConfig",
"ESLint",
"GitHub",
"GraphQL",
"iOS",
"JavaScript",
"JetBrains",
"jQuery",
"LinkedIn",
"Lodash",
"MacBook",
"Markdown",
"OpenType",
"PayPal",
"PhpStorm",
"RubyMine",
"Sass",
"SemVer",
"TypeScript",
"UglifyJS",
"Wasm",
"WebAssembly",
"WebStorm",
"WordPress",
"YouTube",
["Common[ .]js", "CommonJS"],
["JSDocs?", "JSDoc"],
["Nodejs", "Node.js"],
["React[ .]js", "React"],
["SauceLabs", "Sauce Labs"],
["StackOverflow", "Stack Overflow"],
["styled ?components", "styled-components"],
["HTTP[ /]2(?:\\.0)?", "HTTP/2"],
["OS X", "macOS"],
["Mac ?OS", "macOS"],
["a npm", "an npm"],
"ECMAScript",
["ES2015", "ES6"],
["ES7", "ES2016"],
"3D",
["3-D", "3D"],
"Ajax",
"API",
["API[']?s", "APIs"],
"CSS",
"GIF",
" HTML ",
"HTTPS",
"IoT",
"I/O",
["I-O", "I/O"],
"JPEG",
"MIME",
"OK",
"PaaS",
" PDF ",
"PNG",
"SaaS",
"URL",
["URL[']?s", "URLs"],
["an URL", "a URL"],
["wi[- ]?fi", "Wi-Fi"],
"McKenzie",
"McConnell",
[" id", " ID"],
["id[']?s", "IDs"],
["backwards compatible", "backward compatible"],
["build system(s?)", "build tool$1"],
["CLI tool(s?)", "command-line tool$1"],
["he or she", "they"],
["he/she", "they"],
["\\(s\\)he", "they"],
["repo\\b", "repository"],
["smartphone(s?)", "mobile phone$1"],
["web[- ]?site(s?)", "site$1"],
["auto[- ]complete", "autocomplete"],
["auto[- ]format", "autoformat"],
["auto[- ]fix", "autofix"],
["auto[- ]fixing", "autofixing"],
["back[- ]end(\\w*)", "backend$1"],
["bug[- ]fix(es?)", "bugfix$1"],
["change[- ]log(s?)", "changelog$1"],
["check[- ]box(es?)", "checkbox$1"],
["code[- ]base(es?)", "codebase$1"],
["co[- ]locate(d?)", "colocate$1"],
["end[- ]point(s?)", "endpoint$1"],
["e[- ]mail(s?)", "email$1"],
["file[- ]name(s?)", "filename$1"],
["front[- ]end(\\w*)", "frontend$1"],
["hack[- ]a[- ]thon(s?)", "hackathon$1"],
["host[- ]name(s?)", "hostname$1"],
["hot[- ]key(s?)", "hotkey$1"],
["life[- ]cycle", "lifecycle"],
["life[- ]stream(s?)", "lifestream$1"],
["lock[- ]file(s?)", "lockfile$1"],
["mark-up", "markup"],
["meta[- ]data", "metadata"],
["micro[- ]service(s?)", "microservice$1"],
["name[- ]space(s?)", "namespace$1"],
["pre[- ]condition(s?)", "precondition$1"],
["pre[- ]defined", "predefined"],
["pre[- ]release(s?)", "prerelease$1"],
["re[- ]write", "rewrite"],
["run[- ]time", "runtime"],
["screen[- ]shot(s?)", "screenshot$1"],
["screen[- ]?snap(s?)", "screenshot$1"],
["sub[- ]class((?:es|ing)?)", "subclass$1"],
["sub[- ]tree(s?)", "subtree$1"],
["time[- ]stamp(s?)", "timestamp$1"],
["touch[- ]screen(s?)", "touchscreen$1"],
["user[- ]name(s?)", "username$1"],
["walk[- ]through", "walkthrough"],
["white[- ]space", "whitespace"],
["wild[- ]card(s?)", "wildcard$1"],
["css-?in-?js", "CSS in JS"],
["code-?review(s?)", "code review$1"],
["code-?splitting", "code splitting"],
["end-?user(s?)", "end user$1"],
["file-?type(s?)", "file type$1"],
["micro-?frontend(s?)", "micro frontend$1"],
["open-?source(ed?)", "open source$1"],
["regexp?(s?)", "regular expression$1"],
["style-?guide(s?)", "style guide$1"],
["tree-?shaking", "tree shaking"],
["source-?map(s?)", "source map$1"],
["style-?sheet(s?)", "style sheet$1"],
["user-?base", "user base"],
["web-?page(s?)", "web page$1"],
["built ?in", "built-in"],
["client ?side", "client-side"],
["command ?line", "command-line"],
["end ?to ?end", "end-to-end"],
["error ?prone", "error-prone"],
["higher ?order", "higher-order"],
["key[/ ]?value", "key-value"],
["server ?side", "server-side"],
["two ?steps?", "two-step"],
["2 ?steps?", "two-step"],
["(\\w+[^.?!]\\)? )base64", "$1base64"],
["(\\w+[^.?!]\\)? )internet", "$1internet"],
["(\\w+[^.?!]\\)? )stylelint", "$1stylelint"],
["(\\w+[^.?!]\\)? )webpack", "$1webpack"],
["(\\w+[^.?!]\\)? )npm", "$1npm"],
["environemnt(s?)", "environment$1"],
["pacakge(s?)", "package$1"],
["tilda", "tilde"],
["falsey", "falsy"]
]
}
}
}

15
.yamllint Normal file
View File

@ -0,0 +1,15 @@
---
yaml-files:
- "*.yaml"
- "*.yml"
- ".yamllint"
ignore:
- node_modules
extends: default
rules:
line-length:
max: 150
level: warning

86
Makefile Normal file
View File

@ -0,0 +1,86 @@
.DEFAULT_GOAL:= lint
PATH := ./node_modules/.bin:$(PATH)
SHELL := /bin/bash
args = $(filter-out $@, $(MAKECMDGOALS))
.PHONY: all setup install clean reinstall build compile pdfs lint lint-sh lint-shell lint-md lint-markdown lint-txt lint-text pdf lint-yaml lint-yml lint-editorconfig lint-ec ci-lint ci-lint-shell ci-lint-markdown ci-lint-text ci-lint-yaml ci-lint-editorconfig lint-ansible ci-lint-ansible
default: all
all: install
####################################################################
# Installation / Setup #
####################################################################
setup:
@./tools/setup.sh
install:
yarn install
pipenv install
# remove the build and log folders
clean:
rm -rf build node_modules
# reinstall the node_modules and start with a fresh node build
reinstall: clean install
####################################################################
# Linting #
####################################################################
lint: lint-shell lint-markdown lint-text lint-yaml lint-editorconfig lint-ansible
# Note "|| true" is added to locally make lint can be ran and all linting is preformed, regardless of exit code
# Shell Linting
lint-sh lint-shell:
@./tools/lint-shell.sh || true
# Markdown Linting
lint-md lint-markdown:
@./tools/lint-markdown.sh || true
# Text Linting
lint-txt lint-text:
@./tools/lint-text.sh || true
# Yaml Linting
lint-yml lint-yaml:
@./tools/lint-yaml.sh || true
# Editorconfig Linting
lint-ec lint-editorconfig:
@./tools/lint-editorconfig.sh || true
# Ansible Linting
lint-ansible:
@./tools/lint-ansible.sh || true
####################################################################
# CI #
####################################################################
ci-lint: ci-lint-shell ci-lint-markdown ci-lint-text ci-lint-yaml ci-lint-editorconfig ci-lint-ansible
# Shell Linting
ci-lint-shell:
@./tools/lint-shell.sh
# Markdown Linting
ci-lint-markdown:
@./tools/lint-markdown.sh
# Text Linting
ci-lint-text:
@./tools/lint-text.sh
# Yaml Linting
ci-lint-yaml:
@./tools/lint-yaml.sh
# Editorconfig Linting
ci-lint-editorconfig:
@./tools/lint-editorconfig.sh
# Ansible Linting
ci-lint-ansible:
@./tools/lint-ansible.sh

14
Pipfile Normal file
View File

@ -0,0 +1,14 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
yamllint = "==1.29.0"
ansible-lint = ">=6.13.1,<7.0.0"
pylint = ">=2.16.2,<3.0.0"
[dev-packages]
[requires]
python_version = "3.10"

694
Pipfile.lock generated Normal file
View File

@ -0,0 +1,694 @@
{
"_meta": {
"hash": {
"sha256": "88b58b059da943aad9524cfadbbc8df4ef7c20bded8b83ab4c671741e4657a5d"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.10"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"ansible-core": {
"hashes": [
"sha256:093a4bc4a1259eaeb56ea37ec1d33cf1836c88f39281d89197f8d3480e068a58",
"sha256:a4b36bfdd7aa3534d449e9ae6a9fd82d8e513fda8caaf5d18e1e27d217151c8c"
],
"markers": "python_version >= '3.9'",
"version": "==2.14.3"
},
"ansible-lint": {
"hashes": [
"sha256:419b1a2c8523fbe0c0e8a6c51a5c41a692badb5a530e880a9050ac3a69c3fde3",
"sha256:435c12b4fd88da815af6821f3bf8b04ebb651811da89a11c9d190baff21badaa"
],
"index": "pypi",
"version": "==6.13.1"
},
"astroid": {
"hashes": [
"sha256:0e0e3709d64fbffd3037e4ff403580550f14471fd3eaae9fa11cc9a5c7901153",
"sha256:a3cf9f02c53dd259144a7e8f3ccd75d67c9a8c716ef183e0c1f291bc5d7bb3cf"
],
"markers": "python_full_version >= '3.7.2'",
"version": "==2.14.2"
},
"attrs": {
"hashes": [
"sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836",
"sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"
],
"markers": "python_version >= '3.6'",
"version": "==22.2.0"
},
"black": {
"hashes": [
"sha256:0052dba51dec07ed029ed61b18183942043e00008ec65d5028814afaab9a22fd",
"sha256:0680d4380db3719ebcfb2613f34e86c8e6d15ffeabcf8ec59355c5e7b85bb555",
"sha256:121ca7f10b4a01fd99951234abdbd97728e1240be89fde18480ffac16503d481",
"sha256:162e37d49e93bd6eb6f1afc3e17a3d23a823042530c37c3c42eeeaf026f38468",
"sha256:2a951cc83ab535d248c89f300eccbd625e80ab880fbcfb5ac8afb5f01a258ac9",
"sha256:2bf649fda611c8550ca9d7592b69f0637218c2369b7744694c5e4902873b2f3a",
"sha256:382998821f58e5c8238d3166c492139573325287820963d2f7de4d518bd76958",
"sha256:49f7b39e30f326a34b5c9a4213213a6b221d7ae9d58ec70df1c4a307cf2a1580",
"sha256:57c18c5165c1dbe291d5306e53fb3988122890e57bd9b3dcb75f967f13411a26",
"sha256:7a0f701d314cfa0896b9001df70a530eb2472babb76086344e688829efd97d32",
"sha256:8178318cb74f98bc571eef19068f6ab5613b3e59d4f47771582f04e175570ed8",
"sha256:8b70eb40a78dfac24842458476135f9b99ab952dd3f2dab738c1881a9b38b753",
"sha256:9880d7d419bb7e709b37e28deb5e68a49227713b623c72b2b931028ea65f619b",
"sha256:9afd3f493666a0cd8f8df9a0200c6359ac53940cbde049dcb1a7eb6ee2dd7074",
"sha256:a29650759a6a0944e7cca036674655c2f0f63806ddecc45ed40b7b8aa314b651",
"sha256:a436e7881d33acaf2536c46a454bb964a50eff59b21b51c6ccf5a40601fbef24",
"sha256:a59db0a2094d2259c554676403fa2fac3473ccf1354c1c63eccf7ae65aac8ab6",
"sha256:a8471939da5e824b891b25751955be52ee7f8a30a916d570a5ba8e0f2eb2ecad",
"sha256:b0bd97bea8903f5a2ba7219257a44e3f1f9d00073d6cc1add68f0beec69692ac",
"sha256:b6a92a41ee34b883b359998f0c8e6eb8e99803aa8bf3123bf2b2e6fec505a221",
"sha256:bb460c8561c8c1bec7824ecbc3ce085eb50005883a6203dcfb0122e95797ee06",
"sha256:bfffba28dc52a58f04492181392ee380e95262af14ee01d4bc7bb1b1c6ca8d27",
"sha256:c1c476bc7b7d021321e7d93dc2cbd78ce103b84d5a4cf97ed535fbc0d6660648",
"sha256:c91dfc2c2a4e50df0026f88d2215e166616e0c80e86004d0003ece0488db2739",
"sha256:e6663f91b6feca5d06f2ccd49a10f254f9298cc1f7f49c46e498a0771b507104"
],
"markers": "python_version >= '3.7'",
"version": "==23.1.0"
},
"bracex": {
"hashes": [
"sha256:351b7f20d56fb9ea91f9b9e9e7664db466eb234188c175fd943f8f755c807e73",
"sha256:e7b23fc8b2cd06d3dec0692baabecb249dda94e06a617901ff03a6c56fd71693"
],
"markers": "python_version >= '3.7'",
"version": "==2.3.post1"
},
"cffi": {
"hashes": [
"sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5",
"sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef",
"sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104",
"sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426",
"sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405",
"sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375",
"sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a",
"sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e",
"sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc",
"sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf",
"sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185",
"sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497",
"sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3",
"sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35",
"sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c",
"sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83",
"sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21",
"sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca",
"sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984",
"sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac",
"sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd",
"sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee",
"sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a",
"sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2",
"sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192",
"sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7",
"sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585",
"sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f",
"sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e",
"sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27",
"sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b",
"sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e",
"sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e",
"sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d",
"sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c",
"sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415",
"sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82",
"sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02",
"sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314",
"sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325",
"sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c",
"sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3",
"sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914",
"sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045",
"sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d",
"sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9",
"sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5",
"sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2",
"sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c",
"sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3",
"sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2",
"sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8",
"sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d",
"sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d",
"sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9",
"sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162",
"sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76",
"sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4",
"sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e",
"sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9",
"sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6",
"sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b",
"sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01",
"sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"
],
"version": "==1.15.1"
},
"click": {
"hashes": [
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
],
"markers": "python_version >= '3.7'",
"version": "==8.1.3"
},
"cryptography": {
"hashes": [
"sha256:23df8ca3f24699167daf3e23e51f7ba7334d504af63a94af468f468b975b7dd7",
"sha256:2725672bb53bb92dc7b4150d233cd4b8c59615cd8288d495eaa86db00d4e5c06",
"sha256:30b1d1bfd00f6fc80d11300a29f1d8ab2b8d9febb6ed4a38a76880ec564fae84",
"sha256:35d658536b0a4117c885728d1a7032bdc9a5974722ae298d6c533755a6ee3915",
"sha256:5f8c682e736513db7d04349b4f6693690170f95aac449c56f97415c6980edef5",
"sha256:6236a9610c912b129610eb1a274bdc1350b5df834d124fa84729ebeaf7da42c3",
"sha256:788b3921d763ee35dfdb04248d0e3de11e3ca8eb22e2e48fef880c42e1f3c8f9",
"sha256:8bc0008ef798231fac03fe7d26e82d601d15bd16f3afaad1c6113771566570f3",
"sha256:8f35c17bd4faed2bc7797d2a66cbb4f986242ce2e30340ab832e5d99ae60e011",
"sha256:b49a88ff802e1993b7f749b1eeb31134f03c8d5c956e3c125c75558955cda536",
"sha256:bc0521cce2c1d541634b19f3ac661d7a64f9555135e9d8af3980965be717fd4a",
"sha256:bc5b871e977c8ee5a1bbc42fa8d19bcc08baf0c51cbf1586b0e87a2694dde42f",
"sha256:c43ac224aabcbf83a947eeb8b17eaf1547bce3767ee2d70093b461f31729a480",
"sha256:d15809e0dbdad486f4ad0979753518f47980020b7a34e9fc56e8be4f60702fac",
"sha256:d7d84a512a59f4412ca8549b01f94be4161c94efc598bf09d027d67826beddc0",
"sha256:e029b844c21116564b8b61216befabca4b500e6816fa9f0ba49527653cae2108",
"sha256:e8a0772016feeb106efd28d4a328e77dc2edae84dfbac06061319fdb669ff828",
"sha256:e944fe07b6f229f4c1a06a7ef906a19652bdd9fd54c761b0ff87e83ae7a30354",
"sha256:eb40fe69cfc6f5cdab9a5ebd022131ba21453cf7b8a7fd3631f45bbf52bed612",
"sha256:fa507318e427169ade4e9eccef39e9011cdc19534f55ca2f36ec3f388c1f70f3",
"sha256:ffd394c7896ed7821a6d13b24657c6a34b6e2650bd84ae063cf11ccffa4f1a97"
],
"markers": "python_version >= '3.6'",
"version": "==39.0.2"
},
"dill": {
"hashes": [
"sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0",
"sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"
],
"markers": "python_version < '3.11'",
"version": "==0.3.6"
},
"filelock": {
"hashes": [
"sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de",
"sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"
],
"markers": "python_version >= '3.7'",
"version": "==3.9.0"
},
"isort": {
"hashes": [
"sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504",
"sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"
],
"markers": "python_full_version >= '3.8.0'",
"version": "==5.12.0"
},
"jinja2": {
"hashes": [
"sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
"sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
],
"markers": "python_version >= '3.7'",
"version": "==3.1.2"
},
"jsonschema": {
"hashes": [
"sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d",
"sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"
],
"markers": "python_version >= '3.7'",
"version": "==4.17.3"
},
"lazy-object-proxy": {
"hashes": [
"sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382",
"sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82",
"sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9",
"sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494",
"sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46",
"sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30",
"sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63",
"sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4",
"sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae",
"sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be",
"sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701",
"sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd",
"sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006",
"sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a",
"sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586",
"sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8",
"sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821",
"sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07",
"sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b",
"sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171",
"sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b",
"sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2",
"sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7",
"sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4",
"sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8",
"sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e",
"sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f",
"sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda",
"sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4",
"sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e",
"sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671",
"sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11",
"sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455",
"sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734",
"sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb",
"sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"
],
"markers": "python_version >= '3.7'",
"version": "==1.9.0"
},
"markdown-it-py": {
"hashes": [
"sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30",
"sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"
],
"markers": "python_version >= '3.7'",
"version": "==2.2.0"
},
"markupsafe": {
"hashes": [
"sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed",
"sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc",
"sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2",
"sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460",
"sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7",
"sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0",
"sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1",
"sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa",
"sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03",
"sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323",
"sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65",
"sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013",
"sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036",
"sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f",
"sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4",
"sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419",
"sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2",
"sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619",
"sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a",
"sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a",
"sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd",
"sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7",
"sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666",
"sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65",
"sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859",
"sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625",
"sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff",
"sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156",
"sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd",
"sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba",
"sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f",
"sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1",
"sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094",
"sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a",
"sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513",
"sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed",
"sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d",
"sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3",
"sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147",
"sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c",
"sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603",
"sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601",
"sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a",
"sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1",
"sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d",
"sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3",
"sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54",
"sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2",
"sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6",
"sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"
],
"markers": "python_version >= '3.7'",
"version": "==2.1.2"
},
"mccabe": {
"hashes": [
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
],
"markers": "python_version >= '3.6'",
"version": "==0.7.0"
},
"mdurl": {
"hashes": [
"sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8",
"sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"
],
"markers": "python_version >= '3.7'",
"version": "==0.1.2"
},
"mypy-extensions": {
"hashes": [
"sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d",
"sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"
],
"markers": "python_version >= '3.5'",
"version": "==1.0.0"
},
"packaging": {
"hashes": [
"sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2",
"sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"
],
"markers": "python_version >= '3.7'",
"version": "==23.0"
},
"pathspec": {
"hashes": [
"sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229",
"sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"
],
"markers": "python_version >= '3.7'",
"version": "==0.11.0"
},
"platformdirs": {
"hashes": [
"sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9",
"sha256:b1d5eb14f221506f50d6604a561f4c5786d9e80355219694a1b244bcd96f4567"
],
"markers": "python_version >= '3.7'",
"version": "==3.0.0"
},
"pycparser": {
"hashes": [
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
],
"version": "==2.21"
},
"pygments": {
"hashes": [
"sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297",
"sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"
],
"markers": "python_version >= '3.6'",
"version": "==2.14.0"
},
"pylint": {
"hashes": [
"sha256:13b2c805a404a9bf57d002cd5f054ca4d40b0b87542bdaba5e05321ae8262c84",
"sha256:ff22dde9c2128cd257c145cfd51adeff0be7df4d80d669055f24a962b351bbe4"
],
"index": "pypi",
"version": "==2.16.2"
},
"pyrsistent": {
"hashes": [
"sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8",
"sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440",
"sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a",
"sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c",
"sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3",
"sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393",
"sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9",
"sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da",
"sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf",
"sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64",
"sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a",
"sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3",
"sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98",
"sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2",
"sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8",
"sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf",
"sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc",
"sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7",
"sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28",
"sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2",
"sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b",
"sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a",
"sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64",
"sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19",
"sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1",
"sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9",
"sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"
],
"markers": "python_version >= '3.7'",
"version": "==0.19.3"
},
"pyyaml": {
"hashes": [
"sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf",
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
"sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
"sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
"sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
"sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
"sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
"sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
"sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
"sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
"sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
"sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
"sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782",
"sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
"sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
"sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
"sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
"sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
"sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1",
"sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
"sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
"sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
"sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
"sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
"sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
"sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d",
"sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
"sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
"sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7",
"sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
"sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
"sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
"sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358",
"sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
"sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
"sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
"sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
"sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f",
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
],
"markers": "python_version >= '3.6'",
"version": "==6.0"
},
"resolvelib": {
"hashes": [
"sha256:c6ea56732e9fb6fca1b2acc2ccc68a0b6b8c566d8f3e78e0443310ede61dbd37",
"sha256:d9b7907f055c3b3a2cfc56c914ffd940122915826ff5fb5b1de0c99778f4de98"
],
"version": "==0.8.1"
},
"rich": {
"hashes": [
"sha256:125d96d20c92b946b983d0d392b84ff945461e5a06d3867e9f9e575f8697b67f",
"sha256:8aa57747f3fc3e977684f0176a88e789be314a99f99b43b75d1e9cb5dc6db9e9"
],
"markers": "python_full_version >= '3.7.0'",
"version": "==13.3.1"
},
"ruamel.yaml": {
"hashes": [
"sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7",
"sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"
],
"markers": "python_version >= '3'",
"version": "==0.17.21"
},
"ruamel.yaml.clib": {
"hashes": [
"sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e",
"sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3",
"sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5",
"sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497",
"sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f",
"sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac",
"sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697",
"sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763",
"sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282",
"sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94",
"sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1",
"sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072",
"sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9",
"sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5",
"sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231",
"sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93",
"sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b",
"sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb",
"sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f",
"sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307",
"sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8",
"sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b",
"sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b",
"sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640",
"sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7",
"sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a",
"sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71",
"sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8",
"sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122",
"sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7",
"sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80",
"sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e",
"sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab",
"sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0",
"sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646",
"sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"
],
"markers": "python_version < '3.11' and platform_python_implementation == 'CPython'",
"version": "==0.2.7"
},
"setuptools": {
"hashes": [
"sha256:e5fd0a713141a4a105412233c63dc4e17ba0090c8e8334594ac790ec97792330",
"sha256:f106dee1b506dee5102cc3f3e9e68137bbad6d47b616be7991714b0c62204251"
],
"markers": "python_version >= '3.7'",
"version": "==67.4.0"
},
"subprocess-tee": {
"hashes": [
"sha256:b3c124993f8b88d1eb1c2fde0bc2069787eac720ba88771cba17e8c93324825d",
"sha256:eca56973a1c1237093c2055b2731bcaab784683b83f22c76f26e4c5763402e28"
],
"markers": "python_version >= '3.8'",
"version": "==0.4.1"
},
"tomli": {
"hashes": [
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
],
"markers": "python_version < '3.11'",
"version": "==2.0.1"
},
"tomlkit": {
"hashes": [
"sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b",
"sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"
],
"markers": "python_version >= '3.6'",
"version": "==0.11.6"
},
"typing-extensions": {
"hashes": [
"sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb",
"sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"
],
"markers": "python_version < '3.11'",
"version": "==4.5.0"
},
"wcmatch": {
"hashes": [
"sha256:3476cd107aba7b25ba1d59406938a47dc7eec6cfd0ad09ff77193f21a964dee7",
"sha256:b1f042a899ea4c458b7321da1b5e3331e3e0ec781583434de1301946ceadb943"
],
"markers": "python_version >= '3.7'",
"version": "==8.4.1"
},
"wrapt": {
"hashes": [
"sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0",
"sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420",
"sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a",
"sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c",
"sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079",
"sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923",
"sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f",
"sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1",
"sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8",
"sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86",
"sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0",
"sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364",
"sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e",
"sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c",
"sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e",
"sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c",
"sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727",
"sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff",
"sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e",
"sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29",
"sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7",
"sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72",
"sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475",
"sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a",
"sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317",
"sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2",
"sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd",
"sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640",
"sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98",
"sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248",
"sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e",
"sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d",
"sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec",
"sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1",
"sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e",
"sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9",
"sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92",
"sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb",
"sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094",
"sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46",
"sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29",
"sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd",
"sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705",
"sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8",
"sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975",
"sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb",
"sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e",
"sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b",
"sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418",
"sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019",
"sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1",
"sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba",
"sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6",
"sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2",
"sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3",
"sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7",
"sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752",
"sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416",
"sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f",
"sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1",
"sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc",
"sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145",
"sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee",
"sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a",
"sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7",
"sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b",
"sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653",
"sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0",
"sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90",
"sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29",
"sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6",
"sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034",
"sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09",
"sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559",
"sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"
],
"markers": "python_version < '3.11'",
"version": "==1.15.0"
},
"yamllint": {
"hashes": [
"sha256:5153bf9f8205aa9dc6af6217e38bd4f5baf09d9a7c6f4ae1e23f90d9c00c49c5",
"sha256:66a755d5fbcbb8831f1a9568676329b5bac82c37995bcc9afd048b6459f9fa48"
],
"index": "pypi",
"version": "==1.29.0"
}
},
"develop": {}
}

View File

@ -1,22 +1,28 @@
# Ansible Collection for Grafana Cloud # Ansible Collection for Grafana Cloud
[![Grafana](https://img.shields.io/badge/grafana-%23F46800.svg?&logo=grafana&logoColor=white)](https://grafana.com)
[![Ansible Collection](https://img.shields.io/ansible/collection/1935?color=orange&style=flat-square)](https://galaxy.ansible.com/grafana/grafana)
[![GitHub tag](https://img.shields.io/github/tag/grafana/grafana-ansible-collection.svg)](https://github.com/grafana/grafana-ansible-collection/tags)
[![GitHub Last Commit](https://img.shields.io/github/last-commit/grafana/grafana-ansible-collection)](https://github.com/grafana/grafana-ansible-collection/tags)
[![GitHub Contributors](https://img.shields.io/github/contributors/grafana/grafana-ansible-collection)](https://github.com/grafana/grafana-ansible-collection/tags)
[![Lint](https://github.com/grafana/grafana-ansible-collection/actions/workflows/lint.yaml/badge.svg)](https://github.com/grafana/grafana-ansible-collection/actions/workflows/lint.yaml)
[![CI Tests](https://github.com/grafana/grafana-ansible-collection/actions/workflows/ci-test.yml/badge.svg)](https://github.com/grafana/grafana-ansible-collection/actions/workflows/ci-test.yml) [![CI Tests](https://github.com/grafana/grafana-ansible-collection/actions/workflows/ci-test.yml/badge.svg)](https://github.com/grafana/grafana-ansible-collection/actions/workflows/ci-test.yml)
[![Full Integration Test](https://github.com/grafana/grafana-ansible-collection/actions/workflows/full-integration-test.yml/badge.svg?branch=main)](https://github.com/grafana/grafana-ansible-collection/actions/workflows/full-integration-test.yml) [![Full Integration Test](https://github.com/grafana/grafana-ansible-collection/actions/workflows/full-integration-test.yml/badge.svg?branch=main)](https://github.com/grafana/grafana-ansible-collection/actions/workflows/full-integration-test.yml)
This collection (grafana.grafana) contains modules and plugins to assist in automating managing of resources in <b>Grafana Cloud</b> with Ansible. This collection (`grafana.grafana`) contains modules and plugins to assist in automating managing of resources in **Grafana Cloud** with Ansible.
- [Ansible collection Documentation](https://grafana.github.io/grafana-ansible-collection/) - [Ansible collection Documentation](https://grafana.github.io/grafana-ansible-collection/)
- [Grafana website](https://grafana.com) - [Grafana Site](https://grafana.com)
- [Grafana Cloud website](https://grafana.com/products/cloud/) - [Grafana Cloud Site](https://grafana.com/products/cloud/)
## Ansible version compatibility ## Ansible version compatibility
The collection is tested and supported with:
* ansible >= 2.9 The collection is tested and supported with: `ansible >= 2.9`
## Installing the collection ## Installing the collection
Before using the Grafana collection, you need to install it using the below commoand: Before using the Grafana collection, you need to install it using the below command:
```shell ```shell
ansible-galaxy collection install grafana.grafana ansible-galaxy collection install grafana.grafana
@ -29,6 +35,7 @@ You can also include it in a `requirements.yml` file and install it via ansible-
collections: collections:
- name: grafana.grafana - name: grafana.grafana
``` ```
A specific version of the collection can be installed by using the version keyword in the `requirements.yml` file: A specific version of the collection can be installed by using the version keyword in the `requirements.yml` file:
```yaml ```yaml
@ -37,9 +44,11 @@ collections:
- name: grafana.grafana - name: grafana.grafana
version: 1.0.0 version: 1.0.0
``` ```
## Using this collection ## Using this collection
You can call modules by their Fully Qualified Collection Namespace (FQCN), such as `grafana.grafana.cloud_stack`: You can call modules by their Fully Qualified Collection Namespace (FQCN), such as `grafana.grafana.cloud_stack`:
```yaml ```yaml
- name: Using grafana collection - name: Using grafana collection
hosts: localhost hosts: localhost
@ -55,6 +64,7 @@ You can call modules by their Fully Qualified Collection Namespace (FQCN), such
``` ```
or you can add full namespace and collection name in the `collections` element in your playbook or you can add full namespace and collection name in the `collections` element in your playbook
```yaml ```yaml
- name: Using grafana collection - name: Using grafana collection
hosts: localhost hosts: localhost
@ -72,11 +82,12 @@ or you can add full namespace and collection name in the `collections` element i
``` ```
## Contributing ## Contributing
We are accepting Github pull requests and issues. There are many ways in which you can participate in the project, for example:
* Submit bugs and feature requests, and help us verify them We are accepting GitHub pull requests and issues. There are many ways in which you can participate in the project, for example:
* Submit and review source code changes in Github pull requests
* Add new modules for more Grafana resources - Submit bugs and feature requests, and help us verify them
- Submit and review source code changes in GitHub pull requests
- Add new modules for more Grafana resources
## Testing and Development ## Testing and Development
@ -89,24 +100,45 @@ and work on it there.
We use `ansible-test` for sanity. We use `ansible-test` for sanity.
## Commands
| Command | Description |
| :--- | :----------- |
| `make setup` | Checks to see if necessary tools are installed |
| `make install` | Installs project dependencies |
| `make lint` | Performs all linting commands |
| `make lint-sh` / `make lint-shell` | Performs shell script linting |
| `make lint-md` / `make lint-markdown` | Performs Markdown linting |
| `make lint-txt` / `make lint-text` | Performs text linting |
| `make lint-yml` / `make lint-yaml` | Performs Yaml linting |
| `make lint-ec` / `make lint-editorconfig` | Performs Editorconfig Checks |
| `make lint-ansible` | Performs Ansible linting |
| `make clean` | Removes the `./node_modules` and `./build` directories |
| `make reinstall` | Shortcut to `make clean` and `make install` |
## Releasing, Versioning and Deprecation ## Releasing, Versioning and Deprecation
This collection follows [Semantic Versioning](https://semver.org/). More details on versioning can be found [in the Ansible docs](https://docs.ansible.com/ansible/latest/dev_guide/developing_collections.html#collection-versions). This collection follows [Semantic Versioning](https://semver.org/). More details on versioning can be found [in the Ansible docs](https://docs.ansible.com/ansible/latest/dev_guide/developing_collections.html#collection-versions).
We plan to regularly release new minor or bugfix versions once new features or bugfixes have been implemented. We plan to regularly release new minor or bugfix versions once new features or bugfixes have been implemented.
Releasing the current major version on GitHub happens from the `main` branch by the [GitHub Release Workflow](https://github.com/grafana/grafana-ansible-collection/blob/main/.github/workflows/release.yml) Releasing the current major version on GitHub happens from the `main` branch by the
Before the [GitHub Release Workflow](https://github.com/grafana/grafana-ansible-collection/blob/main/.github/workflows/release.yml) is run, Contributors should push the new version on Ansible Galaxy Manually. [GitHub Release Workflow](https://github.com/grafana/grafana-ansible-collection/blob/main/.github/workflows/release.yml).
Before the [GitHub Release Workflow](https://github.com/grafana/grafana-ansible-collection/blob/main/.github/workflows/release.yml)
is run, Contributors should push the new version on Ansible Galaxy Manually.
We currently are not planning any deprecations or new major releases. The current landscape includes minor version updates for Module's documentation in 1.1.2. We currently are not planning any deprecations or new major releases. The current landscape includes minor version updates for
module's documentation in `1.1.2`.
## Code of Conduct ## Code of Conduct
This collection follows the Ansible project's [Code of Conduct](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html). Please read and familiarize yourself with this doc
This collection follows the Ansible project's [Code of Conduct](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html).
Please read and familiarize yourself with this doc
## More information ## More information
- [Maintainer guidelines](https://docs.ansible.com/ansible/devel/community/maintainers.html) - [Maintainer guidelines](https://docs.ansible.com/ansible/devel/community/maintainers.html)
- Subscribe to the [news-for-maintainers](https://github.com/ansible-collections/news-for-maintainers) repo and track announcements there. - Subscribe to the [news-for-maintainers](https://github.com/ansible-collections/news-for-maintainers) repository and track announcements there.
- [Ansible Collection overview](https://github.com/ansible-collections/overview) - [Ansible Collection overview](https://github.com/ansible-collections/overview)
- [Ansible User guide](https://docs.ansible.com/ansible/latest/user_guide/index.html) - [Ansible User guide](https://docs.ansible.com/ansible/latest/user_guide/index.html)
- [Ansible Developer guide](https://docs.ansible.com/ansible/latest/dev_guide/index.html) - [Ansible Developer guide](https://docs.ansible.com/ansible/latest/dev_guide/index.html)

2
ansible.cfg Normal file
View File

@ -0,0 +1,2 @@
[defaults]
collections_paths = ./

View File

@ -1,3 +1,4 @@
---
objects: objects:
role: {} role: {}
plugins: plugins:

View File

@ -1,3 +1,4 @@
---
ancestor: null ancestor: null
releases: releases:
0.0.1: 0.0.1:
@ -46,21 +47,19 @@ releases:
changes: changes:
minor_changes: minor_changes:
- Add a fail method to modules source code if `requests` library is not present - Add a fail method to modules source code if `requests` library is not present
- Fixed markup for arg option in Documenation - Fixed markup for arg option in Documentation
- Updated Documenation with `notes` to specify if the check_mode feature is - Updated Documentation with `notes` to specify if the check_mode feature is
supported by modules supported by modules
- removed `supports_check_mode=True` from source code of modules - removed `supports_check_mode=True` from source code of modules
release_date: '2022-10-20' release_date: '2022-10-20'
1.0.4: 1.0.4:
changes: changes:
bugfixes: bugfixes:
- Fixed cases where cloud_stack and alert_contact_point modules do not return - Fixed cases where cloud_stack and alert_contact_point modules do not return a tuple when nothing in loop matches
a tuple when nothing in loop matches
major_changes: major_changes:
- All modules except dashboard and datasource modules now support idempotency - All modules except dashboard and datasource modules now support idempotency
minor_changes: minor_changes:
- All modules use `missing_required_lib`` to compose the message for module.fail_json() - All modules use `missing_required_lib`` to compose the message for module.fail_json() when required library is missing from host
when required library is missing from host
release_summary: Bug fixes and idempotency fixes for modules release_summary: Bug fixes and idempotency fixes for modules
release_date: '2022-11-01' release_date: '2022-11-01'
1.0.5: 1.0.5:

View File

@ -1,3 +1,4 @@
---
changelog_filename_template: ../CHANGELOG.rst changelog_filename_template: ../CHANGELOG.rst
changelog_filename_version_depth: 0 changelog_filename_version_depth: 0
changes_file: changelog.yaml changes_file: changelog.yaml

View File

@ -0,0 +1,10 @@
---
- hosts: all
become: true
# pre_tasks happen before roles are executed / applied
pre_tasks: []
# roles are ran after pre_tasks
roles:
- grafana_agent
# tasks are ran after roles
tasks: []

View File

@ -0,0 +1,89 @@
---
- hosts: all
become: true
vars:
grafana_agent_metrics_config:
global:
external_labels:
datacenter: primary
cluster: my-cluster
instance: "{{ ansible_host }}"
remote_write:
- url: https://prometheus-<your region>.grafana.net/api/prom/push
basic_auth:
username: "1234567" # your username / instanceID
password: "..." # your grafana.com token
configs:
- name: local
scrape_configs:
# scrape a an application on the localhost
- job_name: my-app
metrics_path: /metrics
static_configs:
- targets:
- localhost:8080
relabel_configs: []
metric_relabel_configs: []
grafana_agent_logs_config:
global:
clients:
- url: https://logs-<your region>.grafana.net/loki/api/v1/push
basic_auth:
username: "1234567" # your username / instanceID
password: "..." # your grafana.com token
scrape_configs:
# scrape all of the log files in /var/log on the localhost
- job_name: log-files
static_configs:
- targets:
- localhost
labels:
job: var-logs
instance: "{{ ansible_host }}"
__path__: /var/log/*.log
# scrape all of the journal logs on localhost
- job_name: systemd-journal
journal:
max_age: 12h
labels:
job: systemd-journal
relabel_configs:
- source_labels:
- __journal__systemd_unit
target_label: systemd_unit
- source_labels:
- __journal__hostname
target_label: hostname
- source_labels:
- __journal_syslog_identifier
target_label: syslog_identifier
- source_labels:
- __journal__pid
target_label: pid
- source_labels:
- __journal__uid
target_label: uid
- source_labels:
- __journal__transport
target_label: transport
grafana_agent_integrations_config:
scrape_integrations: true
# get metrics about the agent
agent:
enabled: true
relabel_configs: []
metric_relabel_configs: []
# get node exporter metrics
node_exporter:
enabled: true
relabel_configs: []
metric_relabel_configs: []
# pre_tasks happen before roles are executed / applied
pre_tasks: []
# roles are ran after pre_tasks
roles:
- grafana_agent
# tasks are ran after roles
tasks: []

32
examples/ansible.cfg Normal file
View File

@ -0,0 +1,32 @@
[defaults]
# (string) Sets the macro for the 'ansible_managed' variable available for :ref:`ansible_collections.ansible.builtin.template_module` and :ref:`ansible_collections.ansible.windows.win_template_module`. This is only relevant for those two modules.
ansible_managed="Ansible managed file. Be wary of possible overwrites."
# (boolean) Toggle to control the showing of deprecation warnings
deprecation_warnings=False
# (boolean) Set this to "False" if you want to avoid host key checking by the underlying tools Ansible uses to connect to the host
host_key_checking=False
# (pathlist) Comma separated list of Ansible inventory sources
inventory=hosts
# (pathspec) Colon separated paths in which Ansible will search for Modules.
library=../plugins/modules
# (path) File to which Ansible will log on the controller. When empty logging is disabled.
log_path=./ansible.log
# (pathspec) Colon separated paths in which Ansible will search for Roles.
roles_path=../roles
[ssh_connection]
# ssh arguments to use
# Leaving off ControlPersist will result in poor performance, so use
# paramiko on older platforms rather than removing it
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
# if True, make ansible use scp if the connection type is ssh
# (default is sftp)
scp_if_ssh = True

View File

@ -1,3 +1,4 @@
---
namespace: grafana namespace: grafana
name: grafana name: grafana
version: 1.1.1 version: 1.1.1

View File

@ -1,2 +1,2 @@
--- ---
requires_ansible: '>=2.11' requires_ansible: ">=2.12.0,<3.0.0"

26
package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "grafana-ansible-collection",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/grafana/grafana-ansible-collection.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/grafana/grafana-ansible-collection/issues"
},
"homepage": "https://github.com/grafana/grafana-ansible-collection#readme",
"dependencies": {
"editorconfig-checker": "^5.0.1",
"markdownlint-cli2": "^0.6.0",
"textlint": "^12.5.1",
"textlint-rule-common-misspellings": "^1.0.1",
"textlint-rule-no-todo": "^2.0.1",
"textlint-rule-terminology": "^3.0.4"
}
}

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
yamllint==1.29.0
ansible-lint>=6.13.1, <7.0.0
pylint>=2.16.2,<3.0.0

View File

@ -1,54 +1,53 @@
Role Name # Role Name
=========
Ansible Role to deploy Grafana Agent on Linux hosts. Using this Role, Grafana Agent can be deployed on Ubunutu, Debian, CentOS and Fedora linux distributions Ansible Role to deploy Grafana Agent on Linux hosts. Using this Role, Grafana Agent can be deployed on RedHat, Ubuntu, Debian, CentOS
and Fedora linux distributions.
## Requirements
Requirements
------------
To use this role, You need a YAML file having the Grafana Agent configuration To use this role, You need a YAML file having the Grafana Agent configuration
Role Variables ## Role Variables
--------------
A description of the variables for this role. All variables which can be overridden are stored in [./defaults/main.yaml](./defaults/main.yaml) file as well as in table below.
| Variable | Required | Default | Choices | Comments | | Variable | Default | Description |
|-------------------------|----------|----------------------|--------------------------------------------------------------------------------------------------------------------|---------------------------------------------| | :------ | :------ | :--------- |
| install_unzip | no | true | true, false | This will install unzip on the Linux host | | `grafana_agent_version` | `latest` | version of the agent to install |
| update_package_cache | no | yes | yes, no | Force dnf/apt to check if cache is out of date and redownload if needed.| | `grafana_agent_install_dir` | `/opt/grafana-agent/bin` | directory to install the binary to |
| agent_version | no | 0.29.0 | 0.29.0, 0.28.1, 0.28.0, 0.27.1, 0.27.0 | Version of the Grafana agent to install| | `grafana_agent_binary` | `grafana-agent` | name to use for the binary |
| linux_architecture | no | linux-amd64 | linux-amd64, linux-arm64, linux-armv6, linux-armv7, linux-ppc64le | Type of linux architecture of the remote host| | `grafana_agent_config_dir` | `/etc/grafana-agent` | directory to store the configuration files in |
| agent_binary_location | no | /usr/local/bin | | Path where the agent binary will be copied to on the remote host| | `grafana_agent_config_filename` | `config.yaml` | name of the configuration file for the agent |
| agent_config_location | no | /etc/grafana | | Path where the agent configuration will be copied to on the remote host| | `grafana_agent_env_file` | `service.env` | name of the environment file loaded by the system unit file |
| agent_config_local_path | yes | agent-config.yml | | Path to the agent configuration file on local| | `grafana_agent_local_tmp_dir` | `/tmp/grafana-agent` | temporary directory to create on the controller/localhost where the archive will be downloaded to |
| systemd_service_state | no | restarted | reloaded, restarted, started, stopped | Operation performed on the systemd service| | `grafana_agent_data_dir` | `/var/lib/grafana-agent` | the data directory to create for the wal and positions |
| systemd_config | no | | | Configuration for grafana-agent systemd service| | `grafana_agent_wal_dir` | `"{{ grafana_agent_data_dir }}/data"` | wal directory to use, should be a sub-folder of grafana_agent_data_dir, will automatically be created when the agent starts |
| `grafana_agent_positions_dir` | `"{{ grafana_agent_data_dir }}/data"` | positions directory to use, should be a sub-folder of grafana_agent_data_dir, will automatically be created when the agent starts |
| `grafana_agent_mode` | `static` | mode to run Grafana Agent in. Can be "flow" or "static", [Flow Docs](https://grafana.com/docs/agent/latest/flow/) |
| `grafana_agent_user` | `grafana-agent` | os user to create for the agent to run as |
| `grafana_agent_user_group` | `grafana-agent` | os user group to create for the agent |
| `grafana_agent_user_shell` | `/usr/sbin/nologin` | the shell for the user |
| `grafana_agent_user_createhome` | `false` | whether or not to create a home directory for the user |
| `grafana_agent_local_binary_file` | `""` | full path to the local binary if already downloaded or built on the controller, this should only be set, if ansible is not downloading the binary and you have manually downloaded the binary |
| `grafana_agent_flags_extra` | see [./defaults/main.yaml](./defaults/main.yaml) | dictionary of additional command-line flags, run grafana-agent --help for a complete list. [Docs](https://grafana.com/docs/agent/latest/configuration/flags/) |
| `grafana_agent_env_vars` | `{}` | dictionary of key/pair values to write to the environment file that is loaded by the service, with the flag `--config.expand-env=true` any generated config files will support the expansion of environment variables at runtime by referencing ${ENVVAR}. be aware of boolean values, when output they will result in the proper-cased string "True" and "False" |
| `grafana_agent_provisioned_config_file` | `""` | path to a config file on the controller that will be used instead of the provided configs below if specified. |
| `grafana_agent_server_config` | see [./defaults/main.yaml](./defaults/main.yaml) | Configures the server of the Agent used to enable self-scraping, [Docs](https://grafana.com/docs/agent/latest/configuration/server-config/) |
| `grafana_agent_metrics_config` | see [./defaults/main.yaml](./defaults/main.yaml) | Configures metric collection, [Docs](https://grafana.com/docs/agent/latest/configuration/metrics-config/) |
| `grafana_agent_logs_config` | see [./defaults/main.yaml](./defaults/main.yaml) | Configures logs collection, [Docs](https://grafana.com/docs/agent/latest/configuration/logs-config/) |
| `grafana_agent_traces_config` | see [./defaults/main.yaml](./defaults/main.yaml) | Configures traces collection, [Docs](https://grafana.com/docs/agent/latest/configuration/traces-config/) |
| `grafana_agent_integrations_config` | see [./defaults/main.yaml](./defaults/main.yaml) | Configures integrations for the agent, [Docs](https://grafana.com/docs/agent/latest/configuration/integrations/) |
Example Playbook ## Example Playbooks
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: See [examples](../../examples)
```yaml ## License
- name: Install Grafana Agent
hosts: all
tasks:
- name: Install Grafana Agent
ansible.builtin.include_role:
name: grafana.grafana.grafana_agent:
vars:
agent_config_local_path: ../agent-config.yml
```
License
-------
See [LICENSE](https://github.com/grafana/grafana-ansible-collection/blob/main/LICENSE) See [LICENSE](https://github.com/grafana/grafana-ansible-collection/blob/main/LICENSE)
Author Information ## Author Information
------------------
- [Grafana Labs](https://github.com/grafana) - [Grafana Labs](https://github.com/grafana)
- [Ishan Jain](https://github.com/ishanjainn) - [Ishan Jain](https://github.com/ishanjainn)
- [Ishan Jain](https://github.com/bentonam)

View File

@ -0,0 +1,183 @@
---
# version of the agent to install
grafana_agent_version: latest
# directory to install the binary to
grafana_agent_install_dir: /opt/grafana-agent/bin
# name to use for the binary
grafana_agent_binary: grafana-agent
# directory to store the configuration files in
grafana_agent_config_dir: /etc/grafana-agent
# name of the configuration file for the agent
grafana_agent_config_filename: config.yaml
# name of the environment file loaded by the system unit file
grafana_agent_env_file: service.env
# temporary directory to create on the controller/localhost where the archive will be downloaded to
grafana_agent_local_tmp_dir: /tmp/grafana-agent
# data directory to create for the wal and positions
grafana_agent_data_dir: /var/lib/grafana-agent
# wal directory to use, should be a sub-folder of grafana_agent_data_dir, will automatically be created when the agent starts
grafana_agent_wal_dir: "{{ grafana_agent_data_dir }}/data"
# positions directory to use, should be a sub-folder of grafana_agent_data_dir, will automatically be created when the agent starts
grafana_agent_positions_dir: "{{ grafana_agent_data_dir }}/data"
# mode to run Grafana Agent in. Can be "flow" or "static".
# Docs: https://grafana.com/docs/agent/latest/flow/
grafana_agent_mode: static
# os user to create for the agent to run as
grafana_agent_user: grafana-agent
# os user group to create for the agent
grafana_agent_user_group: grafana-agent
# the shell for the user
grafana_agent_user_shell: /usr/sbin/nologin
# whether or not to create a home directory for the user
grafana_agent_user_createhome: false
# full path to the local binary if already downloaded or built on the controller
# this should only be set, if ansible is not downloading the binary and you have
# manually downloaded the binary
grafana_agent_local_binary_file: ""
# dictionary of additional command-line flags, run grafana-agent --help for a complete list
# Docs: https://grafana.com/docs/agent/latest/configuration/flags/
grafana_agent_flags_extra:
config.expand-env: 'true'
config.enable-read-api: 'false'
server.register-instrumentation: 'true'
server.http.address: 127.0.0.1:12345
server.grpc.address: 127.0.0.1:12346
# dictionary of key/pair values to write to the environment file that is loaded by the service, with the flag --config.expand-env=true
# any generated config files will support the expansion of environment variables at runtime by referencing ${ENVVAR}.
# be aware of boolean values, when output they will result in the proper-cased string "True" and "False"
grafana_agent_env_vars: {}
# path to a config file on the controller that will be used instead of the provided configs below if specified.
grafana_agent_provisioned_config_file: ""
#################################################################################################
# Configures the server of the Agent used to enable self-scraping #
#################################################################################################
# Docs: https://grafana.com/docs/agent/latest/configuration/server-config/
# the entire dictionary value for this object is copied to the server: block in the config file
grafana_agent_server_config:
# Log only messages with the given severity or above. Supported values [debug,
# info, warn, error]. This level affects logging for all Agent-level logs, not
# just the HTTP and gRPC server.
#
# Note that some integrations use their own loggers which ignore this
# setting.
log_level: info
#################################################################################################
# Configures metric collection #
#################################################################################################
# Docs: https://grafana.com/docs/agent/latest/configuration/metrics-config/
# the entire dictionary value for this object is copied to the metrics: block in the config file
grafana_agent_metrics_config:
# Configure values for all Prometheus instances
# Docs: https://grafana.com/docs/agent/latest/configuration/metrics-config/#global_config
global:
# How frequently should Prometheus instances scrape.
scrape_interval: 1m
# How long to wait before timing out a scrape from a target.
scrape_timeout: 10s
# A dictionary of key/pair static labels to add for all metrics.
external_labels: {}
# Default set of remote_write endpoints. If an instance doesn't define any
# remote_writes, it will use this list.
# Docs: https://prometheus.io/docs/prometheus/2.34/configuration/configuration/#remote_write
remote_write: []
# The list of Prometheus instances to launch with the agent.
# Docs: https://grafana.com/docs/agent/latest/configuration/metrics-config/#metrics_instance_config
configs: []
# - name: name-of-scrape-job
# # Docs: https://prometheus.io/docs/prometheus/2.34/configuration/configuration/#scrape_config
# scrape_configs: []
# # Optional list of remote_write targets, if not specified metrics.global.remote_write is used
# # Docs: https://prometheus.io/docs/prometheus/2.34/configuration/configuration/#remote_write
# remote_write: []
# Configure the directory used by instances to store their WAL.
#
# The Grafana Agent assumes that all folders within wal_directory are managed by
# the agent itself.
wal_directory: "{{ grafana_agent_wal_dir }}"
# Configures how long ago an abandoned (not associated with an instance) WAL
# may be written to before being eligible to be deleted
wal_cleanup_age: 12h
# Configures how often checks for abandoned WALs to be deleted are performed.
# A value of 0 disables periodic cleanup of abandoned WALs
wal_cleanup_period: 30m
#################################################################################################
# Configures logs collection #
#################################################################################################
# Docs: https://grafana.com/docs/agent/latest/configuration/logs-config/
# the entire dictionary value for this object is copied to the logs: block in the config file
grafana_agent_logs_config:
# Directory to store Loki Promtail positions files in. Positions files are
# required to read logs, and are used to store the last read offset of log
# sources. The positions files will be stored in
# <positions_directory>/<logs_instance_config.name>.yml.
#
# Optional only if every config has a positions.filename manually provided.
#
# This directory will be automatically created if it doesn't exist.
positions_directory: "{{ grafana_agent_positions_dir }}"
# Configure values for all Loki Promtail instances.
global:
# Docs: https://grafana.com/docs/agent/latest/configuration/logs-config/#logs_instance_config
clients: []
#################################################################################################
# Configures traces collection #
#################################################################################################
# Docs: https://grafana.com/docs/agent/latest/configuration/traces-config/
# the entire dictionary value for this object is copied to the traces: block in the config file
grafana_agent_traces_config:
# Docs: https://grafana.com/docs/agent/latest/configuration/traces-config/#traces_instance_config
configs: []
#################################################################################################
# Configures integrations for the agent #
#################################################################################################
# Docs: https://grafana.com/docs/agent/latest/configuration/integrations/
# the entire dictionary value for this object is copied to the integrations: block in the config file
grafana_agent_integrations_config:
# Automatically collect metrics from enabled integrations. If disabled,
# integrations will be run but not scraped and thus not remote_written. Metrics
# for integrations will be exposed at /integrations/<integration_key>/metrics
# and can be scraped by an external process.
scrape_integrations: true
# Controls the Agent integration
agent:
# Enables the Agent integration, allowing the Agent to automatically
# collect and send metrics about itself.
enabled: true
# Allows for relabeling labels on the target.
relabel_configs: []
# Relabel metrics coming from the integration, allowing to drop series
# from the integration that you don't care about.
metric_relabel_configs: []
# Controls the node_exporter integration
# Docs: https://grafana.com/docs/agent/latest/configuration/integrations/node-exporter-config/
node_exporter:
enabled: true

View File

@ -1,20 +0,0 @@
install_unzip: true
update_package_cache: yes
agent_version: 0.29.0
linux_architecture: linux-amd64
agent_binary_location: /usr/local/bin
agent_config_location: /etc/grafana
agent_config_local_path: agent-config.yml
systemd_service_state: restarted
systemd_config: |
[Unit]
Description=Grafana Agent
[Service]
User=grafana-agent
ExecStart={{ agent_binary_location }}/agent-{{ linux_architecture }} --config.file={{ agent_config_location }}/agent-config.yaml
Restart=always
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,65 @@
---
- name: Stop grafana-agent if installed
block:
# this will fail the verify first time of installation if local binary is used
- name: Ensure grafana-agent is stopped
ansible.builtin.systemd:
name: grafana-agent
enabled: true
state: stopped
rescue:
# make sure that the service isn't actually installed
- name: Get the list of services
ansible.builtin.service_facts:
- name: Verify that grafana-agent is not installed
ansible.builtin.assert:
that:
- _grafana_agent_systemd_unit not in services
# these tasks are ran in both install and configure, as directories could have changed
- name: Configure Directories
ansible.builtin.import_tasks: install/directories.yaml
- name: Create a symbolic link
ansible.builtin.file:
src: "{{ grafana_agent_install_dir }}/{{ grafana_agent_binary }}"
dest: "/usr/local/bin/{{ grafana_agent_binary }}"
owner: root
group: root
state: link
- name: Overwrite/Create Grafana Agent Service
ansible.builtin.template:
src: grafana-agent.service.j2
dest: "{{ _grafana_agent_systemd_dir }}/{{ _grafana_agent_systemd_unit }}"
owner: root
group: root
mode: 0644
- name: Create the Service Environment File
ansible.builtin.template:
src: EnvironmentFile.j2
dest: "{{ grafana_agent_config_dir }}/{{ grafana_agent_env_file }}"
owner: root
group: "{{ grafana_agent_user_group }}"
mode: 0640
- name: Create Grafana Agent Config
ansible.builtin.template:
src: config.yaml.j2
dest: "{{ grafana_agent_config_dir }}/{{ grafana_agent_config_filename }}"
force: true
owner: root
group: "{{ grafana_agent_user_group }}"
mode: 0640
when: grafana_agent_provisioned_config_file | length == 0
- name: Copy Grafana Agent Config
ansible.builtin.copy:
src: "{{ grafana_agent_provisioned_config_file }}"
dest: "{{ grafana_agent_config_dir }}/{{ grafana_agent_config_filename }}"
owner: root
group: "{{ grafana_agent_user_group }}"
mode: 0640
when: grafana_agent_provisioned_config_file | length > 0

View File

@ -0,0 +1,19 @@
---
# user and group creation
- name: Configure User Groups
ansible.builtin.import_tasks: install/user-group.yaml
# directory creation
- name: Configure Directories
ansible.builtin.import_tasks: install/directories.yaml
# download and install agent
- name: Download and Install Agent
ansible.builtin.import_tasks: install/download-install.yaml
when: (grafana_agent_local_binary_file is not defined) or (grafana_agent_local_binary_file | length == 0)
# local install of agent
- name: Local Install of Agent
ansible.builtin.import_tasks:
file: install/local-install.yaml
when: __grafana_agent_local_install

View File

@ -0,0 +1,26 @@
---
- name: Create Install Directories
block:
- name: Create Grafana Agent Install Directory
ansible.builtin.file:
path: "{{ grafana_agent_install_dir }}"
state: directory
owner: root
group: "{{ grafana_agent_user_group }}"
mode: 0770
- name: Create Grafana Agent Conf Directory
ansible.builtin.file:
path: "{{ grafana_agent_config_dir }}"
state: directory
owner: root
group: "{{ grafana_agent_user_group }}"
mode: 0770
- name: Create Grafana Agent Data Directory
ansible.builtin.file:
path: "{{ grafana_agent_data_dir }}"
state: directory
owner: root
group: "{{ grafana_agent_user_group }}"
mode: 0755

View File

@ -0,0 +1,45 @@
---
- name: Download Grafana Agent Binary to controller (localhost)
block:
- name: Create Grafana Agent temp Directory
become: false
ansible.builtin.file:
path: "{{ grafana_agent_local_tmp_dir }}"
state: directory
mode: 0751
delegate_to: localhost
check_mode: false
run_once: true
- name: Download Grafana Agent Archive to local folder
become: false
ansible.builtin.get_url:
url: "{{ _grafana_agent_download_url }}"
dest: "{{ grafana_agent_local_tmp_dir }}/grafana-agent.zip"
mode: 0664
register: _download_archive
until: _download_archive is succeeded
retries: 5
delay: 2
delegate_to: localhost
check_mode: false
- name: Extract grafana-agent.zip
become: false
ansible.builtin.unarchive:
src: "{{ grafana_agent_local_tmp_dir }}/grafana-agent.zip"
dest: "{{ grafana_agent_local_tmp_dir }}"
remote_src: false
delegate_to: localhost
- name: Set local path
ansible.builtin.set_fact:
__grafana_agent_local_binary_file: "{{ grafana_agent_local_tmp_dir }}/{{ grafana_agent_binary }}"
- name: Propagate downloaded binary
ansible.builtin.copy:
src: "{{ grafana_agent_local_tmp_dir }}/{{ _grafana_agent_download_binary_file }}"
dest: "{{ grafana_agent_install_dir }}/{{ grafana_agent_binary }}"
mode: 0755
owner: root
group: root

View File

@ -0,0 +1,10 @@
---
- name: Install from Local
block:
- name: "Propagate local binary {{ grafana_agent_local_binary_file }}"
ansible.builtin.copy:
src: "{{ grafana_agent_local_binary_file }}"
dest: "{{ grafana_agent_install_dir }}/{{ grafana_agent_binary }}"
mode: 0755
owner: root
group: root

View File

@ -0,0 +1,56 @@
---
- name: Grafana Agent Group Creation
block:
- name: "Check if the group exists ({{ grafana_agent_user_group }})"
ansible.builtin.getent:
database: group
key: "{{ grafana_agent_user_group }}"
fail_key: false
- name: Set whether not the user group exists
ansible.builtin.set_fact:
__grafana_agent_user_group_exists: "{{ ansible_facts.getent_group[grafana_agent_user_group] is not none }}"
- name: Add user group "{{ grafana_agent_user_group }}"
ansible.builtin.group:
name: "{{ grafana_agent_user_group }}"
system: true
state: present
when: not __grafana_agent_user_group_exists and grafana_agent_user_group != 'root'
- name: Grafana Agent User Group Exists
ansible.builtin.debug:
msg: |-
The user group \"{{ grafana_agent_user_group }}\" already exists and will not be modified,
if modifying permissions please perform a separate task
when: __grafana_agent_user_group_exists
- name: Grafana Agent User Creation
block:
- name: "Check if the user exists ({{ grafana_agent_user }})"
ansible.builtin.getent:
database: passwd
key: "{{ grafana_agent_user }}"
fail_key: false
- name: Set whether not the user exists
ansible.builtin.set_fact:
__grafana_agent_user_exists: "{{ ansible_facts.getent_passwd[grafana_agent_user] is not none }}"
- name: Add user "{{ grafana_agent_user }}"
ansible.builtin.user:
name: "{{ grafana_agent_user }}"
comment: "Grafana Agent Account"
groups:
- "{{ grafana_agent_user_group }}"
system: true
shell: "{{ grafana_agent_user_shell }}"
createhome: "{{ grafana_agent_user_createhome }}"
when: not __grafana_agent_user_exists and grafana_agent_user != 'root'
- name: Grafana Agent User Exists
ansible.builtin.debug:
msg: |-
The user \"{{ grafana_agent_user }}\" already exists and will not be modified,
if modifying permissions please perform a separate task
when: __grafana_agent_user_exists

View File

@ -0,0 +1,47 @@
---
- name: Preflight Tasks
ansible.builtin.include_tasks:
file: preflight.yaml
apply:
become: true
tags:
- grafana_agent_install
- grafana_agent_configure
- grafana_agent_run
tags:
- grafana_agent_install
- grafana_agent_configure
- grafana_agent_run
- name: Install Tasks
ansible.builtin.include_tasks:
file: install.yaml
apply:
become: true
tags:
- grafana_agent_install
tags:
- grafana_agent_install
when: __grafana_agent_do_install
- name: Configuration Tasks
ansible.builtin.include_tasks:
file: configure.yaml
apply:
become: true
tags:
- grafana_agent_configure
tags:
- grafana_agent_configure
- name: Ensure Grafana Agent is Started and Enabled on Boot
become: true
ansible.builtin.systemd:
daemon_reload: true
name: grafana-agent
enabled: true
state: started
tags:
- grafana_agent_install
- grafana_agent_configure
- grafana_agent_run

View File

@ -1,59 +0,0 @@
- name: Install unzip on Ubuntu/Debian
ansible.builtin.apt:
name: unzip
state: present
update_cache: "{{ update_package_cache }}"
when: install_unzip | bool and (ansible_distribution == "Ubuntu" or ansible_distribution == "Debian")
- name: Install unzip on Fedora/CentOS
ansible.builtin.dnf:
name: unzip
state: latest
update_cache: "{{ update_package_cache }}"
when: install_unzip | bool and (ansible_distribution == "Fedora" or ansible_distribution == "CentOS")
- name: Download Grafana Agent binary from GitHub
ansible.builtin.get_url:
url: "https://github.com/grafana/agent/releases/download/v{{ agent_version }}/agent-{{ linux_architecture }}.zip"
dest: "/tmp/agent-linux.zip"
mode: '0644'
- name: Unarchive the Grafana Agent binary
ansible.builtin.unarchive:
src: "/tmp/agent-linux.zip"
dest: "{{ agent_binary_location }}"
remote_src: yes
mode: '0755'
- name: Create directory for Grafana Agent Configuration file
ansible.builtin.file:
path: "{{ agent_config_location }}"
state: directory
mode: '0755'
- name: Create configuration file for Grafana Agent
ansible.builtin.copy:
src: "{{ agent_config_local_path }}"
dest: "{{ agent_config_location }}/agent-config.yaml"
- name: Add user 'grafana-agent'
ansible.builtin.user:
name: grafana-agent
create_home: no
shell: /bin/false
- name: Create service file for Grafana Agent
ansible.builtin.copy:
dest: "/etc/systemd/system/grafana-agent.service"
content: "{{ systemd_config }}"
- name: Start Grafana Agent service
ansible.builtin.systemd:
daemon_reload: yes
name: grafana-agent
enabled: yes
state: "{{ systemd_service_state }}"
- name: Checking grafana-agent service status
ansible.builtin.shell:
cmd: systemctl is-active grafana-agent

View File

@ -0,0 +1,21 @@
---
- name: Preflight Variable Checks
ansible.builtin.import_tasks: preflight/vars.yaml
- name: Systemd Variable Checks
ansible.builtin.import_tasks: preflight/systemd.yaml
- name: Install Variable Checks
ansible.builtin.import_tasks: preflight/install.yaml
- name: Download Variable Checks
ansible.builtin.import_tasks: preflight/download.yaml
- name: Set whether or not to do the install
ansible.builtin.set_fact:
__grafana_agent_do_install: >-
{{ not __grafana_agent_is_installed.stat.exists or __grafana_agent_installed_version is version_compare(grafana_agent_version, '<') }}
- name: Do Install
ansible.builtin.debug:
var: __grafana_agent_do_install

View File

@ -0,0 +1,31 @@
---
- name: Get Grafana Agent Version from Github
when: grafana_agent_version == 'latest' and not __grafana_agent_local_install
block:
- name: Get the latest published Grafana-Agent # noqa command-instead-of-module
ansible.builtin.shell: |
curl -s https://api.github.com/repos/{{ _grafana_agent_github_org }}/{{ _grafana_agent_github_repo }}/releases/latest \
| grep -m 1 tag_name \
| cut -d '"' -f 4 | cut -c 2-
changed_when: false
run_once: true
delegate_to: localhost
become: false
register: _grafana_agent_version_request
- name: Set the Grafana Agent Version
ansible.builtin.set_fact:
grafana_agent_version: "{{ _grafana_agent_version_request.stdout }}"
- name: Grafana Agent Version to Download
ansible.builtin.debug:
var: grafana_agent_version
- name: Set the Grafana Agent Download URL
ansible.builtin.set_fact:
_grafana_agent_download_url: |-
{{ _grafana_agent_base_download_url }}/v{{ grafana_agent_version }}/{{ _grafana_agent_download_archive_file }}
- name: Grafana Agent Download URL
ansible.builtin.debug:
var: _grafana_agent_download_url

View File

@ -0,0 +1,72 @@
---
- name: Default to non-local install
ansible.builtin.set_fact:
__grafana_agent_local_install: false
- name: Fail when grafana_agent_local_binary_file is defined but the file doesn't exist
when: grafana_agent_local_binary_file is defined and grafana_agent_local_binary_file | length > 0
block:
- name: Check if grafana_agent_local_binary_file exists
ansible.builtin.stat:
path: "{{ grafana_agent_local_binary_file }}"
register: __grafana_agent_local_binary_check
become: false
delegate_to: localhost
check_mode: false
- name: Fail when the grafana_agent_local_binary_file does not exist
ansible.builtin.fail:
msg: "The grafana_agent_local_binary_file ({{ grafana_agent_local_binary_file }}) was specified but does not exist"
when: not __grafana_agent_local_binary_check.stat.exists
- name: Change to local install
ansible.builtin.set_fact:
__grafana_agent_local_install: true
- name: Fail when grafana_agent_provisioned_config_file is defined but the file doesn't exist
when: grafana_agent_provisioned_config_file is defined and grafana_agent_provisioned_config_file | length > 0
block:
- name: Check if grafana_agent_provisioned_config_file exists
ansible.builtin.stat:
path: "{{ grafana_agent_provisioned_config_file }}"
register: __grafana_agent_provisioned_config_file_check
become: false
delegate_to: localhost
check_mode: false
- name: Fail when the grafana_agent_provisioned_config_file does not exist
ansible.builtin.fail:
msg: "The grafana_agent_provisioned_config_file ({{ grafana_agent_provisioned_config_file }}) was specified but does not exist"
when: not __grafana_agent_provisioned_config_file_check.stat.exists
- name: Check if grafana_agent is already installed on the host
ansible.builtin.stat:
path: "{{ grafana_agent_install_dir }}/{{ grafana_agent_binary }}"
register: __grafana_agent_is_installed
check_mode: false
- name: Is Grafana Agent already installed on the host
ansible.builtin.debug:
var: __grafana_agent_is_installed.stat.exists
- name: Install Checks
when: __grafana_agent_is_installed.stat.exists and not __grafana_agent_local_install
block:
- name: Gather currently installed grafana-agent version from the host
ansible.builtin.shell:
cmd: |
{{ grafana_agent_install_dir }}/{{ grafana_agent_binary }} --version | \
head -n 1 | \
awk '{ print $3; }' | \
cut -d 'v' -f 2
changed_when: false
register: __grafana_agent_current_version_output
check_mode: false
- name: Set Grafana Agent Installed Version for the host
ansible.builtin.set_fact:
__grafana_agent_installed_version: "{{ __grafana_agent_current_version_output.stdout }}"
- name: Grafana Agent Installed Version on host
ansible.builtin.debug:
var: __grafana_agent_installed_version

View File

@ -0,0 +1,28 @@
---
- name: Assert usage of systemd as an init system
ansible.builtin.assert:
that: ansible_service_mgr == 'systemd'
msg: This role only works with systemd
- name: Get systemd version # noqa command-instead-of-module
ansible.builtin.command: systemctl --version
changed_when: false
check_mode: false
register: __systemd_version
- name: Set systemd version fact
ansible.builtin.set_fact:
grafana_agent_systemd_version: "{{ __systemd_version.stdout_lines[0] | regex_replace('^systemd\\s(\\d+).*$', '\\1') }}"
- name: Fail when _grafana_agent_systemd_dir the directory doesn't exist
block:
- name: Check if _grafana_agent_systemd_dir exists
ansible.builtin.stat:
path: "{{ _grafana_agent_systemd_dir }}"
register: ___grafana_agent_systemd_dir_check
check_mode: false
- name: Fail when the _grafana_agent_systemd_dir directory does not exist
ansible.builtin.fail:
msg: "The _grafana_agent_systemd_dir ({{ _grafana_agent_systemd_dir }}) does not exist"
when: not ___grafana_agent_systemd_dir_check.stat.exists

View File

@ -0,0 +1,71 @@
---
# Performs initial variable validation
- name: Fail when variables are not defined
ansible.builtin.fail:
msg: "The {{ item }} property must be set"
when: ( vars[item] is not defined )
with_items:
- grafana_agent_version
- grafana_agent_install_dir
- grafana_agent_binary
- grafana_agent_config_dir
- grafana_agent_config_filename
- grafana_agent_env_file
- grafana_agent_local_tmp_dir
- grafana_agent_wal_dir
- grafana_agent_positions_dir
- grafana_agent_mode
- _grafana_agent_systemd_dir
- _grafana_agent_systemd_unit
- grafana_agent_user
- grafana_agent_user_group
- grafana_agent_user_shell
- grafana_agent_user_createhome
- grafana_agent_local_binary_file
- grafana_agent_flags_extra
- grafana_agent_env_vars
- grafana_agent_provisioned_config_file
- grafana_agent_metrics_config
- grafana_agent_logs_config
- grafana_agent_traces_config
- grafana_agent_integrations_config
- name: Fail when variables do not have a length
ansible.builtin.fail:
msg: "The {{ item }} property must be valued"
when: ( vars[item] | string | length == 0 )
with_items:
- grafana_agent_version
- grafana_agent_install_dir
- grafana_agent_binary
- grafana_agent_config_dir
- grafana_agent_config_filename
- grafana_agent_env_file
- grafana_agent_local_tmp_dir
- grafana_agent_wal_dir
- grafana_agent_positions_dir
- grafana_agent_mode
- _grafana_agent_systemd_dir
- _grafana_agent_systemd_unit
- grafana_agent_user
- grafana_agent_user_group
- grafana_agent_user_shell
- grafana_agent_user_createhome
- name: Fail when variables are not a number
ansible.builtin.fail:
msg: "The {{ item }} property must be number"
when: ( vars[item] is defined and vars[item] is not number)
with_items: []
- name: Fail when flags are not a boolean
ansible.builtin.fail:
msg: "The {{ item }} property must be a boolean true or false"
when: ( vars[item] | bool | string | lower ) not in ['true', 'false']
with_items:
- grafana_agent_user_createhome
- name: Fail when the agent mode is not "flow" or "static"
ansible.builtin.fail:
msg: "The grafana_agent_mode property must be a boolean 'flow' or 'static'"
when: grafana_agent_mode not in ['flow', 'static']

View File

@ -0,0 +1,9 @@
{{ ansible_managed | comment }}
# Grafana Agent Environment File
AGENT_MODE={{ grafana_agent_mode }}
GOMAXPROCS={{ ansible_processor_vcpus|default(ansible_processor_count) }}
{% for key, value in grafana_agent_env_vars.items() %}
{{key}}={{value}}
{% endfor %}

View File

@ -0,0 +1,37 @@
---
{{ ansible_managed | comment }}
#################################################################################################
# Configures the server of the Agent used to enable self-scraping #
#################################################################################################
# Docs: https://grafana.com/docs/agent/latest/configuration/server-config/
server:
{{ grafana_agent_server_config | to_nice_yaml(indent=2,sort_keys=False) | indent(2) }}
#################################################################################################
# Configures metric collection #
#################################################################################################
# Docs: https://grafana.com/docs/agent/latest/configuration/metrics-config/
metrics:
{{ grafana_agent_metrics_config | to_nice_yaml(indent=2,sort_keys=False) | indent(2) }}
#################################################################################################
# Configures logs collection #
#################################################################################################
# Docs: https://grafana.com/docs/agent/latest/configuration/logs-config/
logs:
{{ grafana_agent_logs_config | to_nice_yaml(indent=2,sort_keys=False) | indent(2) }}
#################################################################################################
# Configures traces collection #
#################################################################################################
# Docs: https://grafana.com/docs/agent/latest/configuration/traces-config/
traces:
{{ grafana_agent_traces_config | to_nice_yaml(indent=2,sort_keys=False) | indent(2) }}
#################################################################################################
# Configures integrations for the agent #
#################################################################################################
# Docs: https://grafana.com/docs/agent/latest/configuration/integrations/
integrations:
{{ grafana_agent_integrations_config | to_nice_yaml(indent=2,sort_keys=False) | indent(2) }}

View File

@ -0,0 +1,44 @@
{{ ansible_managed | comment }}
[Unit]
Description=Grafana Agent
Documentation=https://grafana.com/docs/agent/latest/
After=network-online.target
Requires=local-fs.target
After=local-fs.target
[Service]
Type=simple
User={{ grafana_agent_user }}
Group={{ grafana_agent_user_group }}
WorkingDirectory={{ grafana_agent_config_dir }}
EnvironmentFile={{ grafana_agent_config_dir }}/{{ grafana_agent_env_file}}
ExecStart={{ grafana_agent_install_dir }}/{{ grafana_agent_binary }} \
{% for flag, flag_value in grafana_agent_flags_extra.items() %}
{% if not flag_value %}
--{{ flag }} \
{% elif flag_value is string %}
--{{ flag }}={{ flag_value }} \
{% elif flag_value is sequence %}
{% for flag_value_item in flag_value %}
--{{ flag }}={{ flag_value_item }} \
{% endfor %}
{% endif %}
{% endfor %}
--config.file={{ grafana_agent_config_dir }}/{{ grafana_agent_config_filename }}
SyslogIdentifier=grafana-agent
Restart=always
{% if grafana_agent_systemd_version | int >= 232 %}
ProtectSystem=strict
ProtectControlGroups=true
ProtectKernelModules=true
ProtectKernelTunables=yes
{% else %}
ProtectSystem=full
{% endif %}
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,34 @@
---
_grafana_agent_github_org: grafana
_grafana_agent_github_repo: agent
_grafana_agent_base_download_url: "https://github.com/{{ _grafana_agent_github_org }}/{{ _grafana_agent_github_repo }}/releases/download"
# set the go cpu arch
_download_cpu_arch_map:
i386: '386'
x86_64: amd64
aarch64: arm64
armv7l: armv7
armv6l: armv6
_grafana_agent_cpu_arch: "{{ _download_cpu_arch_map[ansible_architecture] | default(ansible_architecture) }}"
# set the go os family
_download_os_family_map:
alpine: linux
debian: linux
redhat: linux
darwin: darwin
freebsd: freebsd
_grafana_agent_os_family: "{{ _download_os_family_map[ansible_os_family | lower] | default(ansible_os_family | lower) }}"
# set the name of the archive file to download
_grafana_agent_download_archive_file: "grafana-agent-{{ _grafana_agent_os_family }}-{{ _grafana_agent_cpu_arch }}.zip"
# set the name of the binary file
_grafana_agent_download_binary_file: "grafana-agent-{{ _grafana_agent_os_family }}-{{ _grafana_agent_cpu_arch }}"
# systemd info
_grafana_agent_systemd_dir: /usr/lib/systemd/system/
_grafana_agent_systemd_unit: grafana-agent.service

View File

@ -1,17 +1,18 @@
---
- name: Create Alerting contact point - name: Create Alerting contact point
grafana.grafana.alert_contact_point: grafana.grafana.alert_contact_point:
name: ops-email name: ops-email
uid: opsemail uid: opsemail
type: email type: email
settings: { settings:
addresses: "ops@mydomain.com,devs@mydomain.com" addresses: ops@mydomain.com,devs@mydomain.com
}
stack_slug: "{{ stack_name }}" stack_slug: "{{ stack_name }}"
grafana_api_key: "{{ grafana_api_key }}" grafana_api_key: "{{ grafana_api_key }}"
state: present state: present
register: add_result register: add_result
- assert: - name: Add Check
ansible.builtin.assert:
that: that:
- add_result.failed == false - add_result.failed == false
- add_result.output.provenance == "api" - add_result.output.provenance == "api"
@ -21,15 +22,15 @@
name: ops-email name: ops-email
uid: opsemail uid: opsemail
type: email type: email
settings: { settings:
addresses: "ops@mydomain.com,devs@mydomain.com" addresses: ops@mydomain.com,devs@mydomain.com
}
stack_slug: "{{ stack_name }}" stack_slug: "{{ stack_name }}"
grafana_api_key: "{{ grafana_api_key }}" grafana_api_key: "{{ grafana_api_key }}"
state: present state: present
register: idempotent_result register: idempotent_result
- assert: - name: Changed Check
ansible.builtin.assert:
that: that:
- idempotent_result.changed == false - idempotent_result.changed == false
- idempotent_result.output.provenance == "api" - idempotent_result.output.provenance == "api"
@ -39,15 +40,15 @@
name: ops-email name: ops-email
uid: opsemail uid: opsemail
type: email type: email
settings: { settings:
addresses: "ops@mydomain.com,devs@mydomain.com,admin@mydomain.com" addresses: "ops@mydomain.com,devs@mydomain.com,admin@mydomain.com"
}
stack_slug: "{{ stack_name }}" stack_slug: "{{ stack_name }}"
grafana_api_key: "{{ grafana_api_key }}" grafana_api_key: "{{ grafana_api_key }}"
state: present state: present
register: update_result register: update_result
- assert: - name: Failed Check
ansible.builtin.assert:
that: that:
- update_result.failed == false - update_result.failed == false
- update_result.output.provenance == "api" - update_result.output.provenance == "api"
@ -57,15 +58,15 @@
name: ops-email name: ops-email
uid: opsemail uid: opsemail
type: email type: email
settings: { settings:
addresses: "ops@mydomain.com,devs@mydomain.com,admin@mydomain.com" addresses: "ops@mydomain.com,devs@mydomain.com,admin@mydomain.com"
}
stack_slug: "{{ stack_name }}" stack_slug: "{{ stack_name }}"
grafana_api_key: "{{ grafana_api_key }}" grafana_api_key: "{{ grafana_api_key }}"
state: absent state: absent
register: delete_result register: delete_result
- assert: - name: Delete Check
ansible.builtin.assert:
that: that:
- delete_result.failed == false - delete_result.failed == false
- delete_result.output.message == "contactpoint deleted" - delete_result.output.message == "contactpoint deleted"

View File

@ -1,16 +1,15 @@
---
- name: Set Notification policy tree - name: Set Notification policy tree
grafana.grafana.alert_notification_policy: grafana.grafana.alert_notification_policy:
stack_slug: "{{ stack_name }}" stack_slug: "{{ stack_name }}"
grafana_api_key: "{{ grafana_api_key }}" grafana_api_key: "{{ grafana_api_key }}"
routes: [ routes:
{ - receiver: grafana-default-email,
receiver: grafana-default-email, object_matchers: [["env", "=", "Production"]]
object_matchers: [["env", "=", "Production"]],
}
]
register: result register: result
- assert: - name: Notification Check
ansible.builtin.assert:
that: that:
- result.failed == false - result.failed == false
- result.output.provenance == "api" - result.output.provenance == "api"

View File

@ -1,14 +1,16 @@
---
- name: Create Grafana Cloud API key - name: Create Grafana Cloud API key
grafana.grafana.cloud_api_key: grafana.grafana.cloud_api_key:
name: ansible-integration-test name: ansible-integration-test
role: Admin role: Admin
org_slug: "{{ org_name }}" org_slug: "{{ org_name }}"
existing_cloud_api_key: "{{ grafana_cloud_api_key }}" existing_cloud_api_key: "{{ grafana_cloud_api_key }}"
fail_if_already_created: False fail_if_already_created: false
state: present state: present
register: add_result register: add_result
- assert: - name: Add Check
ansible.builtin.assert:
that: that:
- add_result.output.name == "ansible-integration-test" - add_result.output.name == "ansible-integration-test"
when: add_result.output.name is defined when: add_result.output.name is defined
@ -19,11 +21,12 @@
role: Admin role: Admin
org_slug: "{{ org_name }}" org_slug: "{{ org_name }}"
existing_cloud_api_key: "{{ grafana_cloud_api_key }}" existing_cloud_api_key: "{{ grafana_cloud_api_key }}"
fail_if_already_created: False fail_if_already_created: false
state: present state: present
register: update_result register: update_result
- assert: - name: Update Check
ansible.builtin.assert:
that: that:
- update_result.changed == false - update_result.changed == false
- update_result.output == "A Cloud API key with the same name already exists" - update_result.output == "A Cloud API key with the same name already exists"
@ -37,7 +40,8 @@
state: absent state: absent
register: delete_result register: delete_result
- assert: - name: Delete Check
ansible.builtin.assert:
that: that:
- delete_result.changed == true - delete_result.changed == true
- delete_result.output == "Cloud API key is deleted" - delete_result.output == "Cloud API key is deleted"

View File

@ -1,3 +1,4 @@
---
- name: Add a plugin - name: Add a plugin
grafana.grafana.cloud_plugin: grafana.grafana.cloud_plugin:
name: grafana-github-datasource name: grafana-github-datasource
@ -7,7 +8,8 @@
state: present state: present
register: add_result register: add_result
- assert: - name: Add Check
ansible.builtin.assert:
that: that:
- add_result.changed == true - add_result.changed == true
- add_result.pluginName == "GitHub" - add_result.pluginName == "GitHub"
@ -21,7 +23,8 @@
state: present state: present
register: idempotency_result register: idempotency_result
- assert: - name: Idempotency Check
ansible.builtin.assert:
that: that:
- idempotency_result.changed == false - idempotency_result.changed == false
- idempotency_result.pluginName == "GitHub" - idempotency_result.pluginName == "GitHub"
@ -35,7 +38,8 @@
state: present state: present
register: update_result register: update_result
- assert: - name: Update Check
ansible.builtin.assert:
that: that:
- update_result.changed == true - update_result.changed == true
- update_result.pluginName == "GitHub" - update_result.pluginName == "GitHub"
@ -49,7 +53,8 @@
state: absent state: absent
register: delete_result register: delete_result
- assert: - name: Delete Check
ansible.builtin.assert:
that: that:
- delete_result.changed == true - delete_result.changed == true
- delete_result.pluginName == "GitHub" - delete_result.pluginName == "GitHub"

View File

@ -1,3 +1,4 @@
---
- name: Create a Grafana Cloud stack - name: Create a Grafana Cloud stack
grafana.grafana.cloud_stack: grafana.grafana.cloud_stack:
name: "{{ test_stack_name }}" name: "{{ test_stack_name }}"
@ -7,7 +8,8 @@
state: present state: present
register: create_result register: create_result
- assert: - name: Create Check
ansible.builtin.assert:
that: that:
- create_result.url == "https://" + "{{ test_stack_name }}" + ".grafana.net" - create_result.url == "https://" + "{{ test_stack_name }}" + ".grafana.net"
@ -24,7 +26,8 @@
state: absent state: absent
register: delete_result register: delete_result
- assert: - name: Delete Check
ansible.builtin.assert:
that: that:
- delete_result.changed == true - delete_result.changed == true
- delete_result.url == "https://" + "{{ test_stack_name }}" + ".grafana.net" - delete_result.url == "https://" + "{{ test_stack_name }}" + ".grafana.net"

View File

@ -1,47 +1,48 @@
---
- name: Create/Update a dashboard - name: Create/Update a dashboard
grafana.grafana.dashboard: grafana.grafana.dashboard:
dashboard: { dashboard:
"dashboard": { dashboard:
"uid": test1234, uid: test1234
"title": "Ansible Integration Test", title: Ansible Integration Test
"tags": [ "templated" ], tags:
"timezone": "browser", - templated
"schemaVersion": 16, timezone: browser
"version": 0, schemaVersion: 16
"refresh": "25s" refresh: 25s
}, version: 0
"overwrite": true overwrite: true
}
stack_slug: "{{ stack_name }}" stack_slug: "{{ stack_name }}"
grafana_api_key: "{{ grafana_api_key }}" grafana_api_key: "{{ grafana_api_key }}"
state: present state: present
register: result_present register: result_present
- assert: - name: Create/Update Check
ansible.builtin.assert:
that: that:
- result_present.changed == true - result_present.changed == true
- result_present.output.status == "success" - result_present.output.status == "success"
- name: Delete dashboard - name: Delete dashboard
grafana.grafana.dashboard: grafana.grafana.dashboard:
dashboard: { dashboard:
"dashboard": { dashboard:
"uid": test1234, uid: test1234
"title": "Ansible Integration Test", title: Ansible Integration Test
"tags": [ "templated" ], tags:
"timezone": "browser", - templated
"schemaVersion": 16, timezone: browser
"version": 0, schemaVersion: 16
"refresh": "25s" version: 0
}, refresh: 25s
"overwrite": true overwrite: true
}
stack_slug: "{{ stack_name }}" stack_slug: "{{ stack_name }}"
grafana_api_key: "{{ grafana_api_key }}" grafana_api_key: "{{ grafana_api_key }}"
state: absent state: absent
register: result_absent register: result_absent
- assert: - name: Delete Check
ansible.builtin.assert:
that: that:
- result_absent.changed == true - result_absent.changed == true
- result_absent.output.message == "Dashboard Ansible Integration Test deleted" - result_absent.output.message == "Dashboard Ansible Integration Test deleted"

View File

@ -1,23 +1,24 @@
---
- name: Create/Update a Data Source - name: Create/Update a Data Source
grafana.grafana.datasource: grafana.grafana.datasource:
dataSource: { dataSource:
name: "ansible-integration", name: ansible-integration
type: "influxdb", type: influxdb
url: "https://grafana.github.com/grafana-ansible-collection", url: https://grafana.github.com/grafana-ansible-collection
user: "user", user: user
secureJsonData: secureJsonData:
{ password: "password" }, password: password
database: "db-name", database: db-name
id: 123, id: 123
uid: "ansibletest", uid: ansibletest
access: "proxy" access: proxy
}
stack_slug: "{{ stack_name }}" stack_slug: "{{ stack_name }}"
grafana_api_key: "{{ grafana_api_key }}" grafana_api_key: "{{ grafana_api_key }}"
state: present state: present
register: create_result register: create_result
- assert: - name: Create Check
ansible.builtin.assert:
that: that:
- create_result.changed == true - create_result.changed == true
- create_result.output.message == "Datasource added" or create_result.output.message == "Datasource updated" - create_result.output.message == "Datasource added" or create_result.output.message == "Datasource updated"

View File

@ -1,3 +1,4 @@
---
- name: Create/Update a Folder in Grafana - name: Create/Update a Folder in Grafana
grafana.grafana.folder: grafana.grafana.folder:
title: Ansible Integration test title: Ansible Integration test
@ -8,7 +9,8 @@
state: present state: present
register: create_result register: create_result
- assert: - name: Create Check
ansible.builtin.assert:
that: that:
- create_result.failed == false - create_result.failed == false
@ -22,7 +24,8 @@
state: absent state: absent
register: delete_result register: delete_result
- assert: - name: Delete Check
ansible.builtin.assert:
that: that:
- delete_result.output.status == 200 - delete_result.output.status == 200
- delete_result.output.response == "Folder has been succesfuly deleted" - delete_result.output.response == "Folder has been successfully deleted"

123
tools/includes/logging.sh Executable file
View File

@ -0,0 +1,123 @@
#!/usr/bin/env bash
LOG_LEVEL=${LOG_LEVEL:=6} # 7 = debug -> 0 = emergency
NO_COLOR="${NO_COLOR:-}"
# shellcheck disable=SC2034
TRACE="0"
# _log
# -----------------------------------
# Handles all logging, all log messages are output to stderr so stdout can still be piped
# Example: _log "info" "Some message"
# -----------------------------------
# shellcheck disable=SC2034
_log () {
local log_level="${1}" # first option is the level, the rest is the message
shift
local color_success="\\x1b[32m"
local color_debug="\\x1b[36m"
local color_info="\\x1b[90m"
local color_notice="\\x1b[34m"
local color_warning="\\x1b[33m"
local color_error="\\x1b[31m"
local color_critical="\\x1b[1;31m"
local color_alert="\\x1b[1;33;41m"
local color_emergency="\\x1b[1;4;5;33;41m"
local colorvar="color_${log_level}"
local color="${!colorvar:-${color_error}}"
local color_reset="\\x1b[0m"
# If no color is set or a non-recognized terminal is used don't use colors
if [[ "${NO_COLOR:-}" = "true" ]] || { [[ "${TERM:-}" != "xterm"* ]] && [[ "${TERM:-}" != "screen"* ]]; } || [[ ! -t 2 ]]; then
if [[ "${NO_COLOR:-}" != "false" ]]; then
color="";
color_reset="";
fi
fi
# all remaining arguments are to be printed
local log_line=""
while IFS=$'\n' read -r log_line; do
echo -e "$(date +"%Y-%m-%d %H:%M:%S %Z") ${color}[${log_level}]${color_reset} ${log_line}" 1>&2
done <<< "${@:-}"
}
# emergency
# -----------------------------------
# Handles emergency logging
# -----------------------------------
emergency() {
_log emergency "${@}"; exit 1;
}
# success
# -----------------------------------
# Handles success logging
# -----------------------------------
success() {
_log success "${@}"; true;
}
# alert
# -----------------------------------
# Handles alert logging
# -----------------------------------
alert() {
[[ "${LOG_LEVEL:-0}" -ge 1 ]] && _log alert "${@}";
true;
}
# critical
# -----------------------------------
# Handles critical logging
# -----------------------------------
critical() {
[[ "${LOG_LEVEL:-0}" -ge 2 ]] && _log critical "${@}";
true;
}
# error
# -----------------------------------
# Handles error logging
# -----------------------------------
error() {
[[ "${LOG_LEVEL:-0}" -ge 3 ]] && _log error "${@}";
true;
}
# warning
# -----------------------------------
# Handles warning logging
# -----------------------------------
warning() {
[[ "${LOG_LEVEL:-0}" -ge 4 ]] && _log warning "${@}";
true;
}
# notice
# -----------------------------------
# Handles notice logging
# -----------------------------------
notice() {
[[ "${LOG_LEVEL:-0}" -ge 5 ]] && _log notice "${@}";
true;
}
# info
# -----------------------------------
# Handles info logging
# -----------------------------------
info() {
[[ "${LOG_LEVEL:-0}" -ge 6 ]] && _log info "${@}";
true;
}
# debug
# -----------------------------------
# Handles debug logging and prepends the name of the that called debug in front of the message
# -----------------------------------
debug() {
[[ "${LOG_LEVEL:-0}" -ge 7 ]] && _log debug "${FUNCNAME[1]}() ${*}";
true;
}

66
tools/includes/utils.sh Executable file
View File

@ -0,0 +1,66 @@
#!/usr/bin/env bash
# heading
# -----------------------------------
# Print standard heading
# -----------------------------------
heading() {
local title="${1}"
local message="${2}"
local width="75"
local orange="\\033[38;5;202m"
local reset="\\033[0m"
local bold="\\x1b[1m"
echo ""
echo -e "${orange} ▒▒▓▓▒▒▒▓ ${reset} ____ __ _ _ "
echo -e "${orange} ▓▓▓ ▒ ${reset} / ___| _ __ __ _ / _| __ _ _ __ __ _ | | __ _ | |__ ___ "
echo -e "${orange} ▒▓ ▒▒▒▒▓ ${reset} | | _ | '__| / _ || |_ / _ || '_ \\ / _ | | | / _ || '_ \\ / __|"
echo -e "${orange} ▒▓▓ ▒ ▒▒ ${reset} | |_| || | | (_| || _|| (_| || | | || (_| | | |___ | (_| || |_) | \\__\\"
echo -e "${orange} ▒▓▒ ▒▓ ${reset} \\____||_| \\__,_||_| \\__,_||_| |_| \\__,_| |_____| \\__,_||_.__/ |___/"
echo -e "${orange} ▒▒▒ ▒▒▒ ${reset} "
echo -e "${orange} ▒▒▒▒▒ ${reset} $(repeat $(( ((width - ${#title}) - 2) / 2)) " ")${bold}$title${reset}"
echo -e "${reset} $(repeat $(( ((width - ${#message}) - 2) / 2)) " ")$message${reset}"
echo ""
}
# repeat
# -----------------------------------
# Repeat a Character N number of times
# -----------------------------------
repeat(){
local times="${1:-80}"
local character="${2:-=}"
local start=1
local range
range=$(seq "$start" "$times")
local str=""
# shellcheck disable=SC2034
for i in $range; do
str="$str${character}"
done
echo "$str"
}
# lintWarning
# -----------------------------------
# Output a Lint Warning Message
# -----------------------------------
lintWarning() {
local msg="${1}"
local color_warning="\\x1b[33m"
local color_reset="\\x1b[0m"
local bold="\\x1b[1m"
echo -e "${color_warning}${bold}[warn]${color_reset} $msg"
}
# lintError
# -----------------------------------
# Output a Lint Error Message
# -----------------------------------
lintError() {
local msg="${1}"
local color_error="\\x1b[31m"
local color_reset="\\x1b[0m"
local bold="\\x1b[1m"
echo -e "${color_error}${bold}[error]${color_reset} $msg"
}

39
tools/lint-ansible.sh Executable file
View File

@ -0,0 +1,39 @@
#!/usr/bin/env bash
source "$(pwd)/tools/includes/utils.sh"
source "./tools/includes/logging.sh"
# output the heading
heading "Grafana Ansible Collection" "Performing Ansible Linting using ansible-lint"
# make sure pipenv exists
if [[ "$(command -v pipenv)" = "" ]]; then
echo >&2 "pipenv command is required, see (https://pipenv.pypa.io/en/latest/) or run: brew install pipenv";
exit 1;
fi
# make sure yamllint exists
if [[ "$(pipenv run pip freeze | grep -c "ansible-lint")" == "0" ]]; then
echo >&2 "ansible-lint command is required, see (https://pypi.org/project/ansible-lint/). Run \"make install\" to install it.";
exit 1;
fi
# determine whether or not the script is called directly or sourced
(return 0 2>/dev/null) && sourced=1 || sourced=0
# run yamllint
echo "$(pwd)/.ansible-lint"
pipenv run ansible-lint --config-file "$(pwd)/.ansible-lint" --strict
statusCode="$?"
if [[ "$statusCode" == "0" ]]; then
echo "no issues found"
echo ""
fi
echo ""
# if the script was called by another, send a valid exit code
if [[ "$sourced" == "1" ]]; then
return "$statusCode"
fi

38
tools/lint-editorconfig.sh Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env bash
source "$(pwd)/tools/includes/utils.sh"
source "./tools/includes/logging.sh"
# output the heading
heading "Grafana Ansible Collection" "Performing Editorconfig Linting using editorconfig-checker"
# check to see if remark is installed
if [[ ! -f "$(pwd)"/node_modules/.bin/editorconfig-checker ]]; then
emergency "editorconfig-checker node module is not installed, please run: make install";
fi
# determine whether or not the script is called directly or sourced
(return 0 2>/dev/null) && sourced=1 || sourced=0
statusCode=0
./node_modules/.bin/editorconfig-checker -config="$(pwd)/.editorconfig" -exclude "LICENSE|.+\.txt|.+\.py"
currentCode="$?"
# only override the statusCode if it is 0
if [[ "$statusCode" == 0 ]]; then
statusCode="$currentCode"
fi
if [[ "$statusCode" == "0" ]]; then
echo "no issues found"
echo ""
fi
echo ""
# if the script was called by another, send a valid exit code
if [[ "$sourced" == "1" ]]; then
return "$statusCode"
else
exit "$statusCode"
fi

49
tools/lint-markdown.sh Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env bash
source "$(pwd)/tools/includes/utils.sh"
source "./tools/includes/logging.sh"
# output the heading
heading "Grafana Ansible Collection" "Performing Markdown Linting using markdownlint"
# check to see if remark is installed
if [[ ! -f "$(pwd)"/node_modules/.bin/markdownlint-cli2 ]]; then
emergency "markdownlint-cli2 node module is not installed, please run: make install";
fi
# determine whether or not the script is called directly or sourced
(return 0 2>/dev/null) && sourced=1 || sourced=0
statusCode=0
while read -r dir; do
info "Checking file/directory: $dir"
./node_modules/.bin/markdownlint-cli2-config "$(pwd)/.markdownlint.yaml" "$dir"
currentCode="$?"
# if the current code is 0, output the file name for logging purposes
if [[ "$currentCode" == 0 ]]; then
echo -e "\\x1b[32m$dir\\x1b[0m: no issues found"
fi
# only override the statusCode if it is 0
if [[ "$statusCode" == 0 ]]; then
statusCode="$currentCode"
fi
echo ""
done < <(find . -type f -name "*.md" -not -path "./node_modules/*" -not -path "./.git/*" -print0 | \
xargs -0 dirname | \
sort -nr | \
uniq | \
sort | \
xargs printf -- '%s/*.md\n' | \
sed 's|\./\*\.md|./README.md|'
)
echo ""
echo ""
# if the script was called by another, send a valid exit code
if [[ "$sourced" == "1" ]]; then
return "$statusCode"
else
exit "$statusCode"
fi

46
tools/lint-shell.sh Executable file
View File

@ -0,0 +1,46 @@
#!/usr/bin/env bash
source "$(pwd)/tools/includes/utils.sh"
source "./tools/includes/logging.sh"
# output the heading
heading "Grafana Ansible Collection" "Performing Shell Linting using shellcheck"
# check to see if shellcheck is installed
if [[ "$(command -v shellcheck)" = "" ]]; then
emergency "shellcheck is required if running lint locally, see: (https://shellcheck.net) or run: brew install nvm && nvm install 18";
fi
# determine whether or not the script is called directly or sourced
(return 0 2>/dev/null) && sourced=1 || sourced=0
statusCode=0
while read -r file; do
shellcheck \
--external-sources \
--shell bash \
--source-path "$(dirname "$file")" \
"$file"
currentCode="$?"
# if the current code is 0, output the file name for logging purposes
if [[ "$currentCode" == 0 ]]; then
echo -e "\\x1b[32m$file\\x1b[0m: no issues found"
else
echo ""
fi
# only override the statusCode if it is 0
if [[ "$statusCode" == 0 ]]; then
statusCode="$currentCode"
fi
done < <(find . -type f -name "*.sh" -not -path "./node_modules/*" -not -path "./.git/*")
echo ""
echo ""
# if the script was called by another, send a valid exit code
if [[ "$sourced" == "1" ]]; then
return "$statusCode"
else
exit "$statusCode"
fi

40
tools/lint-text.sh Executable file
View File

@ -0,0 +1,40 @@
#!/usr/bin/env bash
source "$(pwd)/tools/includes/utils.sh"
source "./tools/includes/logging.sh"
# output the heading
heading "Grafana Ansible Collections" "Performing Text Linting using textlint"
# check to see if remark is installed
if [[ ! -f "$(pwd)"/node_modules/.bin/textlint ]]; then
emergency "remark node module is not installed, please run: make install";
fi
# determine whether or not the script is called directly or sourced
(return 0 2>/dev/null) && sourced=1 || sourced=0
statusCode=0
while read -r file; do
"$(pwd)"/node_modules/.bin/textlint --config "$(pwd)/.textlintrc" "$file"
currentCode="$?"
# if the current code is 0, output the file name for logging purposes
if [[ "$currentCode" == 0 ]]; then
echo -e "\\x1b[32m$file\\x1b[0m: no issues found"
fi
# only override the statusCode if it is 0
if [[ "$statusCode" == 0 ]]; then
statusCode="$currentCode"
fi
done < <(find . -type f -name "*.md" -not -path "./node_modules/*" -not -path "./.git/*")
echo ""
echo ""
# if the script was called by another, send a valid exit code
if [[ "$sourced" == "1" ]]; then
return "$statusCode"
else
exit "$statusCode"
fi

37
tools/lint-yaml.sh Executable file
View File

@ -0,0 +1,37 @@
#!/usr/bin/env bash
source "$(pwd)/tools/includes/utils.sh"
source "./tools/includes/logging.sh"
# output the heading
heading "Grafana Ansible Collection" "Performing YAML Linting using yamllint"
# make sure pipenv exists
if [[ "$(command -v pipenv)" = "" ]]; then
echo >&2 "pipenv command is required, see (https://pipenv.pypa.io/en/latest/) or run: brew install pipenv";
exit 1;
fi
# make sure yamllint exists
if [[ "$(pipenv run pip freeze | grep -c "yamllint")" == "0" ]]; then
echo >&2 "yamllint command is required, see (https://pypi.org/project/yamllint/). Run \"make install\" to install it.";
exit 1;
fi
# determine whether or not the script is called directly or sourced
(return 0 2>/dev/null) && sourced=1 || sourced=0
# run yamllint
pipenv run yamllint --strict --config-file "$(pwd)/.yamllint" .
statusCode="$?"
if [[ "$statusCode" == "0" ]]; then
echo "no issues found"
echo ""
fi
# if the script was called by another, send a valid exit code
if [[ "$sourced" == "1" ]]; then
return "$statusCode"
fi

39
tools/setup.sh Executable file
View File

@ -0,0 +1,39 @@
#!/usr/bin/env bash
source "$(pwd)/tools/includes/utils.sh"
source "./tools/includes/logging.sh"
# output the heading
heading "Grafana Ansible Collection" "Performing Setup Checks"
# make sure Node exists
info "Checking to see if Node is installed"
if [[ "$(command -v node)" = "" ]]; then
warning "node is required if running lint locally, see: (https://nodejs.org) or run: brew install nvm && nvm install 18";
else
success "node is installed"
fi
# make sure yarn exists
info "Checking to see if yarn is installed"
if [[ "$(command -v yarn)" = "" ]]; then
warning "yarn is required if running lint locally, see: (https://yarnpkg.com) or run: brew install yarn";
else
success "yarn is installed"
fi
# make sure shellcheck exists
info "Checking to see if shellcheck is installed"
if [[ "$(command -v shellcheck)" = "" ]]; then
warning "shellcheck is required if running lint locally, see: (https://shellcheck.net) or run: brew install nvm && nvm install 18";
else
success "shellcheck is installed"
fi
# make sure pipenv exists
if [[ "$(command -v pipenv)" = "" ]]; then
warning "pipenv command is required, see (https://pipenv.pypa.io/en/latest/) or run: brew install pipenv";
else
success "pipenv is installed"
fi

1721
yarn.lock Normal file

File diff suppressed because it is too large Load Diff