feat: added new roles to match daily driver desktop; full idempotency; several fixes and tweaks; re-added hosts in inventory
This commit is contained in:
parent
555fde4351
commit
726b7668f9
65 changed files with 10012 additions and 377 deletions
|
@ -17,43 +17,41 @@ description: vaguely similar to a package manager, but for GitHub artifacts.
|
|||
version_added: "2.15.0"
|
||||
|
||||
options:
|
||||
artifacts:
|
||||
description: a list of artifacts to retrieve
|
||||
type: list
|
||||
asset_name:
|
||||
description: filename of the asset to retrieve, used only for release type; supports templating
|
||||
type: str
|
||||
required: false
|
||||
default: ""
|
||||
asset_type:
|
||||
description: whether the asset is a release or just a tagged asset
|
||||
type: str
|
||||
required: true
|
||||
elements: dict
|
||||
suboptions:
|
||||
asset_name:
|
||||
description: filename of the asset to retrieve, used only for release type; supports templating
|
||||
type: str
|
||||
required: false
|
||||
default: ""
|
||||
asset_type:
|
||||
description: whether the asset is a release or just a tagged asset
|
||||
type: str
|
||||
required: true
|
||||
choices:
|
||||
- release
|
||||
- tag
|
||||
cmds:
|
||||
description: commands to execute in order to install the downloaded asset; supports templating
|
||||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
default: []
|
||||
repository:
|
||||
description: repository to query, formatted like "<owner>/<repo>"
|
||||
required: true
|
||||
type: str
|
||||
version:
|
||||
description: version of the asset to fetch; defaults to `latest`
|
||||
required: false
|
||||
type: str
|
||||
default: latest
|
||||
choices:
|
||||
- release
|
||||
- tag
|
||||
cmds:
|
||||
description: commands to execute in order to install the downloaded asset; supports templating
|
||||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
default: []
|
||||
creates:
|
||||
description: if provided and target file / directory exists, this step will **not** be run
|
||||
type: str
|
||||
required: false
|
||||
github_token:
|
||||
description: a GitHub app token if you have one; limits impact of rate-limiting errors
|
||||
type: str
|
||||
required: false
|
||||
repository:
|
||||
description: repository to query, formatted like "<owner>/<repo>"
|
||||
required: true
|
||||
type: str
|
||||
version:
|
||||
description: version of the asset to fetch; defaults to `latest`
|
||||
required: false
|
||||
type: str
|
||||
default: latest
|
||||
|
||||
notes:
|
||||
- "Strings that allow the use of templating variables support the following:"
|
||||
|
@ -72,23 +70,18 @@ author:
|
|||
|
||||
EXAMPLES = r'''
|
||||
- name: Install dependencies from GitHub
|
||||
become: yes
|
||||
become: true
|
||||
tags:
|
||||
- molecule-idempotence-notest
|
||||
github_artifact:
|
||||
artifacts:
|
||||
- asset_type: tag
|
||||
repository: smxi/inxi
|
||||
cmds:
|
||||
- tar -zxf {asset_dirname}/{asset_filename}
|
||||
- install --group=root --mode=755 --owner=root smxi-inxi-*/inxi /usr/bin
|
||||
- install --group=root --mode=644 --owner=root smxi-inxi-*/inxi.1 /usr/share/man/man1
|
||||
- apt-get install libdata-dump-perl
|
||||
- asset_name: dive_{version}_linux_amd64.deb
|
||||
asset_type: release
|
||||
repository: wagoodman/dive
|
||||
cmds:
|
||||
- dpkg -i {asset_dirname}/{asset_filename}
|
||||
nullified.infrastructure.github_artifact:
|
||||
asset_type: tag
|
||||
repository: smxi/inxi
|
||||
cmds:
|
||||
- tar -zxf {asset_dirname}/{asset_filename}
|
||||
- install --group=root --mode=755 --owner=root smxi-inxi-*/inxi /usr/bin
|
||||
- install --group=root --mode=644 --owner=root smxi-inxi-*/inxi.1 /usr/share/man/man1
|
||||
- apt-get install libdata-dump-perl
|
||||
creates: /usr/bin/inxi
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
|
@ -122,7 +115,7 @@ try:
|
|||
from datetime import datetime
|
||||
from difflib import SequenceMatcher
|
||||
from json import loads
|
||||
from os import environ, sep
|
||||
from os import environ, sep, path
|
||||
from platform import system, machine
|
||||
from typing import Any
|
||||
LIB_IMPORTED = True
|
||||
|
@ -260,7 +253,7 @@ def get_tagged_asset(artifact: dict[str, Any]) -> tuple[dict[str, str], dict[str
|
|||
}, info
|
||||
|
||||
|
||||
def fetch_metadata(artifact: dict[str, str]) -> dict[str, str] | None:
|
||||
def fetch_metadata(artifact: dict[str, str | list[str]]) -> dict[str, str] | None:
|
||||
""" retrieve metadata from the specified repository """
|
||||
if artifact["asset_type"] == "tag":
|
||||
metadata, info = get_tagged_asset(artifact)
|
||||
|
@ -278,37 +271,34 @@ def main():
|
|||
global ANSIBLE_MODULE
|
||||
|
||||
module_args: dict[str, dict[str, Any]] = {
|
||||
"artifacts": {
|
||||
"type": "list",
|
||||
"elements": "dict",
|
||||
"asset_name": {
|
||||
"type": "str",
|
||||
"required": False,
|
||||
"default": ""
|
||||
},
|
||||
"asset_type": {
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"options": {
|
||||
"asset_name": {
|
||||
"type": "str",
|
||||
"required": False,
|
||||
"default": ""
|
||||
},
|
||||
"asset_type": {
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"choices": ["release", "tag"],
|
||||
},
|
||||
"cmds": {
|
||||
"type": "list",
|
||||
"elements": "str",
|
||||
"required": False,
|
||||
"default": []
|
||||
},
|
||||
"repository": {
|
||||
"type": "str",
|
||||
"required": True
|
||||
},
|
||||
"version": {
|
||||
"type": "str",
|
||||
"required": False,
|
||||
"default": "latest"
|
||||
}
|
||||
}
|
||||
"choices": ["release", "tag"],
|
||||
},
|
||||
"cmds": {
|
||||
"type": "list",
|
||||
"elements": "str",
|
||||
"required": False,
|
||||
"default": []
|
||||
},
|
||||
"creates": {
|
||||
"type": "str",
|
||||
"required": False
|
||||
},
|
||||
"repository": {
|
||||
"type": "str",
|
||||
"required": True
|
||||
},
|
||||
"version": {
|
||||
"type": "str",
|
||||
"required": False,
|
||||
"default": "latest"
|
||||
},
|
||||
"github_token": {
|
||||
"type": "str",
|
||||
|
@ -317,9 +307,13 @@ def main():
|
|||
},
|
||||
}
|
||||
result: dict[str, Any] = {
|
||||
"artifacts": [],
|
||||
"changed": False,
|
||||
"msg": ""
|
||||
"commands": [],
|
||||
"failed": False,
|
||||
"filepath": "",
|
||||
"msg": "",
|
||||
"state": "",
|
||||
"version": ""
|
||||
}
|
||||
|
||||
ANSIBLE_MODULE = AnsibleModule(argument_spec=module_args, supports_check_mode=True)
|
||||
|
@ -328,6 +322,11 @@ def main():
|
|||
if ANSIBLE_MODULE.params["github_token"]:
|
||||
DEFAULT_HEADERS["Authorization"] = "Bearer {}".format(ANSIBLE_MODULE.params["github_token"])
|
||||
|
||||
creates_file: str | None = ANSIBLE_MODULE.params.get("creates", None)
|
||||
if creates_file and path.exists(creates_file):
|
||||
result["state"] = "ignored"
|
||||
ANSIBLE_MODULE.exit_json(**result)
|
||||
|
||||
if not LIB_IMPORTED:
|
||||
ANSIBLE_MODULE.fail_json(msg=missing_required_lib(IMPORT_LIB_NAME), exception=IMPORT_LIB_ERROR) # pylint: disable=used-before-assignment
|
||||
|
||||
|
@ -335,66 +334,58 @@ def main():
|
|||
TEMPLATE_ASSET_NAME_VARS.update({key.lower(): value for key, value in freedesktop_os_release().items()})
|
||||
TEMPLATE_ASSET_NAME_VARS["system"] = system().lower()
|
||||
TEMPLATE_ASSET_NAME_VARS["machine"] = machine().lower()
|
||||
for artifact in ANSIBLE_MODULE.params["artifacts"]:
|
||||
# fetch artifact metadata
|
||||
artifact_result: dict[str, Any] = {
|
||||
"asset_data": fetch_metadata(artifact),
|
||||
"repository": artifact.get("repository"),
|
||||
"version": artifact.get("version"),
|
||||
"cmds": []
|
||||
}
|
||||
result["rate_limit_remaining"] = artifact_result["asset_data"].get("rate_limit_remaining", "unknown")
|
||||
result["rate_limit_max"] = artifact_result["asset_data"].get("rate_limit_max", "unknown")
|
||||
|
||||
if "error" in artifact_result["asset_data"]:
|
||||
result["artifacts"].append(artifact_result)
|
||||
result["msg"] = artifact_result["asset_data"].get("error")
|
||||
result["failed"] = True
|
||||
ANSIBLE_MODULE.fail_json(**result)
|
||||
artifact: dict[str, str | list[str]] = {}
|
||||
for param_name in ["asset_name", "asset_type", "cmds", "repository", "version"]:
|
||||
artifact[param_name] = ANSIBLE_MODULE.params[param_name]
|
||||
|
||||
# download artifact
|
||||
if ANSIBLE_MODULE.check_mode:
|
||||
artifact_result["download_dir"] = "unknown"
|
||||
else:
|
||||
artifact_result["download_dir"] = fetch_file(ANSIBLE_MODULE, artifact_result["asset_data"].get("download_url", "unknown"), decompress=False)
|
||||
TEMPLATE_ASSET_NAME_VARS["asset_name"] = artifact_result["asset_data"].get("asset_name", "unknown")
|
||||
TEMPLATE_ASSET_NAME_VARS["asset_version"] = artifact_result["asset_data"].get("version", "unknown")
|
||||
parts = artifact_result["download_dir"].rsplit(sep, 1)
|
||||
TEMPLATE_ASSET_NAME_VARS["asset_dirname"] = parts[0] if len(parts) > 1 else ""
|
||||
TEMPLATE_ASSET_NAME_VARS["asset_filename"] = parts[1] if len(parts) > 1 else parts[0]
|
||||
asset_data: dict[str, str] = fetch_metadata(artifact)
|
||||
result["rate_limit_remaining"] = asset_data.get("rate_limit_remaining", "unknown")
|
||||
result["rate_limit_max"] = asset_data.get("rate_limit_max", "unknown")
|
||||
result["version"] = asset_data.get("version", artifact.get("version"))
|
||||
|
||||
# install artifact
|
||||
artifact_commands = [line.format(**TEMPLATE_ASSET_NAME_VARS) for line in artifact["cmds"]]
|
||||
if ANSIBLE_MODULE.check_mode:
|
||||
artifact_result["stdout"] = artifact_result["stderr"] = ""
|
||||
artifact_result["ret_code"] = None
|
||||
artifact_result["cmds"] = artifact_commands
|
||||
artifact_result["state"] = "should be installed" if len(artifact_commands) else "should be downloaded"
|
||||
else:
|
||||
for command_line in artifact_commands:
|
||||
cmd_rc, cmd_out, cmd_err = ANSIBLE_MODULE.run_command(command_line, use_unsafe_shell=True, cwd=ANSIBLE_MODULE.tmpdir)
|
||||
result["changed"] = True
|
||||
artifact_result["cmds"].append({
|
||||
"command": command_line,
|
||||
"stdout": cmd_out,
|
||||
"stderr": cmd_err,
|
||||
"ret_code": cmd_rc
|
||||
})
|
||||
if "error" in asset_data:
|
||||
result["state"] = "fetch failed"
|
||||
result["msg"] = asset_data.get("error", "unknown error encountered")
|
||||
result["failed"] = True
|
||||
ANSIBLE_MODULE.fail_json(**result)
|
||||
|
||||
if cmd_rc:
|
||||
artifact_result["state"] = "installation failed"
|
||||
result["failed"] = True
|
||||
result["artifacts"].append(artifact_result)
|
||||
ANSIBLE_MODULE.fail_json(**result)
|
||||
# download artifact
|
||||
if ANSIBLE_MODULE.check_mode:
|
||||
result["filepath"] = "unknown"
|
||||
else:
|
||||
result["filepath"] = fetch_file(ANSIBLE_MODULE, asset_data.get("download_url", "unknown"), decompress=False)
|
||||
TEMPLATE_ASSET_NAME_VARS["asset_name"] = asset_data.get("asset_name", "unknown")
|
||||
TEMPLATE_ASSET_NAME_VARS["asset_version"] = asset_data.get("version", "unknown")
|
||||
parts = result["filepath"].rsplit(sep, 1)
|
||||
TEMPLATE_ASSET_NAME_VARS["asset_dirname"] = parts[0] if len(parts) > 1 else ""
|
||||
TEMPLATE_ASSET_NAME_VARS["asset_filename"] = parts[1] if len(parts) > 1 else parts[0]
|
||||
|
||||
try:
|
||||
del artifact_result["command"], artifact_result["stdout"], artifact_result["stderr"], artifact_result["ret_code"]
|
||||
except: # pylint: disable=bare-except # noqa: 722
|
||||
pass
|
||||
artifact_result["state"] = "installed" if len(artifact_commands) else "downloaded"
|
||||
# install artifact
|
||||
artifact_commands = [line.format(**TEMPLATE_ASSET_NAME_VARS) for line in artifact["cmds"]]
|
||||
if ANSIBLE_MODULE.check_mode:
|
||||
result["commands"] = artifact_commands
|
||||
result["state"] = "should be installed" if len(artifact_commands) else "should be downloaded"
|
||||
else:
|
||||
for command_line in artifact_commands:
|
||||
cmd_rc, cmd_out, cmd_err = ANSIBLE_MODULE.run_command(command_line, use_unsafe_shell=True, cwd=ANSIBLE_MODULE.tmpdir)
|
||||
result["changed"] = True
|
||||
result["commands"].append({
|
||||
"command": command_line,
|
||||
"stdout": cmd_out,
|
||||
"stderr": cmd_err,
|
||||
"ret_code": cmd_rc
|
||||
})
|
||||
|
||||
result["artifacts"].append(artifact_result)
|
||||
result["msg"] = "OK"
|
||||
if cmd_rc:
|
||||
result["state"] = "installation failed"
|
||||
result["msg"] = cmd_err
|
||||
result["failed"] = True
|
||||
ANSIBLE_MODULE.fail_json(**result)
|
||||
|
||||
result["state"] = "installed" if len(artifact_commands) else "downloaded"
|
||||
|
||||
result["msg"] = "Successful"
|
||||
ANSIBLE_MODULE.exit_json(**result)
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue