pyinfra 0.11.dev3__py3-none-any.whl → 3.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyinfra/__init__.py +9 -12
- pyinfra/__main__.py +4 -0
- pyinfra/api/__init__.py +19 -3
- pyinfra/api/arguments.py +413 -0
- pyinfra/api/arguments_typed.py +79 -0
- pyinfra/api/command.py +274 -0
- pyinfra/api/config.py +222 -28
- pyinfra/api/connect.py +33 -13
- pyinfra/api/connectors.py +27 -0
- pyinfra/api/deploy.py +65 -66
- pyinfra/api/exceptions.py +73 -18
- pyinfra/api/facts.py +267 -200
- pyinfra/api/host.py +416 -50
- pyinfra/api/inventory.py +121 -160
- pyinfra/api/metadata.py +69 -0
- pyinfra/api/operation.py +432 -262
- pyinfra/api/operations.py +273 -260
- pyinfra/api/state.py +302 -248
- pyinfra/api/util.py +309 -369
- pyinfra/connectors/base.py +173 -0
- pyinfra/connectors/chroot.py +212 -0
- pyinfra/connectors/docker.py +405 -0
- pyinfra/connectors/dockerssh.py +297 -0
- pyinfra/connectors/local.py +238 -0
- pyinfra/connectors/scp/__init__.py +1 -0
- pyinfra/connectors/scp/client.py +204 -0
- pyinfra/connectors/ssh.py +727 -0
- pyinfra/connectors/ssh_util.py +114 -0
- pyinfra/connectors/sshuserclient/client.py +309 -0
- pyinfra/connectors/sshuserclient/config.py +102 -0
- pyinfra/connectors/terraform.py +135 -0
- pyinfra/connectors/util.py +417 -0
- pyinfra/connectors/vagrant.py +183 -0
- pyinfra/context.py +145 -0
- pyinfra/facts/__init__.py +7 -6
- pyinfra/facts/apk.py +22 -7
- pyinfra/facts/apt.py +117 -60
- pyinfra/facts/brew.py +100 -15
- pyinfra/facts/bsdinit.py +23 -0
- pyinfra/facts/cargo.py +37 -0
- pyinfra/facts/choco.py +47 -0
- pyinfra/facts/crontab.py +195 -0
- pyinfra/facts/deb.py +94 -0
- pyinfra/facts/dnf.py +48 -0
- pyinfra/facts/docker.py +96 -23
- pyinfra/facts/efibootmgr.py +113 -0
- pyinfra/facts/files.py +629 -58
- pyinfra/facts/flatpak.py +77 -0
- pyinfra/facts/freebsd.py +70 -0
- pyinfra/facts/gem.py +19 -6
- pyinfra/facts/git.py +59 -14
- pyinfra/facts/gpg.py +150 -0
- pyinfra/facts/hardware.py +313 -167
- pyinfra/facts/iptables.py +72 -62
- pyinfra/facts/launchd.py +44 -0
- pyinfra/facts/lxd.py +17 -4
- pyinfra/facts/mysql.py +122 -86
- pyinfra/facts/npm.py +17 -9
- pyinfra/facts/openrc.py +71 -0
- pyinfra/facts/opkg.py +246 -0
- pyinfra/facts/pacman.py +50 -7
- pyinfra/facts/pip.py +24 -7
- pyinfra/facts/pipx.py +82 -0
- pyinfra/facts/pkg.py +15 -6
- pyinfra/facts/pkgin.py +35 -0
- pyinfra/facts/podman.py +54 -0
- pyinfra/facts/postgres.py +178 -0
- pyinfra/facts/postgresql.py +6 -147
- pyinfra/facts/rpm.py +105 -0
- pyinfra/facts/runit.py +77 -0
- pyinfra/facts/selinux.py +161 -0
- pyinfra/facts/server.py +762 -285
- pyinfra/facts/snap.py +88 -0
- pyinfra/facts/systemd.py +139 -0
- pyinfra/facts/sysvinit.py +59 -0
- pyinfra/facts/upstart.py +35 -0
- pyinfra/facts/util/__init__.py +17 -0
- pyinfra/facts/util/databases.py +4 -6
- pyinfra/facts/util/packaging.py +37 -6
- pyinfra/facts/util/units.py +30 -0
- pyinfra/facts/util/win_files.py +99 -0
- pyinfra/facts/vzctl.py +20 -13
- pyinfra/facts/xbps.py +35 -0
- pyinfra/facts/yum.py +34 -40
- pyinfra/facts/zfs.py +77 -0
- pyinfra/facts/zypper.py +42 -0
- pyinfra/local.py +45 -83
- pyinfra/operations/__init__.py +12 -0
- pyinfra/operations/apk.py +99 -0
- pyinfra/operations/apt.py +496 -0
- pyinfra/operations/brew.py +232 -0
- pyinfra/operations/bsdinit.py +59 -0
- pyinfra/operations/cargo.py +45 -0
- pyinfra/operations/choco.py +61 -0
- pyinfra/operations/crontab.py +194 -0
- pyinfra/operations/dnf.py +213 -0
- pyinfra/operations/docker.py +492 -0
- pyinfra/operations/files.py +2014 -0
- pyinfra/operations/flatpak.py +95 -0
- pyinfra/operations/freebsd/__init__.py +12 -0
- pyinfra/operations/freebsd/freebsd_update.py +70 -0
- pyinfra/operations/freebsd/pkg.py +219 -0
- pyinfra/operations/freebsd/service.py +116 -0
- pyinfra/operations/freebsd/sysrc.py +92 -0
- pyinfra/operations/gem.py +48 -0
- pyinfra/operations/git.py +420 -0
- pyinfra/operations/iptables.py +312 -0
- pyinfra/operations/launchd.py +45 -0
- pyinfra/operations/lxd.py +69 -0
- pyinfra/operations/mysql.py +610 -0
- pyinfra/operations/npm.py +57 -0
- pyinfra/operations/openrc.py +63 -0
- pyinfra/operations/opkg.py +89 -0
- pyinfra/operations/pacman.py +82 -0
- pyinfra/operations/pip.py +206 -0
- pyinfra/operations/pipx.py +103 -0
- pyinfra/operations/pkg.py +71 -0
- pyinfra/operations/pkgin.py +92 -0
- pyinfra/operations/postgres.py +437 -0
- pyinfra/operations/postgresql.py +30 -0
- pyinfra/operations/puppet.py +41 -0
- pyinfra/operations/python.py +73 -0
- pyinfra/operations/runit.py +184 -0
- pyinfra/operations/selinux.py +190 -0
- pyinfra/operations/server.py +1100 -0
- pyinfra/operations/snap.py +118 -0
- pyinfra/operations/ssh.py +217 -0
- pyinfra/operations/systemd.py +150 -0
- pyinfra/operations/sysvinit.py +142 -0
- pyinfra/operations/upstart.py +68 -0
- pyinfra/operations/util/__init__.py +12 -0
- pyinfra/operations/util/docker.py +407 -0
- pyinfra/operations/util/files.py +247 -0
- pyinfra/operations/util/packaging.py +338 -0
- pyinfra/operations/util/service.py +46 -0
- pyinfra/operations/vzctl.py +137 -0
- pyinfra/operations/xbps.py +78 -0
- pyinfra/operations/yum.py +213 -0
- pyinfra/operations/zfs.py +176 -0
- pyinfra/operations/zypper.py +193 -0
- pyinfra/progress.py +44 -32
- pyinfra/py.typed +0 -0
- pyinfra/version.py +9 -1
- pyinfra-3.6.dist-info/METADATA +142 -0
- pyinfra-3.6.dist-info/RECORD +160 -0
- {pyinfra-0.11.dev3.dist-info → pyinfra-3.6.dist-info}/WHEEL +1 -2
- pyinfra-3.6.dist-info/entry_points.txt +12 -0
- {pyinfra-0.11.dev3.dist-info → pyinfra-3.6.dist-info/licenses}/LICENSE.md +1 -1
- pyinfra_cli/__init__.py +1 -0
- pyinfra_cli/cli.py +793 -0
- pyinfra_cli/commands.py +66 -0
- pyinfra_cli/exceptions.py +155 -65
- pyinfra_cli/inventory.py +233 -89
- pyinfra_cli/log.py +39 -43
- pyinfra_cli/main.py +26 -495
- pyinfra_cli/prints.py +215 -156
- pyinfra_cli/util.py +172 -105
- pyinfra_cli/virtualenv.py +25 -20
- pyinfra/api/connectors/__init__.py +0 -21
- pyinfra/api/connectors/ansible.py +0 -99
- pyinfra/api/connectors/docker.py +0 -178
- pyinfra/api/connectors/local.py +0 -169
- pyinfra/api/connectors/ssh.py +0 -402
- pyinfra/api/connectors/sshuserclient/client.py +0 -105
- pyinfra/api/connectors/sshuserclient/config.py +0 -90
- pyinfra/api/connectors/util.py +0 -63
- pyinfra/api/connectors/vagrant.py +0 -155
- pyinfra/facts/init.py +0 -176
- pyinfra/facts/util/files.py +0 -102
- pyinfra/hook.py +0 -41
- pyinfra/modules/__init__.py +0 -11
- pyinfra/modules/apk.py +0 -64
- pyinfra/modules/apt.py +0 -272
- pyinfra/modules/brew.py +0 -122
- pyinfra/modules/files.py +0 -711
- pyinfra/modules/gem.py +0 -30
- pyinfra/modules/git.py +0 -115
- pyinfra/modules/init.py +0 -344
- pyinfra/modules/iptables.py +0 -271
- pyinfra/modules/lxd.py +0 -45
- pyinfra/modules/mysql.py +0 -347
- pyinfra/modules/npm.py +0 -47
- pyinfra/modules/pacman.py +0 -60
- pyinfra/modules/pip.py +0 -99
- pyinfra/modules/pkg.py +0 -43
- pyinfra/modules/postgresql.py +0 -245
- pyinfra/modules/puppet.py +0 -20
- pyinfra/modules/python.py +0 -37
- pyinfra/modules/server.py +0 -524
- pyinfra/modules/ssh.py +0 -150
- pyinfra/modules/util/files.py +0 -52
- pyinfra/modules/util/packaging.py +0 -118
- pyinfra/modules/vzctl.py +0 -133
- pyinfra/modules/yum.py +0 -171
- pyinfra/pseudo_modules.py +0 -64
- pyinfra-0.11.dev3.dist-info/.DS_Store +0 -0
- pyinfra-0.11.dev3.dist-info/METADATA +0 -135
- pyinfra-0.11.dev3.dist-info/RECORD +0 -95
- pyinfra-0.11.dev3.dist-info/entry_points.txt +0 -3
- pyinfra-0.11.dev3.dist-info/top_level.txt +0 -2
- pyinfra_cli/__main__.py +0 -40
- pyinfra_cli/config.py +0 -92
- /pyinfra/{modules/util → connectors}/__init__.py +0 -0
- /pyinfra/{api/connectors → connectors}/sshuserclient/__init__.py +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage dnf packages and repositories. Note that dnf package names are case-sensitive.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pyinfra import host, state
|
|
8
|
+
from pyinfra.api import operation
|
|
9
|
+
from pyinfra.facts.rpm import RpmPackageProvides, RpmPackages
|
|
10
|
+
|
|
11
|
+
from .util.packaging import ensure_packages, ensure_rpm, ensure_yum_repo
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@operation(is_idempotent=False)
|
|
15
|
+
def key(src: str):
|
|
16
|
+
"""
|
|
17
|
+
Add dnf gpg keys with ``rpm``.
|
|
18
|
+
|
|
19
|
+
+ key: filename or URL
|
|
20
|
+
|
|
21
|
+
Note:
|
|
22
|
+
always returns one command, not idempotent
|
|
23
|
+
|
|
24
|
+
**Example:**
|
|
25
|
+
|
|
26
|
+
.. code:: python
|
|
27
|
+
|
|
28
|
+
from pyinfra import host
|
|
29
|
+
from pyinfra.operations import dnf
|
|
30
|
+
from pyinfra.facts.server import LinuxDistribution
|
|
31
|
+
linux_id = host.get_fact(LinuxDistribution)["release_meta"].get("ID")
|
|
32
|
+
dnf.key(
|
|
33
|
+
name="Add the Docker gpg key",
|
|
34
|
+
src=f"https://download.docker.com/linux/{linux_id}/gpg",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
yield "rpm --import {0}".format(src)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@operation()
|
|
43
|
+
def repo(
|
|
44
|
+
src: str,
|
|
45
|
+
present=True,
|
|
46
|
+
baseurl: str | None = None,
|
|
47
|
+
description: str | None = None,
|
|
48
|
+
enabled=True,
|
|
49
|
+
gpgcheck=True,
|
|
50
|
+
gpgkey: str | None = None,
|
|
51
|
+
):
|
|
52
|
+
# NOTE: if updating this docstring also update `yum.repo`
|
|
53
|
+
"""
|
|
54
|
+
Add/remove/update dnf repositories.
|
|
55
|
+
|
|
56
|
+
+ src: URL or name for the ``.repo`` file
|
|
57
|
+
+ present: whether the ``.repo`` file should be present
|
|
58
|
+
+ baseurl: the baseurl of the repo (if ``name`` is not a URL)
|
|
59
|
+
+ description: optional verbose description
|
|
60
|
+
+ enabled: whether this repo is enabled
|
|
61
|
+
+ gpgcheck: whether set ``gpgcheck=1``
|
|
62
|
+
+ gpgkey: the URL to the gpg key for this repo
|
|
63
|
+
|
|
64
|
+
``Baseurl``/``description``/``gpgcheck``/``gpgkey``:
|
|
65
|
+
These are only valid when ``name`` is a filename (ie not a URL). This is
|
|
66
|
+
for manual construction of repository files. Use a URL to download and
|
|
67
|
+
install remote repository files.
|
|
68
|
+
|
|
69
|
+
**Examples:**
|
|
70
|
+
|
|
71
|
+
.. code:: python
|
|
72
|
+
|
|
73
|
+
# Download a repository file
|
|
74
|
+
dnf.rpm(
|
|
75
|
+
name="Install Docker-CE repo via URL",
|
|
76
|
+
src="https://download.docker.com/linux/centos/docker-ce.repo",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Create the repository file from baseurl/etc
|
|
80
|
+
dnf.repo(
|
|
81
|
+
name="Add the Docker CentOS repo",
|
|
82
|
+
src="DockerCE",
|
|
83
|
+
baseurl="https://download.docker.com/linux/centos/7/$basearch/stable",
|
|
84
|
+
)
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
yield from ensure_yum_repo(
|
|
88
|
+
host,
|
|
89
|
+
src,
|
|
90
|
+
baseurl,
|
|
91
|
+
present,
|
|
92
|
+
description,
|
|
93
|
+
enabled,
|
|
94
|
+
gpgcheck,
|
|
95
|
+
gpgkey,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@operation()
|
|
100
|
+
def rpm(src: str, present=True):
|
|
101
|
+
# NOTE: if updating this docstring also update `yum.rpm`
|
|
102
|
+
"""
|
|
103
|
+
Add/remove ``.rpm`` file packages.
|
|
104
|
+
|
|
105
|
+
+ src: filename or URL of the ``.rpm`` package
|
|
106
|
+
+ present: whether ore not the package should exist on the system
|
|
107
|
+
|
|
108
|
+
URL sources with ``present=False``:
|
|
109
|
+
If the ``.rpm`` file isn't downloaded, pyinfra can't remove any existing
|
|
110
|
+
package as the file won't exist until mid-deploy.
|
|
111
|
+
|
|
112
|
+
**Example:**
|
|
113
|
+
|
|
114
|
+
.. code:: python
|
|
115
|
+
|
|
116
|
+
major_centos_version = host.get_fact(LinuxDistribution)["major"]
|
|
117
|
+
dnf.rpm(
|
|
118
|
+
name="Install EPEL rpm to enable EPEL repo",
|
|
119
|
+
src=f"https://dl.fedoraproject.org/pub/epel/epel-release-latest-{major_centos_version}.noarch.rpm",
|
|
120
|
+
)
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
yield from ensure_rpm(state, host, src, present, "dnf")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@operation(is_idempotent=False)
|
|
127
|
+
def update():
|
|
128
|
+
"""
|
|
129
|
+
Updates all dnf packages.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
yield "dnf update -y"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
_update = update._inner # noqa: E305 (for use below where update is a kwarg)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@operation()
|
|
139
|
+
def packages(
|
|
140
|
+
packages: str | list[str] | None = None,
|
|
141
|
+
present=True,
|
|
142
|
+
latest=False,
|
|
143
|
+
update=False,
|
|
144
|
+
clean=False,
|
|
145
|
+
nobest=False,
|
|
146
|
+
extra_install_args: str | None = None,
|
|
147
|
+
extra_uninstall_args: str | None = None,
|
|
148
|
+
):
|
|
149
|
+
"""
|
|
150
|
+
Install/remove/update dnf packages & updates.
|
|
151
|
+
|
|
152
|
+
+ packages: packages to ensure
|
|
153
|
+
+ present: whether the packages should be installed
|
|
154
|
+
+ latest: whether to upgrade packages without a specified version
|
|
155
|
+
+ update: run ``dnf update`` before installing packages
|
|
156
|
+
+ clean: run ``dnf clean`` before installing packages
|
|
157
|
+
+ nobest: add the no best option to install
|
|
158
|
+
+ extra_install_args: additional arguments to the dnf install command
|
|
159
|
+
+ extra_uninstall_args: additional arguments to the dnf uninstall command
|
|
160
|
+
|
|
161
|
+
Versions:
|
|
162
|
+
Package versions can be pinned as follows: ``<pkg>=<version>``
|
|
163
|
+
|
|
164
|
+
**Examples:**
|
|
165
|
+
|
|
166
|
+
.. code:: python
|
|
167
|
+
|
|
168
|
+
# Update package list and install packages
|
|
169
|
+
dnf.packages(
|
|
170
|
+
name='Install Vim and Vim enhanced',
|
|
171
|
+
packages=["vim-enhanced", "vim"],
|
|
172
|
+
update=True,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Install the latest versions of packages (always check)
|
|
176
|
+
dnf.packages(
|
|
177
|
+
name="Install latest Vim",
|
|
178
|
+
packages=["vim"],
|
|
179
|
+
latest=True,
|
|
180
|
+
)
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
if clean:
|
|
184
|
+
yield "dnf clean all"
|
|
185
|
+
|
|
186
|
+
if update:
|
|
187
|
+
yield from _update()
|
|
188
|
+
|
|
189
|
+
install_command = ["dnf", "install", "-y"]
|
|
190
|
+
|
|
191
|
+
if nobest:
|
|
192
|
+
install_command.append("--nobest")
|
|
193
|
+
|
|
194
|
+
if extra_install_args:
|
|
195
|
+
install_command.append(extra_install_args)
|
|
196
|
+
|
|
197
|
+
uninstall_command = ["dnf", "remove", "-y"]
|
|
198
|
+
|
|
199
|
+
if extra_uninstall_args:
|
|
200
|
+
uninstall_command.append(extra_uninstall_args)
|
|
201
|
+
|
|
202
|
+
yield from ensure_packages(
|
|
203
|
+
host,
|
|
204
|
+
packages,
|
|
205
|
+
host.get_fact(RpmPackages),
|
|
206
|
+
present,
|
|
207
|
+
install_command=" ".join(install_command),
|
|
208
|
+
uninstall_command=" ".join(uninstall_command),
|
|
209
|
+
upgrade_command="dnf update -y",
|
|
210
|
+
version_join="=",
|
|
211
|
+
latest=latest,
|
|
212
|
+
expand_package_fact=lambda package: host.get_fact(RpmPackageProvides, package=package),
|
|
213
|
+
)
|
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manager Docker containers, volumes and networks. These operations allow you to manage Docker from
|
|
3
|
+
the view of the current inventory host. See the :doc:`../connectors/docker` to use Docker containers
|
|
4
|
+
as inventory directly.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pyinfra import host
|
|
10
|
+
from pyinfra.api import operation
|
|
11
|
+
from pyinfra.facts.docker import (
|
|
12
|
+
DockerContainer,
|
|
13
|
+
DockerImage,
|
|
14
|
+
DockerNetwork,
|
|
15
|
+
DockerPlugin,
|
|
16
|
+
DockerVolume,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from .util.docker import ContainerSpec, handle_docker, parse_image_reference
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@operation()
|
|
23
|
+
def container(
|
|
24
|
+
container: str,
|
|
25
|
+
image: str = "",
|
|
26
|
+
ports: list[str] | None = None,
|
|
27
|
+
networks: list[str] | None = None,
|
|
28
|
+
volumes: list[str] | None = None,
|
|
29
|
+
env_vars: list[str] | None = None,
|
|
30
|
+
labels: list[str] | None = None,
|
|
31
|
+
pull_always: bool = False,
|
|
32
|
+
present: bool = True,
|
|
33
|
+
force: bool = False,
|
|
34
|
+
start: bool = True,
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
Manage Docker containers
|
|
38
|
+
|
|
39
|
+
+ container: name to identify the container
|
|
40
|
+
+ image: container image and tag ex: nginx:alpine
|
|
41
|
+
+ networks: network list to attach on container
|
|
42
|
+
+ ports: port list to expose
|
|
43
|
+
+ volumes: volume list to map on container
|
|
44
|
+
+ env_vars: environment variable list to inject on container
|
|
45
|
+
+ labels: Label list to attach to the container
|
|
46
|
+
+ pull_always: force image pull
|
|
47
|
+
+ force: remove a container with same name and create a new one
|
|
48
|
+
+ present: whether the container should be up and running
|
|
49
|
+
+ start: start or stop the container
|
|
50
|
+
|
|
51
|
+
**Examples:**
|
|
52
|
+
|
|
53
|
+
.. code:: python
|
|
54
|
+
|
|
55
|
+
from pyinfra.operations import docker
|
|
56
|
+
# Run a container
|
|
57
|
+
docker.container(
|
|
58
|
+
name="Deploy Nginx container",
|
|
59
|
+
container="nginx",
|
|
60
|
+
image="nginx:alpine",
|
|
61
|
+
ports=["80:80"],
|
|
62
|
+
present=True,
|
|
63
|
+
force=True,
|
|
64
|
+
networks=["proxy", "services"],
|
|
65
|
+
volumes=["nginx_data:/usr/share/nginx/html"],
|
|
66
|
+
pull_always=True,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Stop a container
|
|
70
|
+
docker.container(
|
|
71
|
+
name="Stop Nginx container",
|
|
72
|
+
container="nginx",
|
|
73
|
+
start=False,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Start a container
|
|
77
|
+
docker.container(
|
|
78
|
+
name="Start Nginx container",
|
|
79
|
+
container="nginx",
|
|
80
|
+
start=True,
|
|
81
|
+
)
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
want_spec = ContainerSpec(
|
|
85
|
+
image,
|
|
86
|
+
ports or list(),
|
|
87
|
+
networks or list(),
|
|
88
|
+
volumes or list(),
|
|
89
|
+
env_vars or list(),
|
|
90
|
+
labels or list(),
|
|
91
|
+
pull_always,
|
|
92
|
+
)
|
|
93
|
+
existent_container = host.get_fact(DockerContainer, object_id=container)
|
|
94
|
+
|
|
95
|
+
container_spec_changes = want_spec.diff_from_inspect(existent_container)
|
|
96
|
+
|
|
97
|
+
is_running = (
|
|
98
|
+
(existent_container[0]["State"]["Status"] == "running")
|
|
99
|
+
if existent_container and existent_container[0]
|
|
100
|
+
else False
|
|
101
|
+
)
|
|
102
|
+
recreating = existent_container and (force or container_spec_changes)
|
|
103
|
+
removing = existent_container and not present
|
|
104
|
+
|
|
105
|
+
do_remove = recreating or removing
|
|
106
|
+
do_create = (present and not existent_container) or recreating
|
|
107
|
+
do_start = start and (recreating or not is_running)
|
|
108
|
+
do_stop = not start and not removing and is_running
|
|
109
|
+
|
|
110
|
+
if do_remove:
|
|
111
|
+
yield handle_docker(
|
|
112
|
+
resource="container",
|
|
113
|
+
command="remove",
|
|
114
|
+
container=container,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if do_create:
|
|
118
|
+
yield handle_docker(
|
|
119
|
+
resource="container",
|
|
120
|
+
command="create",
|
|
121
|
+
container=container,
|
|
122
|
+
spec=want_spec,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
if do_start:
|
|
126
|
+
yield handle_docker(
|
|
127
|
+
resource="container",
|
|
128
|
+
command="start",
|
|
129
|
+
container=container,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if do_stop:
|
|
133
|
+
yield handle_docker(
|
|
134
|
+
resource="container",
|
|
135
|
+
command="stop",
|
|
136
|
+
container=container,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@operation()
|
|
141
|
+
def image(image: str, present: bool = True, force: bool = False):
|
|
142
|
+
"""
|
|
143
|
+
Manage Docker images
|
|
144
|
+
|
|
145
|
+
+ image: Image and tag ex: nginx:alpine
|
|
146
|
+
+ present: whether the Docker image should exist
|
|
147
|
+
+ force: always pull the image if present is True
|
|
148
|
+
|
|
149
|
+
**Examples:**
|
|
150
|
+
|
|
151
|
+
.. code:: python
|
|
152
|
+
|
|
153
|
+
# Pull a Docker image
|
|
154
|
+
docker.image(
|
|
155
|
+
name="Pull nginx image",
|
|
156
|
+
image="nginx:alpine",
|
|
157
|
+
present=True,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Remove a Docker image
|
|
161
|
+
docker.image(
|
|
162
|
+
name="Remove nginx image",
|
|
163
|
+
image:"nginx:image",
|
|
164
|
+
present=False,
|
|
165
|
+
)
|
|
166
|
+
"""
|
|
167
|
+
image_info = parse_image_reference(image)
|
|
168
|
+
if present:
|
|
169
|
+
if force:
|
|
170
|
+
# always pull the image if force is True
|
|
171
|
+
yield handle_docker(
|
|
172
|
+
resource="image",
|
|
173
|
+
command="pull",
|
|
174
|
+
image=image,
|
|
175
|
+
)
|
|
176
|
+
return
|
|
177
|
+
else:
|
|
178
|
+
existent_image = host.get_fact(DockerImage, object_id=image)
|
|
179
|
+
if image_info.digest:
|
|
180
|
+
# If a digest is specified, we must ensure the exact image is present
|
|
181
|
+
if existent_image:
|
|
182
|
+
host.noop(f"Image with digest {image_info.digest} already exists!")
|
|
183
|
+
else:
|
|
184
|
+
yield handle_docker(
|
|
185
|
+
resource="image",
|
|
186
|
+
command="pull",
|
|
187
|
+
image=image,
|
|
188
|
+
)
|
|
189
|
+
elif image_info.tag == "latest" or not image_info.tag:
|
|
190
|
+
# If the tag is 'latest' or not specified, always pull to ensure freshness
|
|
191
|
+
yield handle_docker(
|
|
192
|
+
resource="image",
|
|
193
|
+
command="pull",
|
|
194
|
+
image=image,
|
|
195
|
+
)
|
|
196
|
+
else:
|
|
197
|
+
# For other tags, check if the image exists
|
|
198
|
+
if existent_image:
|
|
199
|
+
host.noop(f"Image with tag {image_info.tag} already exists!")
|
|
200
|
+
else:
|
|
201
|
+
yield handle_docker(
|
|
202
|
+
resource="image",
|
|
203
|
+
command="pull",
|
|
204
|
+
image=image,
|
|
205
|
+
)
|
|
206
|
+
else:
|
|
207
|
+
existent_image = host.get_fact(DockerImage, object_id=image)
|
|
208
|
+
if existent_image:
|
|
209
|
+
yield handle_docker(
|
|
210
|
+
resource="image",
|
|
211
|
+
command="remove",
|
|
212
|
+
image=image,
|
|
213
|
+
)
|
|
214
|
+
else:
|
|
215
|
+
host.noop("There is no {0} image!".format(image))
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@operation()
|
|
219
|
+
def volume(volume: str, driver: str = "", labels: list[str] | None = None, present: bool = True):
|
|
220
|
+
"""
|
|
221
|
+
Manage Docker volumes
|
|
222
|
+
|
|
223
|
+
+ volume: Volume name
|
|
224
|
+
+ driver: Docker volume storage driver
|
|
225
|
+
+ labels: Label list to attach in the volume
|
|
226
|
+
+ present: whether the Docker volume should exist
|
|
227
|
+
|
|
228
|
+
**Examples:**
|
|
229
|
+
|
|
230
|
+
.. code:: python
|
|
231
|
+
|
|
232
|
+
# Create a Docker volume
|
|
233
|
+
docker.volume(
|
|
234
|
+
name="Create nginx volume",
|
|
235
|
+
volume="nginx_data",
|
|
236
|
+
present=True
|
|
237
|
+
)
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
existent_volume = host.get_fact(DockerVolume, object_id=volume)
|
|
241
|
+
|
|
242
|
+
if present:
|
|
243
|
+
if existent_volume:
|
|
244
|
+
host.noop("Volume already exists!")
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
yield handle_docker(
|
|
248
|
+
resource="volume",
|
|
249
|
+
command="create",
|
|
250
|
+
volume=volume,
|
|
251
|
+
driver=driver,
|
|
252
|
+
labels=labels,
|
|
253
|
+
present=present,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
else:
|
|
257
|
+
if existent_volume is None:
|
|
258
|
+
host.noop("There is no {0} volume!".format(volume))
|
|
259
|
+
return
|
|
260
|
+
|
|
261
|
+
yield handle_docker(
|
|
262
|
+
resource="volume",
|
|
263
|
+
command="remove",
|
|
264
|
+
volume=volume,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@operation()
|
|
269
|
+
def network(
|
|
270
|
+
network: str,
|
|
271
|
+
driver: str = "",
|
|
272
|
+
gateway: str = "",
|
|
273
|
+
ip_range: str = "",
|
|
274
|
+
ipam_driver: str = "",
|
|
275
|
+
subnet: str = "",
|
|
276
|
+
scope: str = "",
|
|
277
|
+
aux_addresses: dict[str, str] | None = None,
|
|
278
|
+
opts: list[str] | None = None,
|
|
279
|
+
ipam_opts: list[str] | None = None,
|
|
280
|
+
labels: list[str] | None = None,
|
|
281
|
+
ingress: bool = False,
|
|
282
|
+
attachable: bool = False,
|
|
283
|
+
present: bool = True,
|
|
284
|
+
):
|
|
285
|
+
"""
|
|
286
|
+
Manage docker networks
|
|
287
|
+
|
|
288
|
+
+ network: Network name
|
|
289
|
+
+ driver: Network driver ex: bridge or overlay
|
|
290
|
+
+ gateway: IPv4 or IPv6 Gateway for the master subnet
|
|
291
|
+
+ ip_range: Allocate container ip from a sub-range
|
|
292
|
+
+ ipam_driver: IP Address Management Driver
|
|
293
|
+
+ subnet: Subnet in CIDR format that represents a network segment
|
|
294
|
+
+ scope: Control the network's scope
|
|
295
|
+
+ aux_addresses: named aux addresses for the network
|
|
296
|
+
+ opts: Set driver specific options
|
|
297
|
+
+ ipam_opts: Set IPAM driver specific options
|
|
298
|
+
+ labels: Label list to attach in the network
|
|
299
|
+
+ ingress: Create swarm routing-mesh network
|
|
300
|
+
+ attachable: Enable manual container attachment
|
|
301
|
+
+ present: whether the Docker network should exist
|
|
302
|
+
|
|
303
|
+
**Examples:**
|
|
304
|
+
|
|
305
|
+
.. code:: python
|
|
306
|
+
|
|
307
|
+
# Create Docker network
|
|
308
|
+
docker.network(
|
|
309
|
+
network="nginx",
|
|
310
|
+
attachable=True,
|
|
311
|
+
present=True,
|
|
312
|
+
)
|
|
313
|
+
"""
|
|
314
|
+
existent_network = host.get_fact(DockerNetwork, object_id=network)
|
|
315
|
+
|
|
316
|
+
if present:
|
|
317
|
+
if existent_network:
|
|
318
|
+
host.noop("Network {0} already exists!".format(network))
|
|
319
|
+
return
|
|
320
|
+
|
|
321
|
+
yield handle_docker(
|
|
322
|
+
resource="network",
|
|
323
|
+
command="create",
|
|
324
|
+
network=network,
|
|
325
|
+
driver=driver,
|
|
326
|
+
gateway=gateway,
|
|
327
|
+
ip_range=ip_range,
|
|
328
|
+
ipam_driver=ipam_driver,
|
|
329
|
+
subnet=subnet,
|
|
330
|
+
scope=scope,
|
|
331
|
+
aux_addresses=aux_addresses,
|
|
332
|
+
opts=opts,
|
|
333
|
+
ipam_opts=ipam_opts,
|
|
334
|
+
labels=labels,
|
|
335
|
+
ingress=ingress,
|
|
336
|
+
attachable=attachable,
|
|
337
|
+
present=present,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
else:
|
|
341
|
+
if existent_network is None:
|
|
342
|
+
host.noop("Network {0} does not exist!".format(network))
|
|
343
|
+
return
|
|
344
|
+
|
|
345
|
+
yield handle_docker(
|
|
346
|
+
resource="network",
|
|
347
|
+
command="remove",
|
|
348
|
+
network=network,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@operation(is_idempotent=False)
|
|
353
|
+
def prune(
|
|
354
|
+
all: bool = False,
|
|
355
|
+
volumes: bool = False,
|
|
356
|
+
filter: str = "",
|
|
357
|
+
):
|
|
358
|
+
"""
|
|
359
|
+
Execute a docker system prune.
|
|
360
|
+
|
|
361
|
+
+ all: Remove all unused images not just dangling ones
|
|
362
|
+
+ volumes: Prune anonymous volumes
|
|
363
|
+
+ filter: Provide filter values (e.g. "label=<key>=<value>" or "until=24h")
|
|
364
|
+
|
|
365
|
+
**Examples:**
|
|
366
|
+
|
|
367
|
+
.. code:: python
|
|
368
|
+
|
|
369
|
+
# Remove dangling images
|
|
370
|
+
docker.prune(
|
|
371
|
+
name="remove dangling images",
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
# Remove all images and volumes
|
|
375
|
+
docker.prune(
|
|
376
|
+
name="Remove all images and volumes",
|
|
377
|
+
all=True,
|
|
378
|
+
volumes=True,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
# Remove images older than 90 days
|
|
382
|
+
docker.prune(
|
|
383
|
+
name="Remove unused older than 90 days",
|
|
384
|
+
filter="until=2160h"
|
|
385
|
+
)
|
|
386
|
+
"""
|
|
387
|
+
|
|
388
|
+
yield handle_docker(
|
|
389
|
+
resource="system",
|
|
390
|
+
command="prune",
|
|
391
|
+
all=all,
|
|
392
|
+
volumes=volumes,
|
|
393
|
+
filter=filter,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
@operation()
|
|
398
|
+
def plugin(
|
|
399
|
+
plugin: str,
|
|
400
|
+
alias: str | None = None,
|
|
401
|
+
present: bool = True,
|
|
402
|
+
enabled: bool = True,
|
|
403
|
+
plugin_options: dict[str, str] | None = None,
|
|
404
|
+
):
|
|
405
|
+
"""
|
|
406
|
+
Manage Docker plugins
|
|
407
|
+
|
|
408
|
+
+ plugin: Plugin name
|
|
409
|
+
+ alias: Alias for the plugin (optional)
|
|
410
|
+
+ present: Whether the plugin should be installed
|
|
411
|
+
+ enabled: Whether the plugin should be enabled
|
|
412
|
+
+ plugin_options: Options to pass to the plugin
|
|
413
|
+
|
|
414
|
+
**Examples:**
|
|
415
|
+
|
|
416
|
+
.. code:: python
|
|
417
|
+
|
|
418
|
+
# Install and enable a Docker plugin
|
|
419
|
+
docker.plugin(
|
|
420
|
+
name="Install and enable a Docker plugin",
|
|
421
|
+
plugin="username/my-awesome-plugin:latest",
|
|
422
|
+
alias="my-plugin",
|
|
423
|
+
present=True,
|
|
424
|
+
enabled=True,
|
|
425
|
+
plugin_options={"option1": "value1", "option2": "value2"},
|
|
426
|
+
)
|
|
427
|
+
"""
|
|
428
|
+
plugin_name = alias if alias else plugin
|
|
429
|
+
existent_plugin = host.get_fact(DockerPlugin, object_id=plugin_name)
|
|
430
|
+
if existent_plugin:
|
|
431
|
+
existent_plugin = existent_plugin[0]
|
|
432
|
+
|
|
433
|
+
if present:
|
|
434
|
+
if existent_plugin:
|
|
435
|
+
plugin_options_different = (
|
|
436
|
+
plugin_options and existent_plugin["Settings"]["Env"] != plugin_options
|
|
437
|
+
)
|
|
438
|
+
if plugin_options_different:
|
|
439
|
+
# Update options on existing plugin
|
|
440
|
+
if existent_plugin["Enabled"]:
|
|
441
|
+
yield handle_docker(
|
|
442
|
+
resource="plugin",
|
|
443
|
+
command="disable",
|
|
444
|
+
plugin=plugin_name,
|
|
445
|
+
)
|
|
446
|
+
yield handle_docker(
|
|
447
|
+
resource="plugin",
|
|
448
|
+
command="set",
|
|
449
|
+
plugin=plugin_name,
|
|
450
|
+
enabled=enabled,
|
|
451
|
+
existent_options=existent_plugin["Settings"]["Env"],
|
|
452
|
+
required_options=plugin_options,
|
|
453
|
+
)
|
|
454
|
+
if enabled:
|
|
455
|
+
yield handle_docker(
|
|
456
|
+
resource="plugin",
|
|
457
|
+
command="enable",
|
|
458
|
+
plugin=plugin_name,
|
|
459
|
+
)
|
|
460
|
+
else:
|
|
461
|
+
# Options are the same, check if enabled state is different
|
|
462
|
+
if existent_plugin["Enabled"] == enabled:
|
|
463
|
+
host.noop(
|
|
464
|
+
f"Plugin '{plugin_name}' is already installed with the same options "
|
|
465
|
+
f"and {'enabled' if enabled else 'disabled'}."
|
|
466
|
+
)
|
|
467
|
+
return
|
|
468
|
+
else:
|
|
469
|
+
command = "enable" if enabled else "disable"
|
|
470
|
+
yield handle_docker(
|
|
471
|
+
resource="plugin",
|
|
472
|
+
command=command,
|
|
473
|
+
plugin=plugin_name,
|
|
474
|
+
)
|
|
475
|
+
else:
|
|
476
|
+
yield handle_docker(
|
|
477
|
+
resource="plugin",
|
|
478
|
+
command="install",
|
|
479
|
+
plugin=plugin,
|
|
480
|
+
alias=alias,
|
|
481
|
+
enabled=enabled,
|
|
482
|
+
plugin_options=plugin_options,
|
|
483
|
+
)
|
|
484
|
+
else:
|
|
485
|
+
if not existent_plugin:
|
|
486
|
+
host.noop(f"Plugin '{plugin_name}' is not installed.")
|
|
487
|
+
return
|
|
488
|
+
yield handle_docker(
|
|
489
|
+
resource="plugin",
|
|
490
|
+
command="remove",
|
|
491
|
+
plugin=plugin_name,
|
|
492
|
+
)
|