diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 20:03:01 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 20:03:01 +0000 |
commit | a453ac31f3428614cceb99027f8efbdb9258a40b (patch) | |
tree | f61f87408f32a8511cbd91799f9cececb53e0374 /collections-debian-merged/ansible_collections/containers | |
parent | Initial commit. (diff) | |
download | ansible-upstream.tar.xz ansible-upstream.zip |
Adding upstream version 2.10.7+merged+base+2.10.8+dfsg.upstream/2.10.7+merged+base+2.10.8+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'collections-debian-merged/ansible_collections/containers')
78 files changed, 14645 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/containers/podman/CHANGELOG.rst b/collections-debian-merged/ansible_collections/containers/podman/CHANGELOG.rst new file mode 100644 index 00000000..6352efb2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/CHANGELOG.rst @@ -0,0 +1,316 @@ +================================================ +Ansible Podman modules and plugins Release Notes +================================================ + +.. contents:: Topics + + +v1.4.1 +====== + +Release Summary +--------------- + +Bugfixes for podman container + +Bugfixes +-------- + +- podman_container - Convert gidmap to list for podman_container +- podman_container - Convert log-opts to dictionary and idempotent + +v1.4.0 +====== + +Release Summary +--------------- + +New modules and bugfixes, new network options + +Minor Changes +------------- + +- podman_container - Add log level for Podman in module +- podman_container - Add mac_address field to podman_container module +- podman_container - Add strict image compare with hashes +- podman_container - Improve compatibility with docker_container by adding aliases +- podman_container - Move containers logic to module utils +- podman_image - reuse existing results in present() +- podman_network - Add IPv6 to network +- podman_network - Add support of network options like MTU, VLAN +- podman_pod - Move pod logic to separate library + +Bugfixes +-------- + +- podman_container - Fix force restart option for containers +- podman_container - Fix idempotency for volume GID and UID +- podman_container - Fix no_hosts idempotency for newer version +- podman_container - Remove 'detach' when creating container +- podman_image - Fix doc defaults for podman_image +- podman_logout - Handle podman logout not logging out when logged in via different tool +- podman_network - Correct IP range example for podman_network + +New Modules +----------- + +- containers.podman.podman_containers - Manage multiple Podman containers at once +- containers.podman.podman_login_info - Get info about Podman logged in registries +- containers.podman.podman_logout - Log out with Podman from registries + +v1.3.2 +====== + +Release Summary +--------------- + +bugfixes + +Bugfixes +-------- + +- podman_container - Fix signals case for podman_container + +v1.3.1 +====== + +Release Summary +--------------- + +bugfixes + +Bugfixes +-------- + +- multiple modules - fix diff calculation for lower/upper cases +- podman_container - Add note about containerPort setting +- podman_container - Fix init option it's boolean not string +- podman_container - Remove pyyaml from requirements +- podman_network - Check if dnsname plugin installed for CNI +- podman_volume - Set options for a volume as list and fix idempotency + +v1.3.0 +====== + +Release Summary +--------------- + +New podman_network module and bugfixes + +Minor Changes +------------- + +- Create podman_network module for podman networks management + +Bugfixes +-------- + +- podman_volume - Fix return data from podman_volume module + +New Modules +----------- + +- containers.podman.podman_network - Manage Podman networks + +v1.2.0 +====== + +Release Summary +--------------- + +Add changelog file. + +Minor Changes +------------- + +- Add changelog file to collection. + +v1.1.4 +====== + +Release Summary +--------------- + +Pip install and minor fixes. + +Minor Changes +------------- + +- Add pip installation for podman collection. + +v1.1.3 +====== + +Release Summary +--------------- + +Idempotency fixes for podman containers. + +Bugfixes +-------- + +- podman_container - Fix idempotency for case with = in env +- podman_container - Fix issue with idempotency uts, ipc with pod + +v1.1.2 +====== + +Release Summary +--------------- + +Urgent fix for podman connection plugin. + +Bugfixes +-------- + +- podman_connection - Chown file for users when copy them to container + +v1.1.1 +====== + +Release Summary +--------------- + +New modules for volumes management. + +Minor Changes +------------- + +- Create podman_volume module for volumes management + +Bugfixes +-------- + +- podman_volume_info - Improve podman volume info tests with new module + +New Modules +----------- + +- containers.podman.podman_volume - Manage Podman volumes + +v1.1.0 +====== + +Release Summary +--------------- + +New modules for pods management. + +Minor Changes +------------- + +- Add podman pod and pod info modules + +Bugfixes +-------- + +- podman_container - Fix idempotency for networks and add tests + +New Modules +----------- + +- containers.podman.podman_pod - Manage Podman pods +- containers.podman.podman_pod_info - Retrieve information about Podman pods + +v1.0.5 +====== + +Release Summary +--------------- + +Idempotency and another bugfixes for podman connection plugin. + +Bugfixes +-------- + +- podman_connection - Add check for empty dir for podman connection mount +- podman_connection - Increase verbosity for mount failure messages +- podman_container - Improve idempotency for volumes with slashesAdd idempotency for ulimits and tests +- podman_container - Improve ports idempotency and support UDP + +v1.0.4 +====== + +Release Summary +--------------- + +Idempotency and Podman v2 fixes + +Bugfixes +-------- + +- podman_container - Add idempotency for ulimits and tests +- podman_container - Fix idempotency for podman > 2 versions + +v1.0.3 +====== + +Release Summary +--------------- + +Relicense under GPLv3 and clean up modules + +Minor Changes +------------- + +- Relicense under GPLv3 and clean up modules + +v1.0.2 +====== + +Release Summary +--------------- + +Idempotency fixes + +Bugfixes +-------- + +- podman_container - Add idempotency for existing local volumes + +v1.0.1 +====== + +Release Summary +--------------- + +Idempotency and images improvements + +Bugfixes +-------- + +- podman_container - Add inspect of image and user idempotency +- podman_image - Add option for tls_verify=false for images + +v1.0.0 +====== + +Release Summary +--------------- + +Initial release of collection with new modules + +Minor Changes +------------- + +- buildah_connection - add support of specific user +- buildah_connection - added Buildah connection rootless +- podman_connection - add user flags before container id in podman exec + +Bugfixes +-------- + +- buildah_connection - Fix buildah debug output for py2 +- podman_connection - Run pause=false w/o message condition +- podman_container - Add idempotency for user and stop signal +- podman_container - Fix idempotency issues with workdir and volumes +- podman_container - Fix image, healthcheck and other idempotency +- podman_container - Improve idempotency of podman_container in uts, ipc, networks, cpu_shares +- podman_image - only set changed=true if there is a new image +- podman_image - use correct option for remove_signatures flag + +New Modules +----------- + +- containers.podman.podman_container - Manage Podman containers +- containers.podman.podman_network_info module - Retrieve information about Podman networks diff --git a/collections-debian-merged/ansible_collections/containers/podman/CODE-OF-CONDUCT.md b/collections-debian-merged/ansible_collections/containers/podman/CODE-OF-CONDUCT.md new file mode 100644 index 00000000..3833bfad --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/CODE-OF-CONDUCT.md @@ -0,0 +1,3 @@ +## The Podman Ansible Collections Project Community Code of Conduct + +The Podman Ansible Collections project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/master/CODE-OF-CONDUCT.md). diff --git a/collections-debian-merged/ansible_collections/containers/podman/COPYING b/collections-debian-merged/ansible_collections/containers/podman/COPYING new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. diff --git a/collections-debian-merged/ansible_collections/containers/podman/FILES.json b/collections-debian-merged/ansible_collections/containers/podman/FILES.json new file mode 100644 index 00000000..d77f43ce --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/FILES.json @@ -0,0 +1,845 @@ +{ + "files": [ + { + "name": ".", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "setup.cfg", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "df77e31936daa4f2509685b9442e8518931651064d466d9f6e0b597ebf92d6cd", + "format": 1 + }, + { + "name": "galaxy.yml.in", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ba21f50b97b7f801dd811f96d4941327fccab34b13311e9bfcc5facfeec16999", + "format": 1 + }, + { + "name": "CHANGELOG.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2405397f51e1165e32f6143c192a63190ba6cd6fec2589a0ec8edf3cb884ea5b", + "format": 1 + }, + { + "name": "plugins", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/podman", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/podman/common.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3f05e994e92dbafccf88acbc4cd13c60502cac1d91d6d5e9541c209e381d2ce8", + "format": 1 + }, + { + "name": "plugins/module_utils/podman/podman_container_lib.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8730c3b385230d19c49a18c630e032c897fcf292ef17bd0c0026e8dd301b93ca", + "format": 1 + }, + { + "name": "plugins/module_utils/podman/podman_pod_lib.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7013f0aff8ce4bd19a256b513d4da891a334e1f242d483de0b703023eeaefd23", + "format": 1 + }, + { + "name": "plugins/module_utils/podman/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/connection", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/connection/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/connection/buildah.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4a3396273b074751c294160d2a72e106cf50a700254b458928409797d73d73f9", + "format": 1 + }, + { + "name": "plugins/connection/podman.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2156de75c958b386a6d680ea568f62c918b0dd3f3c2ffbc34dd5fc02fe24e961", + "format": 1 + }, + { + "name": "plugins/modules", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules/podman_image.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bb4317ba8c692f6f7a7b82b6dd12aecf1c58764f942cec52aaeb16e3ea6be74b", + "format": 1 + }, + { + "name": "plugins/modules/podman_network.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3b0d6e4e17de28057599dbaa4d2b643e96ff84a1b573e9513e79fe00233cdd56", + "format": 1 + }, + { + "name": "plugins/modules/podman_pod_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "66d47cc3b8116869afbc4c970529e6480cb7722862ac25f451292508f9cbd886", + "format": 1 + }, + { + "name": "plugins/modules/podman_containers.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8da4189ce3a6c1156b1e427130c27e3d918883bc29815ba4001299fb326546ca", + "format": 1 + }, + { + "name": "plugins/modules/podman_container_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "662767f6eb1a1284a77b4405c7dca2930faf4217da86026cccf05110e0e6320d", + "format": 1 + }, + { + "name": "plugins/modules/podman_logout.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e93b33f6350423f84574a347f33e94bd071162ef2b79bc0b8ec7b2e71f667164", + "format": 1 + }, + { + "name": "plugins/modules/podman_container.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5083df6679d0f401fc987e69777585ed3edd4d49c7a64e13d9c8cbdfa213fe14", + "format": 1 + }, + { + "name": "plugins/modules/podman_image_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "65047995f92d7dd14ae3c11a5deae3dd3fe916a8eacfea1094f0a4d702ad1380", + "format": 1 + }, + { + "name": "plugins/modules/podman_volume.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5382fcb5c1320bd9fac247984b98d3f9086dd03aa3f49328c299a3d54086c172", + "format": 1 + }, + { + "name": "plugins/modules/podman_volume_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "eb8cf9d38d438d24589d547458f4c0ba8341f687eb8a1ded30de1fc0b8933c83", + "format": 1 + }, + { + "name": "plugins/modules/podman_network_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9beb6e0c733ca304f38f95116dfd89569df21a68428039ef59ec9650314c5222", + "format": 1 + }, + { + "name": "plugins/modules/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/modules/podman_login_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a73d1481551913253f811d95984eec7c19e5ea91847f3fe381cde9176e74222f", + "format": 1 + }, + { + "name": "plugins/modules/podman_pod.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "32f847ca079e085fb43414221e9f1aa95cd7d13e20f526af665e54922817b283", + "format": 1 + }, + { + "name": "SECURITY.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b73dc04e41dbb3774a3244e8e40d13eb97d169caa3e1230a622e077d60c1edd9", + "format": 1 + }, + { + "name": "tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_info/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3f3f7888f8d4b02194b01ebfc162e12751a736bfa0c78dc5af2a9f4d933c87d6", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_image_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_image_info/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_image_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6844ce4970c7650072fd11d5c31a0323b8f868619f6038357ef7cae671665a7f", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2088981c37e04eba3c9a66e0801d4ea440ad3c987f9785611a1872b89bd0a55b", + "format": 1 + }, + { + "name": "tests/integration/targets/connection", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/connection/test_connection.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a0c126383c62ba38cf5688d282006c1bc4943d95927bb1e95d3db02a1180c03b", + "format": 1 + }, + { + "name": "tests/integration/targets/connection_buildah", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/connection_buildah/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "706de11cf3d424148cbe8060c09e0417ab24cb35d9b4fb7af47595d43722f9b3", + "format": 1 + }, + { + "name": "tests/integration/targets/connection_buildah/test_connection.inventory", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5012fc0a2af812b87b675f31cd605ecf8d80fe32667b6ed20ea51fe0c8685742", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_volume", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_volume/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_volume/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "37fe448f10fdc2ebf7537cafc0177d680aa4999f9924c092354fc625b28e905b", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_network_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_network_info/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_network_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a174d23da44b07628cd0328f7568cd40dc35f2203cb3694d90dcb9cef7c3d045", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_volume_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_volume_info/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_volume_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f9499fbf029050fa869b66c40f32297868fcf0e1d4962d67569ecfe87dd8adbb", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_pod_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_pod_info/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_pod_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "970e52c046870ea2407a8d3397a4648629445f551ff485a5875beed3a1382198", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_image", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_image/files", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_image/files/Containerfile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ec6bc9968ca494ec22fcb0bed27fc12ddf2241fea1fe58d0d8a4f0b33487b506", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_image/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_image/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bfaeb2c66cf4d6275a46f871060ed4bcfc67ef03ffb51df25f537465cfedbd72", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_logout", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_logout/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_logout/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2eb01bf694efdc5320cb8a897f91a42bc63c35f86001c7c45a96ab5ce6cce748", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_pod", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_pod/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_pod/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d66ff4f4f62398759fec5b771c23d6135fd6f6432f0b9fb3b6f3313b8d966dea", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_pod/tasks/root-pod.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dd8435b69bbf1d78ddcdda0e13a91b0f0afc66fb4158cf973b830fbaf2e37c8e", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_pod/tasks/net-pod.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "85291241762740e1984c2081ff345d4923ad7d5dbd54d3be9986951c5310ed1e", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_pod/tasks/network-tests.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0f181c4be5e5193fec17680d59e7800d889c9677a1614eb5e13f0bf286e4e42f", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_login_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_login_info/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_login_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a3fd41d974fd43741834251b14d4650e11d97600f59f3e29916e957ae5f8eab0", + "format": 1 + }, + { + "name": "tests/integration/targets/connection_podman", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/connection_podman/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0d90338274f3a8d2540a17265b3064918c8989a266fd16348cc1b17678d0d877", + "format": 1 + }, + { + "name": "tests/integration/targets/connection_podman/test_connection.inventory", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f75a535c614dbdf71e61eb605b4bd1e857bbec85ff3ae9da0eff6f138f1ae56d", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_network", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_network/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_network/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5a78be16b7f719d896b1f738c944bcf6bfa12abbf44898974b015fd908380c99", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_containers", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_containers/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_containers/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "72cb66e4c58b8f2f3003ae0b8ee4f6fd89edf21ac85a6edd9a3eea1a00001b07", + "format": 1 + }, + { + "name": "tests/integration/targets/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/files", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/files/Dockerfile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "afd12e628f7b02a9135c6aa80ee72c226a98bac5b76545b564c4ebd1ab56f772", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/files/start.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bb80ab18f5df29c7c77f8acea5285af4f0f4d1ddac161801d0ff26ac242c2d86", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/root-podman-network.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "164d3be94b787a04cb92878d4325f48b66d355112313b3739a0ccf54ee18ce9d", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2e1a16ec44234318280017bbdbc36394ca4cc360a19d501cc8a6d862bf4fd4b2", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/build_test_container.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "96fc295d3eefde4ac79d7b57cad36b2061f76ea11c8c61442737da8a5316a5d3", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/idem_all.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bf27db386dfc434fa6454393a16d868e435a6622b8de12a3601b026dc0ed651c", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/idem_stopsignal.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "543a6ce2e1a6fb59eb0723f77f41a5877980276d10b89a3e0fa032fa5a390c13", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/idem_pods.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ea839d720ca73f8ca2adfaa495dd2422e504bed7b55c667ef03e1c7c2e689755", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/idem_users.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1a48eed443ea3c636af09a6b4fc582a4cb5b54a9a9c89ed6e174270a3933b1ae", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/idem_workdir.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6b6bda6cff4560e2feba738e98cab6b4547f514b07d663653164561cfb2e9f3d", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/idem_labels.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "157287f51969989865cd7c1a34849fc5a9145e2f9ac283d306ce3077f082e0bf", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/idem_networks.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "96b0be29c3574812dd33e4e117a91819635b121c66a24ce5eeec2615d7d4280b", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/idem_volumes.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fb1c39f891db71f677c686783c7e32449bc2249d2cccf0083746c9d02acc06b1", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/root-podman.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "44535a061ffd8b6043430b4330041e1aaf255bc08daf4148b34fbc0df63ad42f", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/idem_ports.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8ae87843b586035a0d596f9bb531107d60f73a5caec7f67ad4c33269a3514f73", + "format": 1 + }, + { + "name": "tests/integration/targets/podman_container_idempotency/tasks/rootless-podman-network.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b629ff1c9a41aa5721f4018b8e8e75b01f867304ba3e34f1b9e39403cc38b33a", + "format": 1 + }, + { + "name": "tests/.gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b5726d3ec9335a09c124469eca039523847a6b0f08a083efaefd002b83326600", + "format": 1 + }, + { + "name": "tests/sanity", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.11.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a8d07f551872f701cc0e0bf370322ce927d42b4d2672232cc8d575f47a9b0f51", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.10.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a8d07f551872f701cc0e0bf370322ce927d42b4d2672232cc8d575f47a9b0f51", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.9.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f735f0fbd58c43ff739387278d2ea8983a6a97898c68c9fc82f75dff499ee135", + "format": 1 + }, + { + "name": "tests/sanity/requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "471c16a6346df1b74757306a436cbab143fe65c30e6f9cda1f5c7179d6012b73", + "format": 1 + }, + { + "name": "README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5b64357ef4e01867ec98b3e67294979e274a8f2c73c5edebb95678a7c25f4815", + "format": 1 + }, + { + "name": "CODE-OF-CONDUCT.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5b42ff686c8d61fc9879d2512a9fa01f8810a7274318e7952ad3322eeea02f11", + "format": 1 + }, + { + "name": "changelogs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "changelogs/config.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "72cc16b684890f4595ab75ffdd6f80f5ae19bc84cbb2d82606bc840eb7842209", + "format": 1 + }, + { + "name": "changelogs/changelog.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fc1f5607fed0a3d804d5ab8e904da83b4085fbdc97cf80f8328c98da400bb78a", + "format": 1 + }, + { + "name": "setup.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7478e980356fe29366647e7c7d6687b7da68d1ea763d4ca865a75ca9498db1c2", + "format": 1 + }, + { + "name": "COPYING", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986", + "format": 1 + } + ], + "format": 1 +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/containers/podman/MANIFEST.json b/collections-debian-merged/ansible_collections/containers/podman/MANIFEST.json new file mode 100644 index 00000000..398f0d5a --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/MANIFEST.json @@ -0,0 +1,35 @@ +{ + "collection_info": { + "namespace": "containers", + "name": "podman", + "version": "1.4.1", + "authors": [ + "Sagi Shnaidman <sshnaidm@redhat.com>", + "Ansible team" + ], + "readme": "README.md", + "tags": [ + "containers", + "podman", + "libpod" + ], + "description": "Podman container Ansible modules", + "license": [ + "GPL-3.0-or-later" + ], + "license_file": null, + "dependencies": {}, + "repository": "https://github.com/containers/ansible-podman-collections.git", + "documentation": "https://github.com/containers/ansible-podman-collections", + "homepage": "https://github.com/containers/ansible-podman-collections", + "issues": "https://github.com/containers/ansible-podman-collections/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc" + }, + "file_manifest_file": { + "name": "FILES.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "43a0dc3c62bd183588c40a607b7fcd8a232007b3f2786767c9d2cc494afe5cbf", + "format": 1 + }, + "format": 1 +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/containers/podman/README.md b/collections-debian-merged/ansible_collections/containers/podman/README.md new file mode 100644 index 00000000..a8b9b1f7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/README.md @@ -0,0 +1,105 @@ +[![GitHub Actions CI/CD build status — Collection test suite](https://github.com/containers/ansible-podman-collections/workflows/Collection%20build%20and%20tests/badge.svg?branch=master)](https://github.com/containers/ansible-podman-collections/actions?query=workflow%3A%22Collection%20build%20and%20tests) + +# Ansible Collection: containers.podman + +This repo hosts the `containers.podman` Ansible Collection. + +The collection includes the Podman container plugins to help the build and management of Podman containers. + +## Installation and Usage + +### Installing the Collection from Ansible Galaxy + +Before using the Podman collection, you need to install the collection with the `ansible-galaxy` CLI: + +`ansible-galaxy collection install containers.podman` + +You can also include it in a `requirements.yml` file and install it via +`ansible-galaxy collection install -r requirements.yml` using the format: + +```yaml +collections: +- name: containers.podman +``` + +or clone by your own: + +```bash +mkdir -p ~/.ansible/collections/ansible_collections/containers +git clone https://github.com/containers/ansible-podman-collections.git ~/.ansible/collections/ansible_collections/containers/podman +``` + +### Playbooks + +To use a module from Podman collection, please reference the full namespace, collection name, +and modules name that you want to use: + +```yaml +--- +- name: Using Podman collection + hosts: localhost + tasks: + - name: Run redis container + containers.podman.podman_container: + name: myredis + image: redis + command: redis-server --appendonly yes + state: present + recreate: yes + expose: + - 6379 + volumes_from: + - mydata +``` + +Or you can add full namespace and collection name in the `collections` element: + +```yaml +--- +- name: Using Podman collection + hosts: localhost + collections: + - containers.podman + tasks: + - name: Build and push an image using existing credentials + podman_image: + name: nginx + path: /path/to/build/dir + push: yes + push_args: + dest: quay.io/acme +``` + +## 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 +- Submit and review source code changes in Github pull requests +- Add new modules for Podman containers and images + +## Testing and Development + +If you want to develop new content for this collection or improve what is already +here, the easiest way to work on the collection is to clone it into one of the configured +[`COLLECTIONS_PATHS`](https://docs.ansible.com/ansible/latest/reference_appendices/config.html#collections-paths), +and work on it there. + +### Testing with `ansible-test` + +We use `ansible-test` for sanity. + +## More Information + +TBD + +## Communication + +Please submit Github issues for communication any issues. +You can ask Podman related questions on `#podman` channel of Ansible Podman questions +on `#ansible-podman` channel on Freenode IRC. + +## License + +GPL-3.0-or-later diff --git a/collections-debian-merged/ansible_collections/containers/podman/SECURITY.md b/collections-debian-merged/ansible_collections/containers/podman/SECURITY.md new file mode 100644 index 00000000..aa1f6c18 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/SECURITY.md @@ -0,0 +1,4 @@ +## Security and Disclosure Information Policy for the Podman Ansible Collections Project + +The Podman Ansible Collections Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/master/SECURITY.md) for the Containers Projects. + diff --git a/collections-debian-merged/ansible_collections/containers/podman/changelogs/changelog.yaml b/collections-debian-merged/ansible_collections/containers/podman/changelogs/changelog.yaml new file mode 100644 index 00000000..bf1f2a0d --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/changelogs/changelog.yaml @@ -0,0 +1,182 @@ +ancestor: null +releases: + 1.0.0: + changes: + bugfixes: + - buildah_connection - Fix buildah debug output for py2 + - podman_connection - Run pause=false w/o message condition + - podman_container - Add idempotency for user and stop signal + - podman_container - Fix idempotency issues with workdir and volumes + - podman_container - Fix image, healthcheck and other idempotency + - podman_container - Improve idempotency of podman_container in uts, ipc, networks, + cpu_shares + - podman_image - only set changed=true if there is a new image + - podman_image - use correct option for remove_signatures flag + minor_changes: + - buildah_connection - add support of specific user + - buildah_connection - added Buildah connection rootless + - podman_connection - add user flags before container id in podman exec + release_summary: Initial release of collection with new modules + modules: + - description: Manage Podman containers + name: podman_container + namespace: '' + - description: Retrieve information about Podman networks + name: podman_network_info module + namespace: '' + release_date: '2020-05-20' + 1.0.1: + changes: + bugfixes: + - podman_container - Add inspect of image and user idempotency + - podman_image - Add option for tls_verify=false for images + release_summary: Idempotency and images improvements + release_date: '2020-06-01' + 1.0.2: + changes: + bugfixes: + - podman_container - Add idempotency for existing local volumes + release_summary: Idempotency fixes + release_date: '2020-06-05' + 1.0.3: + changes: + minor_changes: + - Relicense under GPLv3 and clean up modules + release_summary: Relicense under GPLv3 and clean up modules + release_date: '2020-06-08' + 1.0.4: + changes: + bugfixes: + - podman_container - Add idempotency for ulimits and tests + - podman_container - Fix idempotency for podman > 2 versions + release_summary: Idempotency and Podman v2 fixes + release_date: '2020-06-29' + 1.0.5: + changes: + bugfixes: + - podman_connection - Add check for empty dir for podman connection mount + - podman_connection - Increase verbosity for mount failure messages + - podman_container - Improve idempotency for volumes with slashesAdd idempotency + for ulimits and tests + - podman_container - Improve ports idempotency and support UDP + release_summary: Idempotency and another bugfixes for podman connection plugin. + release_date: '2020-07-09' + 1.1.0: + changes: + bugfixes: + - podman_container - Fix idempotency for networks and add tests + minor_changes: + - Add podman pod and pod info modules + release_summary: New modules for pods management. + modules: + - description: Manage Podman pods + name: podman_pod + namespace: '' + - description: Retrieve information about Podman pods + name: podman_pod_info + namespace: '' + release_date: '2020-07-19' + 1.1.1: + changes: + bugfixes: + - podman_volume_info - Improve podman volume info tests with new module + minor_changes: + - Create podman_volume module for volumes management + release_summary: New modules for volumes management. + modules: + - description: Manage Podman volumes + name: podman_volume + namespace: '' + release_date: '2020-07-22' + 1.1.2: + changes: + bugfixes: + - podman_connection - Chown file for users when copy them to container + release_summary: Urgent fix for podman connection plugin. + release_date: '2020-07-26' + 1.1.3: + changes: + bugfixes: + - podman_container - Fix idempotency for case with = in env + - podman_container - Fix issue with idempotency uts, ipc with pod + release_summary: Idempotency fixes for podman containers. + release_date: '2020-07-29' + 1.1.4: + changes: + minor_changes: + - Add pip installation for podman collection. + release_summary: Pip install and minor fixes. + release_date: '2020-08-06' + 1.2.0: + changes: + minor_changes: + - Add changelog file to collection. + release_summary: Add changelog file. + release_date: '2020-08-17' + 1.3.0: + changes: + bugfixes: + - podman_volume - Fix return data from podman_volume module + minor_changes: + - Create podman_network module for podman networks management + release_summary: New podman_network module and bugfixes + modules: + - description: Manage Podman networks + name: podman_network + namespace: '' + release_date: '2020-09-03' + 1.3.1: + changes: + bugfixes: + - multiple modules - fix diff calculation for lower/upper cases + - podman_container - Add note about containerPort setting + - podman_container - Fix init option it's boolean not string + - podman_container - Remove pyyaml from requirements + - podman_network - Check if dnsname plugin installed for CNI + - podman_volume - Set options for a volume as list and fix idempotency + release_summary: bugfixes + release_date: '2020-10-09' + 1.3.2: + changes: + bugfixes: + - podman_container - Fix signals case for podman_container + release_summary: bugfixes + release_date: '2020-10-20' + 1.4.0: + changes: + bugfixes: + - podman_container - Fix force restart option for containers + - podman_logout - Handle podman logout not logging out when logged in via different tool + - podman_network - Correct IP range example for podman_network + - podman_image - Fix doc defaults for podman_image + - podman_container - Remove 'detach' when creating container + - podman_container - Fix no_hosts idempotency for newer version + - podman_container - Fix idempotency for volume GID and UID + minor_changes: + - podman_container - Add log level for Podman in module + - podman_container - Add strict image compare with hashes + - podman_container - Move containers logic to module utils + - podman_pod - Move pod logic to separate library + - podman_container - Improve compatibility with docker_container by adding aliases + - podman_image - reuse existing results in present() + - podman_container - Add mac_address field to podman_container module + - podman_network - Add IPv6 to network + - podman_network - Add support of network options like MTU, VLAN + release_summary: New modules and bugfixes, new network options + modules: + - description: Log out with Podman from registries + name: podman_logout + namespace: '' + - description: Get info about Podman logged in registries + name: podman_login_info + namespace: '' + - description: Manage multiple Podman containers at once + name: podman_containers + namespace: '' + release_date: '2020-09-03' + 1.4.1: + changes: + bugfixes: + - podman_container - Convert gidmap to list for podman_container + - podman_container - Convert log-opts to dictionary and idempotent + release_summary: Bugfixes for podman container diff --git a/collections-debian-merged/ansible_collections/containers/podman/changelogs/config.yaml b/collections-debian-merged/ansible_collections/containers/podman/changelogs/config.yaml new file mode 100644 index 00000000..39c40f1e --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/changelogs/config.yaml @@ -0,0 +1,31 @@ +changelog_filename_template: ../CHANGELOG.rst +changelog_filename_version_depth: 0 +changes_file: changelog.yaml +changes_format: combined +ignore_other_fragment_extensions: true +keep_fragments: false +mention_ancestor: true +new_plugins_after_name: removed_features +notesdir: fragments +prelude_section_name: release_summary +prelude_section_title: Release Summary +sections: +- - major_changes + - Major Changes +- - minor_changes + - Minor Changes +- - breaking_changes + - Breaking Changes / Porting Guide +- - deprecated_features + - Deprecated Features +- - removed_features + - Removed Features (previously deprecated) +- - security_fixes + - Security Fixes +- - bugfixes + - Bugfixes +- - known_issues + - Known Issues +title: Ansible Podman modules and plugins +trivial_section_name: trivial +use_fqcn: true diff --git a/collections-debian-merged/ansible_collections/containers/podman/galaxy.yml.in b/collections-debian-merged/ansible_collections/containers/podman/galaxy.yml.in new file mode 100644 index 00000000..9c40e3f4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/galaxy.yml.in @@ -0,0 +1,30 @@ +namespace: containers +name: podman +# version: +readme: README.md +authors: + - Sagi Shnaidman <sshnaidm@redhat.com> + - Ansible team +description: Podman container Ansible modules +license: GPL-3.0-or-later +tags: + - containers + - podman + - libpod +dependencies: {} +repository: https://github.com/containers/ansible-podman-collections.git +documentation: https://github.com/containers/ansible-podman-collections +homepage: https://github.com/containers/ansible-podman-collections +issues: https://github.com/containers/ansible-podman-collections/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc +build_ignore: + - ci + - tests/output + - build_artifact + - "*.tar.gz" + - ".gitignore" + - ".history" + - ".vscode" + - ".idea" + - ".github" + - contrib + - importer_result.json diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/connection/__init__.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/connection/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/connection/__init__.py diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/connection/buildah.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/connection/buildah.py new file mode 100644 index 00000000..748c0ec7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/connection/buildah.py @@ -0,0 +1,202 @@ +# Based on the docker connection plugin +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +# Connection plugin for building container images using buildah tool +# https://github.com/projectatomic/buildah +# +# Written by: Tomas Tomecek (https://github.com/TomasTomecek) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' + connection: buildah + short_description: Interact with an existing buildah container + description: + - Run commands or put/fetch files to an existing container using buildah tool. + author: Tomas Tomecek (ttomecek@redhat.com) + options: + remote_addr: + description: + - The ID of the container you want to access. + default: inventory_hostname + vars: + - name: ansible_host +# keyword: +# - name: hosts + remote_user: + description: + - User specified via name or ID which is used to execute commands inside the container. + ini: + - section: defaults + key: remote_user + env: + - name: ANSIBLE_REMOTE_USER + vars: + - name: ansible_user +# keyword: +# - name: remote_user +''' + +import os +import shlex +import shutil +import subprocess + +from ansible.errors import AnsibleError +from ansible.module_utils._text import to_bytes, to_native, to_text +from ansible.plugins.connection import ConnectionBase, ensure_connect +from ansible.utils.display import Display + +display = Display() + + +# this _has to be_ named Connection +class Connection(ConnectionBase): + """ + This is a connection plugin for buildah: it uses buildah binary to interact with the containers + """ + + # String used to identify this Connection class from other classes + transport = 'containers.podman.buildah' + has_pipelining = True + + def __init__(self, play_context, new_stdin, *args, **kwargs): + super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) + + self._container_id = self._play_context.remote_addr + self._connected = False + # container filesystem will be mounted here on host + self._mount_point = None + # `buildah inspect` doesn't contain info about what the default user is -- if it's not + # set, it's empty + self.user = self._play_context.remote_user + display.vvvv("Using buildah connection from collection") + + def _set_user(self): + self._buildah(b"config", [b"--user=" + to_bytes(self.user, errors='surrogate_or_strict')]) + + def _buildah(self, cmd, cmd_args=None, in_data=None, outfile_stdout=None): + """ + run buildah executable + + :param cmd: buildah's command to execute (str) + :param cmd_args: list of arguments to pass to the command (list of str/bytes) + :param in_data: data passed to buildah's stdin + :param outfile_stdout: file for writing STDOUT to + :return: return code, stdout, stderr + """ + buildah_exec = 'buildah' + local_cmd = [buildah_exec] + + if isinstance(cmd, str): + local_cmd.append(cmd) + else: + local_cmd.extend(cmd) + if self.user and self.user != 'root': + if cmd == 'run': + local_cmd.extend(("--user", self.user)) + elif cmd == 'copy': + local_cmd.extend(("--chown", self.user)) + local_cmd.append(self._container_id) + + if cmd_args: + if isinstance(cmd_args, str): + local_cmd.append(cmd_args) + else: + local_cmd.extend(cmd_args) + + local_cmd = [to_bytes(i, errors='surrogate_or_strict') + for i in local_cmd] + + display.vvv("RUN %s" % (local_cmd,), host=self._container_id) + if outfile_stdout: + stdout_fd = open(outfile_stdout, "wb") + else: + stdout_fd = subprocess.PIPE + p = subprocess.Popen(local_cmd, shell=False, stdin=subprocess.PIPE, + stdout=stdout_fd, stderr=subprocess.PIPE) + + stdout, stderr = p.communicate(input=in_data) + display.vvvv("STDOUT %s" % to_text(stdout)) + display.vvvv("STDERR %s" % to_text(stderr)) + display.vvvv("RC CODE %s" % p.returncode) + stdout = to_bytes(stdout, errors='surrogate_or_strict') + stderr = to_bytes(stderr, errors='surrogate_or_strict') + return p.returncode, stdout, stderr + + def _connect(self): + """ + no persistent connection is being maintained, mount container's filesystem + so we can easily access it + """ + super(Connection, self)._connect() + rc, self._mount_point, stderr = self._buildah("mount") + if rc != 0: + display.v("Failed to mount container %s: %s" % (self._container_id, stderr.strip())) + else: + self._mount_point = self._mount_point.strip() + to_bytes(os.path.sep, errors='surrogate_or_strict') + display.vvvv("MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr)) + self._connected = True + + @ensure_connect + def exec_command(self, cmd, in_data=None, sudoable=False): + """ run specified command in a running OCI container using buildah """ + super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) + + # shlex.split has a bug with text strings on Python-2.6 and can only handle text strings on Python-3 + cmd_args_list = shlex.split(to_native(cmd, errors='surrogate_or_strict')) + + rc, stdout, stderr = self._buildah("run", cmd_args_list, in_data) + + display.vvvv("STDOUT %r\nSTDERR %r" % (stderr, stderr)) + return rc, stdout, stderr + + def put_file(self, in_path, out_path): + """ Place a local file located in 'in_path' inside container at 'out_path' """ + super(Connection, self).put_file(in_path, out_path) + display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._container_id) + if not self._mount_point or self.user: + rc, stdout, stderr = self._buildah( + "copy", [in_path, out_path]) + if rc != 0: + raise AnsibleError( + "Failed to copy file from %s to %s in container %s\n%s" % ( + in_path, out_path, self._container_id, stderr) + ) + else: + real_out_path = self._mount_point + to_bytes(out_path, errors='surrogate_or_strict') + shutil.copyfile( + to_bytes(in_path, errors='surrogate_or_strict'), + to_bytes(real_out_path, errors='surrogate_or_strict') + ) + + def fetch_file(self, in_path, out_path): + """ obtain file specified via 'in_path' from the container and place it at 'out_path' """ + super(Connection, self).fetch_file(in_path, out_path) + display.vvv("FETCH %s TO %s" % + (in_path, out_path), host=self._container_id) + if not self._mount_point: + rc, stdout, stderr = self._buildah( + "run", + ["cat", to_bytes(in_path, errors='surrogate_or_strict')], + outfile_stdout=out_path) + if rc != 0: + raise AnsibleError("Failed to fetch file from %s to %s from container %s\n%s" % ( + in_path, out_path, self._container_id, stderr)) + else: + real_in_path = self._mount_point + \ + to_bytes(in_path, errors='surrogate_or_strict') + shutil.copyfile( + to_bytes(real_in_path, errors='surrogate_or_strict'), + to_bytes(out_path, errors='surrogate_or_strict') + ) + + def close(self): + """ unmount container's filesystem """ + super(Connection, self).close() + rc, stdout, stderr = self._buildah("umount") + display.vvvv("RC %s STDOUT %r STDERR %r" % (rc, stdout, stderr)) + self._connected = False diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/connection/podman.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/connection/podman.py new file mode 100644 index 00000000..2e46bd9c --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/connection/podman.py @@ -0,0 +1,224 @@ +# Based on the buildah connection plugin +# Copyright (c) 2018 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +# Connection plugin to interact with existing podman containers. +# https://github.com/containers/libpod +# +# Written by: Tomas Tomecek (https://github.com/TomasTomecek) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import distutils.spawn +import os +import shlex +import shutil +import subprocess + +from ansible.errors import AnsibleError +from ansible.module_utils._text import to_bytes, to_native +from ansible.plugins.connection import ConnectionBase, ensure_connect +from ansible.utils.display import Display + +display = Display() + + +DOCUMENTATION = ''' + author: Tomas Tomecek (ttomecek@redhat.com) + connection: podman + short_description: Interact with an existing podman container + description: + - Run commands or put/fetch files to an existing container using podman tool. + options: + remote_addr: + description: + - The ID of the container you want to access. + default: inventory_hostname + vars: + - name: ansible_host + remote_user: + description: + - User specified via name or UID which is used to execute commands inside the container. If you + specify the user via UID, you must set C(ANSIBLE_REMOTE_TMP) to a path that exits + inside the container and is writable by Ansible. + ini: + - section: defaults + key: remote_user + env: + - name: ANSIBLE_REMOTE_USER + vars: + - name: ansible_user + podman_extra_args: + description: + - Extra arguments to pass to the podman command line. + default: '' + ini: + - section: defaults + key: podman_extra_args + vars: + - name: ansible_podman_extra_args + env: + - name: ANSIBLE_PODMAN_EXTRA_ARGS + podman_executable: + description: + - Executable for podman command. + default: podman + vars: + - name: ansible_podman_executable + env: + - name: ANSIBLE_PODMAN_EXECUTABLE +''' + + +# this _has to be_ named Connection +class Connection(ConnectionBase): + """ + This is a connection plugin for podman. It uses podman binary to interact with the containers + """ + + # String used to identify this Connection class from other classes + transport = 'containers.podman.podman' + has_pipelining = True + + def __init__(self, play_context, new_stdin, *args, **kwargs): + super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) + + self._container_id = self._play_context.remote_addr + self._connected = False + # container filesystem will be mounted here on host + self._mount_point = None + self.user = self._play_context.remote_user + display.vvvv("Using podman connection from collection") + + def _podman(self, cmd, cmd_args=None, in_data=None, use_container_id=True): + """ + run podman executable + + :param cmd: podman's command to execute (str or list) + :param cmd_args: list of arguments to pass to the command (list of str/bytes) + :param in_data: data passed to podman's stdin + :return: return code, stdout, stderr + """ + podman_exec = self.get_option('podman_executable') + podman_cmd = distutils.spawn.find_executable(podman_exec) + if not podman_cmd: + raise AnsibleError("%s command not found in PATH" % podman_exec) + local_cmd = [podman_cmd] + if self.get_option('podman_extra_args'): + local_cmd += shlex.split( + to_native( + self.get_option('podman_extra_args'), + errors='surrogate_or_strict')) + if isinstance(cmd, str): + local_cmd.append(cmd) + else: + local_cmd.extend(cmd) + + if use_container_id: + local_cmd.append(self._container_id) + if cmd_args: + local_cmd += cmd_args + local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd] + + display.vvv("RUN %s" % (local_cmd,), host=self._container_id) + p = subprocess.Popen(local_cmd, shell=False, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + stdout, stderr = p.communicate(input=in_data) + display.vvvvv("STDOUT %s" % stdout) + display.vvvvv("STDERR %s" % stderr) + display.vvvvv("RC CODE %s" % p.returncode) + stdout = to_bytes(stdout, errors='surrogate_or_strict') + stderr = to_bytes(stderr, errors='surrogate_or_strict') + return p.returncode, stdout, stderr + + def _connect(self): + """ + no persistent connection is being maintained, mount container's filesystem + so we can easily access it + """ + super(Connection, self)._connect() + rc, self._mount_point, stderr = self._podman("mount") + if rc != 0: + display.vvvv("Failed to mount container %s: %s" % (self._container_id, stderr.strip())) + elif not os.listdir(self._mount_point.strip()): + display.vvvv("Failed to mount container with CGroups2: empty dir %s" % self._mount_point.strip()) + self._mount_point = None + else: + self._mount_point = self._mount_point.strip() + display.vvvvv("MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr)) + self._connected = True + + @ensure_connect + def exec_command(self, cmd, in_data=None, sudoable=False): + """ run specified command in a running OCI container using podman """ + super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) + + # shlex.split has a bug with text strings on Python-2.6 and can only handle text strings on Python-3 + cmd_args_list = shlex.split(to_native(cmd, errors='surrogate_or_strict')) + exec_args_list = ["exec"] + if self.user: + exec_args_list.extend(("--user", self.user)) + + rc, stdout, stderr = self._podman(exec_args_list, cmd_args_list, in_data) + + display.vvvvv("STDOUT %r STDERR %r" % (stderr, stderr)) + return rc, stdout, stderr + + def put_file(self, in_path, out_path): + """ Place a local file located in 'in_path' inside container at 'out_path' """ + super(Connection, self).put_file(in_path, out_path) + display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._container_id) + if not self._mount_point or self.user: + rc, stdout, stderr = self._podman( + "cp", [in_path, self._container_id + ":" + out_path], use_container_id=False + ) + if rc != 0: + rc, stdout, stderr = self._podman( + "cp", ["--pause=false", in_path, self._container_id + ":" + out_path], use_container_id=False + ) + if rc != 0: + raise AnsibleError( + "Failed to copy file from %s to %s in container %s\n%s" % ( + in_path, out_path, self._container_id, stderr) + ) + if self.user: + rc, stdout, stderr = self._podman( + "exec", ["chown", self.user, out_path]) + if rc != 0: + raise AnsibleError( + "Failed to chown file %s for user %s in container %s\n%s" % ( + out_path, self.user, self._container_id, stderr) + ) + else: + real_out_path = self._mount_point + to_bytes(out_path, errors='surrogate_or_strict') + shutil.copyfile( + to_bytes(in_path, errors='surrogate_or_strict'), + to_bytes(real_out_path, errors='surrogate_or_strict') + ) + + def fetch_file(self, in_path, out_path): + """ obtain file specified via 'in_path' from the container and place it at 'out_path' """ + super(Connection, self).fetch_file(in_path, out_path) + display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._container_id) + if not self._mount_point: + rc, stdout, stderr = self._podman( + "cp", [self._container_id + ":" + in_path, out_path], use_container_id=False) + if rc != 0: + raise AnsibleError("Failed to fetch file from %s to %s from container %s\n%s" % ( + in_path, out_path, self._container_id, stderr)) + else: + real_in_path = self._mount_point + to_bytes(in_path, errors='surrogate_or_strict') + shutil.copyfile( + to_bytes(real_in_path, errors='surrogate_or_strict'), + to_bytes(out_path, errors='surrogate_or_strict') + ) + + def close(self): + """ unmount container's filesystem """ + super(Connection, self).close() + # we actually don't need to unmount since the container is mounted anyway + # rc, stdout, stderr = self._podman("umount") + # display.vvvvv("RC %s STDOUT %r STDERR %r" % (rc, stdout, stderr)) + self._connected = False diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/__init__.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/__init__.py diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/__init__.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/__init__.py diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/common.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/common.py new file mode 100644 index 00000000..d7b0ed79 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/common.py @@ -0,0 +1,28 @@ +#!/usr/bin/python +# Copyright (c) 2020 Red Hat +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +def run_podman_command(module, executable='podman', args=None, expected_rc=0, ignore_errors=False): + if not isinstance(executable, list): + command = [executable] + if args is not None: + command.extend(args) + rc, out, err = module.run_command(command) + if not ignore_errors and rc != expected_rc: + module.fail_json( + msg='Failed to run {command} {args}: {err}'.format( + command=command, args=args, err=err)) + return rc, out, err + + +def lower_keys(x): + if isinstance(x, list): + return [lower_keys(v) for v in x] + elif isinstance(x, dict): + return dict((k.lower(), lower_keys(v)) for k, v in x.items()) + else: + return x diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/podman_container_lib.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/podman_container_lib.py new file mode 100644 index 00000000..b9fdc431 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/podman_container_lib.py @@ -0,0 +1,1479 @@ +from __future__ import (absolute_import, division, print_function) +import json # noqa: F402 +from distutils.version import LooseVersion # noqa: F402 + +from ansible.module_utils._text import to_bytes, to_native # noqa: F402 +from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys + +__metaclass__ = type + +ARGUMENTS_SPEC_CONTAINER = dict( + name=dict(required=True, type='str'), + executable=dict(default='podman', type='str'), + state=dict(type='str', default='started', choices=[ + 'absent', 'present', 'stopped', 'started']), + image=dict(type='str'), + annotation=dict(type='dict'), + authfile=dict(type='path'), + blkio_weight=dict(type='int'), + blkio_weight_device=dict(type='dict'), + cap_add=dict(type='list', elements='str', aliases=['capabilities']), + cap_drop=dict(type='list', elements='str'), + cgroup_parent=dict(type='path'), + cgroupns=dict(type='str'), + cgroups=dict(type='str', choices=['default', 'disabled']), + cidfile=dict(type='path'), + cmd_args=dict(type='list', elements='str'), + conmon_pidfile=dict(type='path'), + command=dict(type='raw'), + cpu_period=dict(type='int'), + cpu_rt_period=dict(type='int'), + cpu_rt_runtime=dict(type='int'), + cpu_shares=dict(type='int'), + cpus=dict(type='str'), + cpuset_cpus=dict(type='str'), + cpuset_mems=dict(type='str'), + detach=dict(type='bool', default=True), + debug=dict(type='bool', default=False), + detach_keys=dict(type='str'), + device=dict(type='list', elements='str'), + device_read_bps=dict(type='list'), + device_read_iops=dict(type='list'), + device_write_bps=dict(type='list'), + device_write_iops=dict(type='list'), + dns=dict(type='list', elements='str', aliases=['dns_servers']), + dns_option=dict(type='str', aliases=['dns_opts']), + dns_search=dict(type='str', aliases=['dns_search_domains']), + entrypoint=dict(type='str'), + env=dict(type='dict'), + env_file=dict(type='path'), + env_host=dict(type='bool'), + etc_hosts=dict(type='dict', aliases=['add_hosts']), + expose=dict(type='list', elements='str', aliases=[ + 'exposed', 'exposed_ports']), + force_restart=dict(type='bool', default=False, + aliases=['restart']), + gidmap=dict(type='list', elements='str'), + group_add=dict(type='list', aliases=['groups']), + healthcheck=dict(type='str'), + healthcheck_interval=dict(type='str'), + healthcheck_retries=dict(type='int'), + healthcheck_start_period=dict(type='str'), + healthcheck_timeout=dict(type='str'), + hostname=dict(type='str'), + http_proxy=dict(type='bool'), + image_volume=dict(type='str', choices=['bind', 'tmpfs', 'ignore']), + image_strict=dict(type='bool', default=False), + init=dict(type='bool'), + init_path=dict(type='str'), + interactive=dict(type='bool'), + ip=dict(type='str'), + ipc=dict(type='str', aliases=['ipc_mode']), + kernel_memory=dict(type='str'), + label=dict(type='dict', aliases=['labels']), + label_file=dict(type='str'), + log_driver=dict(type='str', choices=[ + 'k8s-file', 'journald', 'json-file']), + log_level=dict( + type='str', + choices=["debug", "info", "warn", "error", "fatal", "panic"]), + log_opt=dict(type='dict', aliases=['log_options'], + options=dict( + max_size=dict(type='str'), + path=dict(type='str'), + tag=dict(type='str'))), + mac_address=dict(type='str'), + memory=dict(type='str'), + memory_reservation=dict(type='str'), + memory_swap=dict(type='str'), + memory_swappiness=dict(type='int'), + mount=dict(type='str'), + network=dict(type='list', elements='str', aliases=['net', 'network_mode']), + no_hosts=dict(type='bool'), + oom_kill_disable=dict(type='bool'), + oom_score_adj=dict(type='int'), + pid=dict(type='str', aliases=['pid_mode']), + pids_limit=dict(type='str'), + pod=dict(type='str'), + privileged=dict(type='bool'), + publish=dict(type='list', elements='str', aliases=[ + 'ports', 'published', 'published_ports']), + publish_all=dict(type='bool'), + read_only=dict(type='bool'), + read_only_tmpfs=dict(type='bool'), + recreate=dict(type='bool', default=False), + restart_policy=dict(type='str'), + rm=dict(type='bool', aliases=['remove', 'auto_remove']), + rootfs=dict(type='bool'), + security_opt=dict(type='list', elements='str'), + shm_size=dict(type='str'), + sig_proxy=dict(type='bool'), + stop_signal=dict(type='int'), + stop_timeout=dict(type='int'), + subgidname=dict(type='str'), + subuidname=dict(type='str'), + sysctl=dict(type='dict'), + systemd=dict(type='bool'), + tmpfs=dict(type='dict'), + tty=dict(type='bool'), + uidmap=dict(type='list', elements='str'), + ulimit=dict(type='list', aliases=['ulimits']), + user=dict(type='str'), + userns=dict(type='str', aliases=['userns_mode']), + uts=dict(type='str'), + volume=dict(type='list', elements='str', aliases=['volumes']), + volumes_from=dict(type='list', elements='str'), + workdir=dict(type='str', aliases=['working_dir']) +) + + +class PodmanModuleParams: + """Creates list of arguments for podman CLI command. + + Arguments: + action {str} -- action type from 'run', 'stop', 'create', 'delete', + 'start' + params {dict} -- dictionary of module parameters + + """ + + def __init__(self, action, params, podman_version, module): + self.params = params + self.action = action + self.podman_version = podman_version + self.module = module + + def construct_command_from_params(self): + """Create a podman command from given module parameters. + + Returns: + list -- list of byte strings for Popen command + """ + if self.action in ['start', 'stop', 'delete']: + return self.start_stop_delete() + if self.action in ['create', 'run']: + cmd = [self.action, '--name', self.params['name']] + all_param_methods = [func for func in dir(self) + if callable(getattr(self, func)) + and func.startswith("addparam")] + params_set = (i for i in self.params if self.params[i] is not None) + for param in params_set: + func_name = "_".join(["addparam", param]) + if func_name in all_param_methods: + cmd = getattr(self, func_name)(cmd) + cmd.append(self.params['image']) + if self.params['command']: + if isinstance(self.params['command'], list): + cmd += self.params['command'] + else: + cmd += self.params['command'].split() + return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + + def start_stop_delete(self): + + if self.action in ['stop', 'start']: + cmd = [self.action, self.params['name']] + return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + + if self.action == 'delete': + cmd = ['rm', '-f', self.params['name']] + return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + + def check_version(self, param, minv=None, maxv=None): + if minv and LooseVersion(minv) > LooseVersion( + self.podman_version): + self.module.fail_json(msg="Parameter %s is supported from podman " + "version %s only! Current version is %s" % ( + param, minv, self.podman_version)) + if maxv and LooseVersion(maxv) < LooseVersion( + self.podman_version): + self.module.fail_json(msg="Parameter %s is supported till podman " + "version %s only! Current version is %s" % ( + param, minv, self.podman_version)) + + def addparam_annotation(self, c): + for annotate in self.params['annotation'].items(): + c += ['--annotation', '='.join(annotate)] + return c + + def addparam_authfile(self, c): + return c + ['--authfile', self.params['authfile']] + + def addparam_blkio_weight(self, c): + return c + ['--blkio-weight', self.params['blkio_weight']] + + def addparam_blkio_weight_device(self, c): + for blkio in self.params['blkio_weight_device'].items(): + c += ['--blkio-weight-device', ':'.join(blkio)] + return c + + def addparam_cap_add(self, c): + for cap_add in self.params['cap_add']: + c += ['--cap-add', cap_add] + return c + + def addparam_cap_drop(self, c): + for cap_drop in self.params['cap_drop']: + c += ['--cap-drop', cap_drop] + return c + + def addparam_cgroups(self, c): + self.check_version('--cgroups', minv='1.6.0') + return c + ['--cgroups=%s' % self.params['cgroups']] + + def addparam_cgroupns(self, c): + self.check_version('--cgroupns', minv='1.6.2') + return c + ['--cgroupns=%s' % self.params['cgroupns']] + + def addparam_cgroup_parent(self, c): + return c + ['--cgroup-parent', self.params['cgroup_parent']] + + def addparam_cidfile(self, c): + return c + ['--cidfile', self.params['cidfile']] + + def addparam_conmon_pidfile(self, c): + return c + ['--conmon-pidfile', self.params['conmon_pidfile']] + + def addparam_cpu_period(self, c): + return c + ['--cpu-period', self.params['cpu_period']] + + def addparam_cpu_rt_period(self, c): + return c + ['--cpu-rt-period', self.params['cpu_rt_period']] + + def addparam_cpu_rt_runtime(self, c): + return c + ['--cpu-rt-runtime', self.params['cpu_rt_runtime']] + + def addparam_cpu_shares(self, c): + return c + ['--cpu-shares', self.params['cpu_shares']] + + def addparam_cpus(self, c): + return c + ['--cpus', self.params['cpus']] + + def addparam_cpuset_cpus(self, c): + return c + ['--cpuset-cpus', self.params['cpuset_cpus']] + + def addparam_cpuset_mems(self, c): + return c + ['--cpuset-mems', self.params['cpuset_mems']] + + def addparam_detach(self, c): + return c + ['--detach=%s' % self.params['detach']] + + def addparam_detach_keys(self, c): + return c + ['--detach-keys', self.params['detach_keys']] + + def addparam_device(self, c): + for dev in self.params['device']: + c += ['--device', dev] + return c + + def addparam_device_read_bps(self, c): + for dev in self.params['device_read_bps']: + c += ['--device-read-bps', dev] + return c + + def addparam_device_read_iops(self, c): + for dev in self.params['device_read_iops']: + c += ['--device-read-iops', dev] + return c + + def addparam_device_write_bps(self, c): + for dev in self.params['device_write_bps']: + c += ['--device-write-bps', dev] + return c + + def addparam_device_write_iops(self, c): + for dev in self.params['device_write_iops']: + c += ['--device-write-iops', dev] + return c + + def addparam_dns(self, c): + return c + ['--dns', ','.join(self.params['dns'])] + + def addparam_dns_option(self, c): + return c + ['--dns-option', self.params['dns_option']] + + def addparam_dns_search(self, c): + return c + ['--dns-search', self.params['dns_search']] + + def addparam_entrypoint(self, c): + return c + ['--entrypoint', self.params['entrypoint']] + + def addparam_env(self, c): + for env_value in self.params['env'].items(): + c += ['--env', + b"=".join([to_bytes(k, errors='surrogate_or_strict') + for k in env_value])] + return c + + def addparam_env_file(self, c): + return c + ['--env-file', self.params['env_file']] + + def addparam_env_host(self, c): + self.check_version('--env-host', minv='1.5.0') + return c + ['--env-host=%s' % self.params['env_host']] + + def addparam_etc_hosts(self, c): + for host_ip in self.params['etc_hosts'].items(): + c += ['--add-host', ':'.join(host_ip)] + return c + + def addparam_expose(self, c): + for exp in self.params['expose']: + c += ['--expose', exp] + return c + + def addparam_gidmap(self, c): + for gidmap in self.params['gidmap']: + c += ['--gidmap', gidmap] + return c + + def addparam_group_add(self, c): + for g in self.params['group_add']: + c += ['--group-add', g] + return c + + def addparam_healthcheck(self, c): + return c + ['--healthcheck-command', self.params['healthcheck']] + + def addparam_healthcheck_interval(self, c): + return c + ['--healthcheck-interval', + self.params['healthcheck_interval']] + + def addparam_healthcheck_retries(self, c): + return c + ['--healthcheck-retries', + self.params['healthcheck_retries']] + + def addparam_healthcheck_start_period(self, c): + return c + ['--healthcheck-start-period', + self.params['healthcheck_start_period']] + + def addparam_healthcheck_timeout(self, c): + return c + ['--healthcheck-timeout', + self.params['healthcheck_timeout']] + + def addparam_hostname(self, c): + return c + ['--hostname', self.params['hostname']] + + def addparam_http_proxy(self, c): + return c + ['--http-proxy=%s' % self.params['http_proxy']] + + def addparam_image_volume(self, c): + return c + ['--image-volume', self.params['image_volume']] + + def addparam_init(self, c): + if self.params['init']: + c += ['--init'] + return c + + def addparam_init_path(self, c): + return c + ['--init-path', self.params['init_path']] + + def addparam_interactive(self, c): + return c + ['--interactive=%s' % self.params['interactive']] + + def addparam_ip(self, c): + return c + ['--ip', self.params['ip']] + + def addparam_ipc(self, c): + return c + ['--ipc', self.params['ipc']] + + def addparam_kernel_memory(self, c): + return c + ['--kernel-memory', self.params['kernel_memory']] + + def addparam_label(self, c): + for label in self.params['label'].items(): + c += ['--label', b'='.join([to_bytes(l, errors='surrogate_or_strict') + for l in label])] + return c + + def addparam_label_file(self, c): + return c + ['--label-file', self.params['label_file']] + + def addparam_log_driver(self, c): + return c + ['--log-driver', self.params['log_driver']] + + def addparam_log_opt(self, c): + for k, v in self.params['log_opt'].items(): + if v is not None: + c += ['--log-opt', + b"=".join([to_bytes(k.replace('max_size', 'max-size'), + errors='surrogate_or_strict'), + to_bytes(v, + errors='surrogate_or_strict')])] + return c + + def addparam_log_level(self, c): + return c + ['--log-level', self.params['log_level']] + + def addparam_mac_address(self, c): + return c + ['--mac-address', self.params['mac_address']] + + def addparam_memory(self, c): + return c + ['--memory', self.params['memory']] + + def addparam_memory_reservation(self, c): + return c + ['--memory-reservation', self.params['memory_reservation']] + + def addparam_memory_swap(self, c): + return c + ['--memory-swap', self.params['memory_swap']] + + def addparam_memory_swappiness(self, c): + return c + ['--memory-swappiness', self.params['memory_swappiness']] + + def addparam_mount(self, c): + return c + ['--mount', self.params['mount']] + + def addparam_network(self, c): + return c + ['--network', ",".join(self.params['network'])] + + def addparam_no_hosts(self, c): + return c + ['--no-hosts=%s' % self.params['no_hosts']] + + def addparam_oom_kill_disable(self, c): + return c + ['--oom-kill-disable=%s' % self.params['oom_kill_disable']] + + def addparam_oom_score_adj(self, c): + return c + ['--oom-score-adj', self.params['oom_score_adj']] + + def addparam_pid(self, c): + return c + ['--pid', self.params['pid']] + + def addparam_pids_limit(self, c): + return c + ['--pids-limit', self.params['pids_limit']] + + def addparam_pod(self, c): + return c + ['--pod', self.params['pod']] + + def addparam_privileged(self, c): + return c + ['--privileged=%s' % self.params['privileged']] + + def addparam_publish(self, c): + for pub in self.params['publish']: + c += ['--publish', pub] + return c + + def addparam_publish_all(self, c): + return c + ['--publish-all=%s' % self.params['publish_all']] + + def addparam_read_only(self, c): + return c + ['--read-only=%s' % self.params['read_only']] + + def addparam_read_only_tmpfs(self, c): + return c + ['--read-only-tmpfs=%s' % self.params['read_only_tmpfs']] + + def addparam_restart_policy(self, c): + return c + ['--restart=%s' % self.params['restart_policy']] + + def addparam_rm(self, c): + if self.params['rm']: + c += ['--rm'] + return c + + def addparam_rootfs(self, c): + return c + ['--rootfs=%s' % self.params['rootfs']] + + def addparam_security_opt(self, c): + for secopt in self.params['security_opt']: + c += ['--security-opt', secopt] + return c + + def addparam_shm_size(self, c): + return c + ['--shm-size', self.params['shm_size']] + + def addparam_sig_proxy(self, c): + return c + ['--sig-proxy=%s' % self.params['sig_proxy']] + + def addparam_stop_signal(self, c): + return c + ['--stop-signal', self.params['stop_signal']] + + def addparam_stop_timeout(self, c): + return c + ['--stop-timeout', self.params['stop_timeout']] + + def addparam_subgidname(self, c): + return c + ['--subgidname', self.params['subgidname']] + + def addparam_subuidname(self, c): + return c + ['--subuidname', self.params['subuidname']] + + def addparam_sysctl(self, c): + for sysctl in self.params['sysctl'].items(): + c += ['--sysctl', + b"=".join([to_bytes(k, errors='surrogate_or_strict') + for k in sysctl])] + return c + + def addparam_systemd(self, c): + return c + ['--systemd=%s' % self.params['systemd']] + + def addparam_tmpfs(self, c): + for tmpfs in self.params['tmpfs'].items(): + c += ['--tmpfs', ':'.join(tmpfs)] + return c + + def addparam_tty(self, c): + return c + ['--tty=%s' % self.params['tty']] + + def addparam_uidmap(self, c): + for uidmap in self.params['uidmap']: + c += ['--uidmap', uidmap] + return c + + def addparam_ulimit(self, c): + for u in self.params['ulimit']: + c += ['--ulimit', u] + return c + + def addparam_user(self, c): + return c + ['--user', self.params['user']] + + def addparam_userns(self, c): + return c + ['--userns', self.params['userns']] + + def addparam_uts(self, c): + return c + ['--uts', self.params['uts']] + + def addparam_volume(self, c): + for vol in self.params['volume']: + if vol: + c += ['--volume', vol] + return c + + def addparam_volumes_from(self, c): + for vol in self.params['volumes_from']: + c += ['--volumes-from', vol] + return c + + def addparam_workdir(self, c): + return c + ['--workdir', self.params['workdir']] + + # Add your own args for podman command + def addparam_cmd_args(self, c): + return c + self.params['cmd_args'] + + +class PodmanDefaults: + def __init__(self, image_info, podman_version): + self.version = podman_version + self.image_info = image_info + self.defaults = { + "blkio_weight": 0, + "cgroups": "default", + "cidfile": "", + "cpus": 0.0, + "cpu_shares": 0, + "cpu_quota": 0, + "cpu_period": 0, + "cpu_rt_runtime": 0, + "cpu_rt_period": 0, + "cpuset_cpus": "", + "cpuset_mems": "", + "detach": True, + "device": [], + "env_host": False, + "etc_hosts": {}, + "group_add": [], + "ipc": "", + "kernelmemory": "0", + "log_driver": "k8s-file", + "log_level": "error", + "memory": "0", + "memory_swap": "0", + "memory_reservation": "0", + # "memory_swappiness": -1, + "no_hosts": False, + # libpod issue with networks in inspection + "oom_score_adj": 0, + "pid": "", + "privileged": False, + "rm": False, + "security_opt": [], + "stop_signal": self.image_info['config'].get('stopsignal', "15"), + "tty": False, + "user": self.image_info.get('user', ''), + "workdir": self.image_info['config'].get('workingdir', '/'), + "uts": "", + } + + def default_dict(self): + # make here any changes to self.defaults related to podman version + # https://github.com/containers/libpod/pull/5669 + if (LooseVersion(self.version) >= LooseVersion('1.8.0') + and LooseVersion(self.version) < LooseVersion('1.9.0')): + self.defaults['cpu_shares'] = 1024 + if (LooseVersion(self.version) >= LooseVersion('2.0.0')): + self.defaults['network'] = ["slirp4netns"] + self.defaults['ipc'] = "private" + self.defaults['uts'] = "private" + self.defaults['pid'] = "private" + return self.defaults + + +class PodmanContainerDiff: + def __init__(self, module, module_params, info, image_info, podman_version): + self.module = module + self.module_params = module_params + self.version = podman_version + self.default_dict = None + self.info = lower_keys(info) + self.image_info = lower_keys(image_info) + self.params = self.defaultize() + self.diff = {'before': {}, 'after': {}} + self.non_idempotent = { + 'env_file', # We can't get env vars from file to check + 'env_host', + } + + def defaultize(self): + params_with_defaults = {} + self.default_dict = PodmanDefaults( + self.image_info, self.version).default_dict() + for p in self.module_params: + if self.module_params[p] is None and p in self.default_dict: + params_with_defaults[p] = self.default_dict[p] + else: + params_with_defaults[p] = self.module_params[p] + return params_with_defaults + + def _diff_update_and_compare(self, param_name, before, after): + if before != after: + self.diff['before'].update({param_name: before}) + self.diff['after'].update({param_name: after}) + return True + return False + + def diffparam_annotation(self): + before = self.info['config']['annotations'] or {} + after = before.copy() + if self.module_params['annotation'] is not None: + after.update(self.params['annotation']) + return self._diff_update_and_compare('annotation', before, after) + + def diffparam_env_host(self): + # It's impossible to get from inspest, recreate it if not default + before = False + after = self.params['env_host'] + return self._diff_update_and_compare('env_host', before, after) + + def diffparam_blkio_weight(self): + before = self.info['hostconfig']['blkioweight'] + after = self.params['blkio_weight'] + return self._diff_update_and_compare('blkio_weight', before, after) + + def diffparam_blkio_weight_device(self): + before = self.info['hostconfig']['blkioweightdevice'] + if before == [] and self.module_params['blkio_weight_device'] is None: + after = [] + else: + after = self.params['blkio_weight_device'] + return self._diff_update_and_compare('blkio_weight_device', before, after) + + def diffparam_cap_add(self): + before = self.info['effectivecaps'] or [] + after = [] + if self.module_params['cap_add'] is not None: + after += ["cap_" + i.lower() + for i in self.module_params['cap_add']] + after += before + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('cap_add', before, after) + + def diffparam_cap_drop(self): + before = self.info['effectivecaps'] or [] + after = before[:] + if self.module_params['cap_drop'] is not None: + for c in ["cap_" + i.lower() for i in self.module_params['cap_drop']]: + if c in after: + after.remove(c) + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('cap_drop', before, after) + + def diffparam_cgroup_parent(self): + before = self.info['hostconfig']['cgroupparent'] + after = self.params['cgroup_parent'] + if after is None: + after = before + return self._diff_update_and_compare('cgroup_parent', before, after) + + def diffparam_cgroups(self): + # Cgroups output is not supported in all versions + if 'cgroups' in self.info['hostconfig']: + before = self.info['hostconfig']['cgroups'] + after = self.params['cgroups'] + return self._diff_update_and_compare('cgroups', before, after) + return False + + def diffparam_cidfile(self): + before = self.info['hostconfig']['containeridfile'] + after = self.params['cidfile'] + return self._diff_update_and_compare('cidfile', before, after) + + def diffparam_command(self): + # TODO(sshnaidm): to inspect image to get the default command + if self.module_params['command'] is not None: + before = self.info['config']['cmd'] + after = self.params['command'] + if isinstance(after, str): + after = [i.lower() for i in after.split()] + elif isinstance(after, list): + after = [i.lower() for i in after] + return self._diff_update_and_compare('command', before, after) + return False + + def diffparam_conmon_pidfile(self): + before = self.info['conmonpidfile'] + if self.module_params['conmon_pidfile'] is None: + after = before + else: + after = self.params['conmon_pidfile'] + return self._diff_update_and_compare('conmon_pidfile', before, after) + + def diffparam_cpu_period(self): + before = self.info['hostconfig']['cpuperiod'] + after = self.params['cpu_period'] + return self._diff_update_and_compare('cpu_period', before, after) + + def diffparam_cpu_rt_period(self): + before = self.info['hostconfig']['cpurealtimeperiod'] + after = self.params['cpu_rt_period'] + return self._diff_update_and_compare('cpu_rt_period', before, after) + + def diffparam_cpu_rt_runtime(self): + before = self.info['hostconfig']['cpurealtimeruntime'] + after = self.params['cpu_rt_runtime'] + return self._diff_update_and_compare('cpu_rt_runtime', before, after) + + def diffparam_cpu_shares(self): + before = self.info['hostconfig']['cpushares'] + after = self.params['cpu_shares'] + return self._diff_update_and_compare('cpu_shares', before, after) + + def diffparam_cpus(self): + before = int(self.info['hostconfig']['nanocpus']) / 1000000000 + after = self.params['cpus'] + return self._diff_update_and_compare('cpus', before, after) + + def diffparam_cpuset_cpus(self): + before = self.info['hostconfig']['cpusetcpus'] + after = self.params['cpuset_cpus'] + return self._diff_update_and_compare('cpuset_cpus', before, after) + + def diffparam_cpuset_mems(self): + before = self.info['hostconfig']['cpusetmems'] + after = self.params['cpuset_mems'] + return self._diff_update_and_compare('cpuset_mems', before, after) + + def diffparam_device(self): + before = [":".join([i['pathonhost'], i['pathincontainer']]) + for i in self.info['hostconfig']['devices']] + after = [":".join(i.split(":")[:2]) for i in self.params['device']] + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('devices', before, after) + + def diffparam_device_read_bps(self): + before = self.info['hostconfig']['blkiodevicereadbps'] or [] + before = ["%s:%s" % (i['path'], i['rate']) for i in before] + after = self.params['device_read_bps'] or [] + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('device_read_bps', before, after) + + def diffparam_device_read_iops(self): + before = self.info['hostconfig']['blkiodevicereadiops'] or [] + before = ["%s:%s" % (i['path'], i['rate']) for i in before] + after = self.params['device_read_iops'] or [] + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('device_read_iops', before, after) + + def diffparam_device_write_bps(self): + before = self.info['hostconfig']['blkiodevicewritebps'] or [] + before = ["%s:%s" % (i['path'], i['rate']) for i in before] + after = self.params['device_write_bps'] or [] + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('device_write_bps', before, after) + + def diffparam_device_write_iops(self): + before = self.info['hostconfig']['blkiodevicewriteiops'] or [] + before = ["%s:%s" % (i['path'], i['rate']) for i in before] + after = self.params['device_write_iops'] or [] + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('device_write_iops', before, after) + + # Limited idempotency, it can't guess default values + def diffparam_env(self): + env_before = self.info['config']['env'] or {} + before = {i.split("=")[0]: "=".join(i.split("=")[1:]) + for i in env_before} + after = before.copy() + if self.params['env']: + after.update({ + k: v + for k, v in self.params['env'].items() + }) + return self._diff_update_and_compare('env', before, after) + + def diffparam_etc_hosts(self): + if self.info['hostconfig']['extrahosts']: + before = dict([i.split(":") + for i in self.info['hostconfig']['extrahosts']]) + else: + before = {} + after = self.params['etc_hosts'] + return self._diff_update_and_compare('etc_hosts', before, after) + + def diffparam_group_add(self): + before = self.info['hostconfig']['groupadd'] + after = self.params['group_add'] + return self._diff_update_and_compare('group_add', before, after) + + # Healthcheck is only defined in container config if a healthcheck + # was configured; otherwise the config key isn't part of the config. + def diffparam_healthcheck(self): + if 'healthcheck' in self.info['config']: + # the "test" key is a list of 2 items where the first one is + # "CMD-SHELL" and the second one is the actual healthcheck command. + before = self.info['config']['healthcheck']['test'][1] + else: + before = '' + after = self.params['healthcheck'] or before + return self._diff_update_and_compare('healthcheck', before, after) + + # Because of hostname is random generated, this parameter has partial idempotency only. + def diffparam_hostname(self): + before = self.info['config']['hostname'] + after = self.params['hostname'] or before + return self._diff_update_and_compare('hostname', before, after) + + def diffparam_image(self): + before_id = self.info['image'] + after_id = self.image_info['id'] + if before_id == after_id: + return self._diff_update_and_compare('image', before_id, after_id) + before = self.info['config']['image'] + after = self.params['image'] + mode = self.params['image_strict'] + if mode is None or not mode: + # In a idempotency 'lite mode' assume all images from different registries are the same + before = before.replace(":latest", "") + after = after.replace(":latest", "") + before = before.split("/")[-1] + after = after.split("/")[-1] + else: + return self._diff_update_and_compare('image', before_id, after_id) + return self._diff_update_and_compare('image', before, after) + + def diffparam_ipc(self): + before = self.info['hostconfig']['ipcmode'] + after = self.params['ipc'] + if self.params['pod'] and not self.module_params['ipc']: + after = before + return self._diff_update_and_compare('ipc', before, after) + + def diffparam_label(self): + before = self.info['config']['labels'] or {} + after = self.image_info.get('labels') or {} + if self.params['label']: + after.update({ + str(k).lower(): str(v).lower() + for k, v in self.params['label'].items() + }) + return self._diff_update_and_compare('label', before, after) + + def diffparam_log_driver(self): + before = self.info['hostconfig']['logconfig']['type'] + after = self.params['log_driver'] + return self._diff_update_and_compare('log_driver', before, after) + + def diffparam_log_level(self): + excom = self.info['exitcommand'] + if '--log-level' in excom: + before = excom[excom.index('--log-level') + 1].lower() + else: + before = self.params['log_level'] + after = self.params['log_level'] + return self._diff_update_and_compare('log_level', before, after) + + # Parameter has limited idempotency, unable to guess the default log_path + def diffparam_log_opt(self): + before, after = {}, {} + # Log path + if 'logpath' in self.info: + path_before = self.info['logpath'] + if (self.module_params['log_opt'] and + 'path' in self.module_params['log_opt'] and + self.module_params['log_opt']['path'] is not None): + path_after = self.params['log_opt']['path'] + else: + path_after = path_before + if path_before != path_after: + before.update({'log-path': path_before}) + after.update({'log-path': path_after}) + + # Log tag + if 'logtag' in self.info: + tag_before = self.info['logtag'] + if (self.module_params['log_opt'] and + 'tag' in self.module_params['log_opt'] and + self.module_params['log_opt']['tag'] is not None): + tag_after = self.params['log_opt']['tag'] + else: + tag_after = '' + if tag_before != tag_after: + before.update({'log-tag': tag_before}) + after.update({'log-tag': tag_after}) + + return self._diff_update_and_compare('log_opt', before, after) + + def diffparam_mac_address(self): + before = str(self.info['networksettings']['macaddress']) + if self.module_params['mac_address'] is not None: + after = self.params['mac_address'] + else: + after = before + return self._diff_update_and_compare('mac_address', before, after) + + def diffparam_memory(self): + before = str(self.info['hostconfig']['memory']) + after = self.params['memory'] + return self._diff_update_and_compare('memory', before, after) + + def diffparam_memory_swap(self): + # By default it's twice memory parameter + before = str(self.info['hostconfig']['memoryswap']) + after = self.params['memory_swap'] + if (self.module_params['memory_swap'] is None + and self.params['memory'] != 0 + and self.params['memory'].isdigit()): + after = str(int(self.params['memory']) * 2) + return self._diff_update_and_compare('memory_swap', before, after) + + def diffparam_memory_reservation(self): + before = str(self.info['hostconfig']['memoryreservation']) + after = self.params['memory_reservation'] + return self._diff_update_and_compare('memory_reservation', before, after) + + def diffparam_network(self): + net_mode_before = self.info['hostconfig']['networkmode'] + net_mode_after = '' + before = list(self.info['networksettings'].get('networks', {})) + after = self.params['network'] or [] + # If container is in pod and no networks are provided + if not self.module_params['network'] and self.params['pod']: + after = before + return self._diff_update_and_compare('network', before, after) + # Check special network modes + if after in [['bridge'], ['host'], ['slirp4netns'], ['none']]: + net_mode_after = after[0] + # If changes are only for network mode and container has no networks + if net_mode_after and not before: + # Remove differences between v1 and v2 + net_mode_after = net_mode_after.replace('bridge', 'default') + net_mode_after = net_mode_after.replace('slirp4netns', 'default') + net_mode_before = net_mode_before.replace('bridge', 'default') + net_mode_before = net_mode_before.replace('slirp4netns', 'default') + return self._diff_update_and_compare('network', net_mode_before, net_mode_after) + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('network', before, after) + + def diffparam_no_hosts(self): + before = not bool(self.info['hostspath']) + # For newer verions of Podman + if 'resolvconfpath' in self.info: + before = not bool(self.info['resolvconfpath']) + after = self.params['no_hosts'] + if self.params['network'] == ['none']: + after = True + return self._diff_update_and_compare('no_hosts', before, after) + + def diffparam_oom_score_adj(self): + before = self.info['hostconfig']['oomscoreadj'] + after = self.params['oom_score_adj'] + return self._diff_update_and_compare('oom_score_adj', before, after) + + def diffparam_privileged(self): + before = self.info['hostconfig']['privileged'] + after = self.params['privileged'] + return self._diff_update_and_compare('privileged', before, after) + + def diffparam_pid(self): + before = self.info['hostconfig']['pidmode'] + after = self.params['pid'] + return self._diff_update_and_compare('pid', before, after) + + # TODO(sshnaidm) Need to add port ranges support + def diffparam_publish(self): + ports = self.info['hostconfig']['portbindings'] + before = [":".join([ + j[0]['hostip'], + str(j[0]["hostport"]), + i.replace('/tcp', '') + ]).strip(':') for i, j in ports.items()] + after = self.params['publish'] or [] + if self.params['publish_all']: + image_ports = self.image_info['config'].get('exposedports', {}) + if image_ports: + after += list(image_ports.keys()) + after = [i.replace("/tcp", "") for i in after] + # No support for port ranges yet + for ports in after: + if "-" in ports: + return self._diff_update_and_compare('publish', '', '') + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('publish', before, after) + + def diffparam_rm(self): + before = self.info['hostconfig']['autoremove'] + after = self.params['rm'] + return self._diff_update_and_compare('rm', before, after) + + def diffparam_security_opt(self): + before = self.info['hostconfig']['securityopt'] + # In rootful containers with apparmor there is a default security opt + before = [o for o in before if 'apparmor=containers-default' not in o] + after = self.params['security_opt'] + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('security_opt', before, after) + + def diffparam_stop_signal(self): + signals = { + "sighup": "1", + "sigint": "2", + "sigquit": "3", + "sigill": "4", + "sigtrap": "5", + "sigabrt": "6", + "sigiot": "6", + "sigbus": "7", + "sigfpe": "8", + "sigkill": "9", + "sigusr1": "10", + "sigsegv": "11", + "sigusr2": "12", + "sigpipe": "13", + "sigalrm": "14", + "sigterm": "15", + "sigstkflt": "16", + "sigchld": "17", + "sigcont": "18", + "sigstop": "19", + "sigtstp": "20", + "sigttin": "21", + "sigttou": "22", + "sigurg": "23", + "sigxcpu": "24", + "sigxfsz": "25", + "sigvtalrm": "26", + "sigprof": "27", + "sigwinch": "28", + "sigio": "29", + "sigpwr": "30", + "sigsys": "31" + } + before = str(self.info['config']['stopsignal']) + if not before.isdigit(): + before = signals[before.lower()] + after = str(self.params['stop_signal']) + if not after.isdigit(): + after = signals[after.lower()] + return self._diff_update_and_compare('stop_signal', before, after) + + def diffparam_tty(self): + before = self.info['config']['tty'] + after = self.params['tty'] + return self._diff_update_and_compare('tty', before, after) + + def diffparam_user(self): + before = self.info['config']['user'] + after = self.params['user'] + return self._diff_update_and_compare('user', before, after) + + def diffparam_ulimit(self): + after = self.params['ulimit'] or [] + # In case of latest podman + if 'createcommand' in self.info['config']: + ulimits = [] + for k, c in enumerate(self.info['config']['createcommand']): + if c == '--ulimit': + ulimits.append(self.info['config']['createcommand'][k + 1]) + before = ulimits + before, after = sorted(before), sorted(after) + return self._diff_update_and_compare('ulimit', before, after) + if after: + ulimits = self.info['hostconfig']['ulimits'] + before = { + u['name'].replace('rlimit_', ''): "%s:%s" % (u['soft'], u['hard']) for u in ulimits} + after = {i.split('=')[0]: i.split('=')[1] + for i in self.params['ulimit']} + new_before = [] + new_after = [] + for u in list(after.keys()): + # We don't support unlimited ulimits because it depends on platform + if u in before and "-1" not in after[u]: + new_before.append([u, before[u]]) + new_after.append([u, after[u]]) + return self._diff_update_and_compare('ulimit', new_before, new_after) + return self._diff_update_and_compare('ulimit', '', '') + + def diffparam_uts(self): + before = self.info['hostconfig']['utsmode'] + after = self.params['uts'] + if self.params['pod'] and not self.module_params['uts']: + after = before + return self._diff_update_and_compare('uts', before, after) + + def diffparam_volume(self): + def clean_volume(x): + '''Remove trailing and double slashes from volumes.''' + return x.replace("//", "/").rstrip("/") + + before = self.info['mounts'] + before_local_vols = [] + if before: + volumes = [] + local_vols = [] + for m in before: + if m['type'] != 'volume': + volumes.append([m['source'], m['destination']]) + elif m['type'] == 'volume': + local_vols.append([m['name'], m['destination']]) + before = [":".join(v) for v in volumes] + before_local_vols = [":".join(v) for v in local_vols] + if self.params['volume'] is not None: + after = [":".join( + [clean_volume(i) for i in v.split(":")[:2]] + ) for v in self.params['volume']] + else: + after = [] + if before_local_vols: + after = list(set(after).difference(before_local_vols)) + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('volume', before, after) + + def diffparam_volumes_from(self): + # Possibly volumesfrom is not in config + before = self.info['hostconfig'].get('volumesfrom', []) or [] + after = self.params['volumes_from'] or [] + return self._diff_update_and_compare('volumes_from', before, after) + + def diffparam_workdir(self): + before = self.info['config']['workingdir'] + after = self.params['workdir'] + return self._diff_update_and_compare('workdir', before, after) + + def is_different(self): + diff_func_list = [func for func in dir(self) + if callable(getattr(self, func)) and func.startswith( + "diffparam")] + fail_fast = not bool(self.module._diff) + different = False + for func_name in diff_func_list: + dff_func = getattr(self, func_name) + if dff_func(): + if fail_fast: + return True + different = True + # Check non idempotent parameters + for p in self.non_idempotent: + if self.module_params[p] is not None and self.module_params[p] not in [{}, [], '']: + different = True + return different + + +def ensure_image_exists(module, image, module_params): + """If image is passed, ensure it exists, if not - pull it or fail. + + Arguments: + module {obj} -- ansible module object + image {str} -- name of image + + Returns: + list -- list of image actions - if it pulled or nothing was done + """ + image_actions = [] + module_exec = module_params['executable'] + if not image: + return image_actions + rc, out, err = module.run_command([module_exec, 'image', 'exists', image]) + if rc == 0: + return image_actions + rc, out, err = module.run_command([module_exec, 'image', 'pull', image]) + if rc != 0: + module.fail_json(msg="Can't pull image %s" % image, stdout=out, + stderr=err) + image_actions.append("pulled image %s" % image) + return image_actions + + +class PodmanContainer: + """Perform container tasks. + + Manages podman container, inspects it and checks its current state + """ + + def __init__(self, module, name, module_params): + """Initialize PodmanContainer class. + + Arguments: + module {obj} -- ansible module object + name {str} -- name of container + """ + + self.module = module + self.module_params = module_params + self.name = name + self.stdout, self.stderr = '', '' + self.info = self.get_info() + self.version = self._get_podman_version() + self.diff = {} + self.actions = [] + + @property + def exists(self): + """Check if container exists.""" + return bool(self.info != {}) + + @property + def different(self): + """Check if container is different.""" + diffcheck = PodmanContainerDiff( + self.module, + self.module_params, + self.info, + self.get_image_info(), + self.version) + is_different = diffcheck.is_different() + diffs = diffcheck.diff + if self.module._diff and is_different and diffs['before'] and diffs['after']: + self.diff['before'] = "\n".join( + ["%s - %s" % (k, v) for k, v in sorted( + diffs['before'].items())]) + "\n" + self.diff['after'] = "\n".join( + ["%s - %s" % (k, v) for k, v in sorted( + diffs['after'].items())]) + "\n" + return is_different + + @property + def running(self): + """Return True if container is running now.""" + return self.exists and self.info['State']['Running'] + + @property + def stopped(self): + """Return True if container exists and is not running now.""" + return self.exists and not self.info['State']['Running'] + + def get_info(self): + """Inspect container and gather info about it.""" + # pylint: disable=unused-variable + rc, out, err = self.module.run_command( + [self.module_params['executable'], b'container', b'inspect', self.name]) + return json.loads(out)[0] if rc == 0 else {} + + def get_image_info(self): + """Inspect container image and gather info about it.""" + # pylint: disable=unused-variable + rc, out, err = self.module.run_command( + [self.module_params['executable'], b'image', b'inspect', self.module_params['image']]) + return json.loads(out)[0] if rc == 0 else {} + + def _get_podman_version(self): + # pylint: disable=unused-variable + rc, out, err = self.module.run_command( + [self.module_params['executable'], b'--version']) + if rc != 0 or not out or "version" not in out: + self.module.fail_json(msg="%s run failed!" % + self.module_params['executable']) + return out.split("version")[1].strip() + + def _perform_action(self, action): + """Perform action with container. + + Arguments: + action {str} -- action to perform - start, create, stop, run, + delete + """ + b_command = PodmanModuleParams(action, + self.module_params, + self.version, + self.module, + ).construct_command_from_params() + if action == 'create': + b_command.remove(b'--detach=True') + full_cmd = " ".join([self.module_params['executable']] + + [to_native(i) for i in b_command]) + self.actions.append(full_cmd) + if self.module.check_mode: + self.module.log( + "PODMAN-CONTAINER-DEBUG (check_mode): %s" % full_cmd) + else: + rc, out, err = self.module.run_command( + [self.module_params['executable'], b'container'] + b_command, + expand_user_and_vars=False) + self.module.log("PODMAN-CONTAINER-DEBUG: %s" % full_cmd) + if self.module_params['debug']: + self.module.log("PODMAN-CONTAINER-DEBUG STDOUT: %s" % out) + self.module.log("PODMAN-CONTAINER-DEBUG STDERR: %s" % err) + self.module.log("PODMAN-CONTAINER-DEBUG RC: %s" % rc) + self.stdout = out + self.stderr = err + if rc != 0: + self.module.fail_json( + msg="Can't %s container %s" % (action, self.name), + stdout=out, stderr=err) + + def run(self): + """Run the container.""" + self._perform_action('run') + + def delete(self): + """Delete the container.""" + self._perform_action('delete') + + def stop(self): + """Stop the container.""" + self._perform_action('stop') + + def start(self): + """Start the container.""" + self._perform_action('start') + + def create(self): + """Create the container.""" + self._perform_action('create') + + def recreate(self): + """Recreate the container.""" + self.delete() + self.run() + + def restart(self): + """Restart the container.""" + self.stop() + self.start() + + +class PodmanManager: + """Module manager class. + + Defines according to parameters what actions should be applied to container + """ + + def __init__(self, module, params): + """Initialize PodmanManager class. + + Arguments: + module {obj} -- ansible module object + """ + + self.module = module + self.results = { + 'changed': False, + 'actions': [], + 'container': {}, + } + self.module_params = params + self.name = self.module_params['name'] + self.executable = \ + self.module.get_bin_path(self.module_params['executable'], + required=True) + self.image = self.module_params['image'] + image_actions = ensure_image_exists( + self.module, self.image, self.module_params) + self.results['actions'] += image_actions + self.state = self.module_params['state'] + self.restart = self.module_params['force_restart'] + self.recreate = self.module_params['recreate'] + self.container = PodmanContainer( + self.module, self.name, self.module_params) + + def update_container_result(self, changed=True): + """Inspect the current container, update results with last info, exit. + + Keyword Arguments: + changed {bool} -- whether any action was performed + (default: {True}) + """ + facts = self.container.get_info() if changed else self.container.info + out, err = self.container.stdout, self.container.stderr + self.results.update({'changed': changed, 'container': facts, + 'podman_actions': self.container.actions}, + stdout=out, stderr=err) + if self.container.diff: + self.results.update({'diff': self.container.diff}) + if self.module.params['debug'] or self.module_params['debug']: + self.results.update({'podman_version': self.container.version}) + + def make_started(self): + """Run actions if desired state is 'started'.""" + if self.container.running and \ + (self.container.different or self.recreate): + self.container.recreate() + self.results['actions'].append('recreated %s' % + self.container.name) + self.update_container_result() + return + elif self.container.running and not self.container.different: + if self.restart: + self.container.restart() + self.results['actions'].append('restarted %s' % + self.container.name) + self.update_container_result() + return + self.update_container_result(changed=False) + return + elif not self.container.exists: + self.container.run() + self.results['actions'].append('started %s' % self.container.name) + self.update_container_result() + return + elif self.container.stopped and self.container.different: + self.container.recreate() + self.results['actions'].append('recreated %s' % + self.container.name) + self.update_container_result() + return + elif self.container.stopped and not self.container.different: + self.container.start() + self.results['actions'].append('started %s' % self.container.name) + self.update_container_result() + return + + def make_stopped(self): + """Run actions if desired state is 'stopped'.""" + if not self.container.exists and not self.image: + self.module.fail_json(msg='Cannot create container when image' + ' is not specified!') + if not self.container.exists: + self.container.create() + self.results['actions'].append('created %s' % self.container.name) + self.update_container_result() + return + if self.container.stopped: + self.update_container_result(changed=False) + return + elif self.container.running: + self.container.stop() + self.results['actions'].append('stopped %s' % self.container.name) + self.update_container_result() + return + + def make_absent(self): + """Run actions if desired state is 'absent'.""" + if not self.container.exists: + self.results.update({'changed': False}) + elif self.container.exists: + self.container.delete() + self.results['actions'].append('deleted %s' % self.container.name) + self.results.update({'changed': True}) + self.results.update({'container': {}, + 'podman_actions': self.container.actions}) + + def execute(self): + """Execute the desired action according to map of actions & states.""" + states_map = { + 'present': self.make_started, + 'started': self.make_started, + 'absent': self.make_absent, + 'stopped': self.make_stopped + } + process_action = states_map[self.state] + process_action() + return self.results diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/podman_pod_lib.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/podman_pod_lib.py new file mode 100644 index 00000000..c3f0e4e3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/module_utils/podman/podman_pod_lib.py @@ -0,0 +1,736 @@ +from __future__ import (absolute_import, division, print_function) +import json +from distutils.version import LooseVersion + +from ansible.module_utils._text import to_bytes, to_native + +from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys + +__metaclass__ = type + +ARGUMENTS_SPEC_POD = dict( + state=dict( + type='str', + default="created", + choices=[ + 'created', + 'killed', + 'restarted', + 'absent', + 'started', + 'stopped', + 'paused', + 'unpaused', + ]), + recreate=dict(type='bool', default=False), + add_host=dict(type='list', required=False, elements='str'), + cgroup_parent=dict(type='str', required=False), + dns=dict(type='list', elements='str', required=False), + dns_opt=dict(type='list', elements='str', required=False), + dns_search=dict(type='list', elements='str', required=False), + hostname=dict(type='str', required=False), + infra=dict(type='bool', required=False), + infra_conmon_pidfile=dict(type='str', required=False), + infra_command=dict(type='str', required=False), + infra_image=dict(type='str', required=False), + ip=dict(type='str', required=False), + label=dict(type='dict', required=False), + label_file=dict(type='str', required=False), + mac_address=dict(type='str', required=False), + name=dict(type='str', required=True), + network=dict(type='str', required=False), + no_hosts=dict(type='bool', required=False), + pod_id_file=dict(type='str', required=False), + publish=dict(type='list', required=False, + elements='str', aliases=['ports']), + share=dict(type='str', required=False), + executable=dict(type='str', required=False, default='podman'), + debug=dict(type='bool', default=False), +) + + +class PodmanPodModuleParams: + """Creates list of arguments for podman CLI command. + + Arguments: + action {str} -- action type from 'run', 'stop', 'create', 'delete', + 'start' + params {dict} -- dictionary of module parameters + + """ + + def __init__(self, action, params, podman_version, module): + self.params = params + self.action = action + self.podman_version = podman_version + self.module = module + + def construct_command_from_params(self): + """Create a podman command from given module parameters. + + Returns: + list -- list of byte strings for Popen command + """ + if self.action in ['start', 'restart', 'stop', 'delete', 'pause', + 'unpause', 'kill']: + return self._simple_action() + if self.action in ['create']: + return self._create_action() + self.module.fail_json(msg="Unknown action %s" % self.action) + + def _simple_action(self): + if self.action in ['start', 'restart', 'stop', 'pause', 'unpause', 'kill']: + cmd = [self.action, self.params['name']] + return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + + if self.action == 'delete': + cmd = ['rm', '-f', self.params['name']] + return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + self.module.fail_json(msg="Unknown action %s" % self.action) + + def _create_action(self): + cmd = [self.action] + all_param_methods = [func for func in dir(self) + if callable(getattr(self, func)) + and func.startswith("addparam")] + params_set = (i for i in self.params if self.params[i] is not None) + for param in params_set: + func_name = "_".join(["addparam", param]) + if func_name in all_param_methods: + cmd = getattr(self, func_name)(cmd) + return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + + def check_version(self, param, minv=None, maxv=None): + if minv and LooseVersion(minv) > LooseVersion( + self.podman_version): + self.module.fail_json(msg="Parameter %s is supported from podman " + "version %s only! Current version is %s" % ( + param, minv, self.podman_version)) + if maxv and LooseVersion(maxv) < LooseVersion( + self.podman_version): + self.module.fail_json(msg="Parameter %s is supported till podman " + "version %s only! Current version is %s" % ( + param, minv, self.podman_version)) + + def addparam_add_host(self, c): + for g in self.params['add_host']: + c += ['--add-host', g] + return c + + def addparam_cgroup_parent(self, c): + return c + ['--cgroup-parent', self.params['cgroup_parent']] + + def addparam_dns(self, c): + for g in self.params['dns']: + c += ['--dns', g] + return c + + def addparam_dns_opt(self, c): + for g in self.params['dns_opt']: + c += ['--dns-opt', g] + return c + + def addparam_dns_search(self, c): + for g in self.params['dns_search']: + c += ['--dns-search', g] + return c + + def addparam_hostname(self, c): + return c + ['--hostname', self.params['hostname']] + + def addparam_infra(self, c): + return c + [b'='.join([b'--infra', + to_bytes(self.params['infra'], + errors='surrogate_or_strict')])] + + def addparam_infra_conmon_pidfile(self, c): + return c + ['--infra-conmon-pidfile', self.params['infra_conmon_pidfile']] + + def addparam_infra_command(self, c): + return c + ['--infra-command', self.params['infra_command']] + + def addparam_infra_image(self, c): + return c + ['--infra-image', self.params['infra_image']] + + def addparam_ip(self, c): + return c + ['--ip', self.params['ip']] + + def addparam_label(self, c): + for label in self.params['label'].items(): + c += ['--label', b'='.join( + [to_bytes(l, errors='surrogate_or_strict') for l in label])] + return c + + def addparam_label_file(self, c): + return c + ['--label-file', self.params['label_file']] + + def addparam_mac_address(self, c): + return c + ['--mac-address', self.params['mac_address']] + + def addparam_name(self, c): + return c + ['--name', self.params['name']] + + def addparam_network(self, c): + return c + ['--network', self.params['network']] + + def addparam_no_hosts(self, c): + return c + ["=".join('--no-hosts', self.params['no_hosts'])] + + def addparam_pod_id_file(self, c): + return c + ['--pod-id-file', self.params['pod_id_file']] + + def addparam_publish(self, c): + for g in self.params['publish']: + c += ['--publish', g] + return c + + def addparam_share(self, c): + return c + ['--share', self.params['share']] + + +class PodmanPodDefaults: + def __init__(self, module, podman_version): + self.module = module + self.version = podman_version + self.defaults = { + 'add_host': [], + 'dns': [], + 'dns_opt': [], + 'dns_search': [], + 'infra': True, + 'label': {}, + } + + def default_dict(self): + # make here any changes to self.defaults related to podman version + # https://github.com/containers/libpod/pull/5669 + # if (LooseVersion(self.version) >= LooseVersion('1.8.0') + # and LooseVersion(self.version) < LooseVersion('1.9.0')): + # self.defaults['cpu_shares'] = 1024 + return self.defaults + + +class PodmanPodDiff: + def __init__(self, module, module_params, info, infra_info, podman_version): + self.module = module + self.module_params = module_params + self.version = podman_version + self.default_dict = None + self.info = lower_keys(info) + self.infra_info = lower_keys(infra_info) + self.params = self.defaultize() + self.diff = {'before': {}, 'after': {}} + self.non_idempotent = {} + + def defaultize(self): + params_with_defaults = {} + self.default_dict = PodmanPodDefaults( + self.module, self.version).default_dict() + for p in self.module_params: + if self.module_params[p] is None and p in self.default_dict: + params_with_defaults[p] = self.default_dict[p] + else: + params_with_defaults[p] = self.module_params[p] + return params_with_defaults + + def _diff_update_and_compare(self, param_name, before, after): + if before != after: + self.diff['before'].update({param_name: before}) + self.diff['after'].update({param_name: after}) + return True + return False + + def diffparam_add_host(self): + if not self.infra_info: + return self._diff_update_and_compare('add_host', '', '') + before = self.infra_info['hostconfig']['extrahosts'] or [] + after = self.params['add_host'] + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('add_host', before, after) + + def diffparam_cgroup_parent(self): + if 'cgroupparent' in self.info: + before = self.info['cgroupparent'] + elif 'config' in self.info and self.info['config'].get('cgroupparent'): + before = self.info['config']['cgroupparent'] + after = self.params['cgroup_parent'] or before + return self._diff_update_and_compare('cgroup_parent', before, after) + + def diffparam_dns(self): + if not self.infra_info: + return self._diff_update_and_compare('dns', '', '') + before = self.infra_info['hostconfig']['dns'] or [] + after = self.params['dns'] + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('dns', before, after) + + def diffparam_dns_opt(self): + if not self.infra_info: + return self._diff_update_and_compare('dns_opt', '', '') + before = self.infra_info['hostconfig']['dnsoptions'] or [] + after = self.params['dns_opt'] + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('dns_opt', before, after) + + def diffparam_dns_search(self): + if not self.infra_info: + return self._diff_update_and_compare('dns_search', '', '') + before = self.infra_info['hostconfig']['dnssearch'] or [] + after = self.params['dns_search'] + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('dns_search', before, after) + + def diffparam_hostname(self): + if not self.infra_info: + return self._diff_update_and_compare('hostname', '', '') + before = self.infra_info['config']['hostname'] + after = self.params['hostname'] or before + return self._diff_update_and_compare('hostname', before, after) + + # TODO(sshnaidm): https://github.com/containers/podman/issues/6968 + def diffparam_infra(self): + if 'state' in self.info and 'infracontainerid' in self.info['state']: + before = self.info['state']['infracontainerid'] != "" + else: + # TODO(sshnaidm): https://github.com/containers/podman/issues/6968 + before = 'infracontainerid' in self.info + after = self.params['infra'] + return self._diff_update_and_compare('infra', before, after) + + # TODO(sshnaidm): https://github.com/containers/podman/issues/6969 + # def diffparam_infra_command(self): + # before = str(self.info['hostconfig']['infra_command']) + # after = self.params['infra_command'] + # return self._diff_update_and_compare('infra_command', before, after) + + def diffparam_infra_image(self): + if not self.infra_info: + return self._diff_update_and_compare('infra_image', '', '') + before = str(self.infra_info['imagename']) + after = before + if self.module_params['infra_image']: + after = self.params['infra_image'] + before = before.replace(":latest", "") + after = after.replace(":latest", "") + before = before.split("/")[-1] + after = after.split("/")[-1] + return self._diff_update_and_compare('infra_image', before, after) + + # TODO(sshnaidm): https://github.com/containers/podman/pull/6956 + # def diffparam_ip(self): + # before = str(self.info['hostconfig']['ip']) + # after = self.params['ip'] + # return self._diff_update_and_compare('ip', before, after) + + def diffparam_label(self): + if 'config' in self.info and 'labels' in self.info['config']: + before = self.info['config'].get('labels') or {} + else: + before = self.info['labels'] if 'labels' in self.info else {} + after = self.params['label'] + return self._diff_update_and_compare('label', before, after) + + # TODO(sshnaidm): https://github.com/containers/podman/pull/6956 + # def diffparam_mac_address(self): + # before = str(self.info['hostconfig']['mac_address']) + # after = self.params['mac_address'] + # return self._diff_update_and_compare('mac_address', before, after) + + def diffparam_network(self): + if not self.infra_info: + return self._diff_update_and_compare('network', [], []) + net_mode_before = self.infra_info['hostconfig']['networkmode'] + net_mode_after = '' + before = self.infra_info['networksettings'].get('networks', []) + after = self.params['network'] + # Currently supported only 'host' and 'none' network modes idempotency + if after in ['bridge', 'host', 'slirp4netns']: + net_mode_after = after + elif after: + after = after.split(",") + else: + after = [] + if net_mode_after and not before: + # Remove differences between v1 and v2 + net_mode_after = net_mode_after.replace('bridge', 'default') + net_mode_after = net_mode_after.replace('slirp4netns', 'default') + net_mode_before = net_mode_before.replace('bridge', 'default') + net_mode_before = net_mode_before.replace('slirp4netns', 'default') + return self._diff_update_and_compare('network', net_mode_before, net_mode_after) + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('network', before, after) + + # TODO(sshnaidm) + # def diffparam_no_hosts(self): + # before = str(self.info['hostconfig']['no_hosts']) + # after = self.params['no_hosts'] + # return self._diff_update_and_compare('no_hosts', before, after) + + # TODO(sshnaidm) Need to add port ranges support + def diffparam_publish(self): + if not self.infra_info: + return self._diff_update_and_compare('publish', '', '') + ports = self.infra_info['hostconfig']['portbindings'] + before = [":".join([ + j[0]['hostip'], + str(j[0]["hostport"]), + i.replace('/tcp', '') + ]).strip(':') for i, j in ports.items()] + after = self.params['publish'] or [] + after = [i.replace("/tcp", "") for i in after] + # No support for port ranges yet + for ports in after: + if "-" in ports: + return self._diff_update_and_compare('publish', '', '') + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('publish', before, after) + + def diffparam_share(self): + if not self.infra_info: + return self._diff_update_and_compare('share', '', '') + if 'sharednamespaces' in self.info: + before = self.info['sharednamespaces'] + elif 'config' in self.info: + before = [ + i.split('shares')[1].lower() + for i in self.info['config'] if 'shares' in i] + # TODO(sshnaidm): to discover why in podman v1 'cgroup' appears + before.remove('cgroup') + else: + before = [] + if self.params['share'] is not None: + after = self.params['share'].split(",") + else: + after = ['uts', 'ipc', 'net'] + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('share', before, after) + + def is_different(self): + diff_func_list = [func for func in dir(self) + if callable(getattr(self, func)) and func.startswith( + "diffparam")] + fail_fast = not bool(self.module._diff) + different = False + for func_name in diff_func_list: + dff_func = getattr(self, func_name) + if dff_func(): + if fail_fast: + return True + different = True + # Check non idempotent parameters + for p in self.non_idempotent: + if self.module_params[p] is not None and self.module_params[p] not in [{}, [], '']: + different = True + return different + + +class PodmanPod: + """Perform pod tasks. + + Manages podman pod, inspects it and checks its current state + """ + + def __init__(self, module, name, module_params): + """Initialize PodmanPod class. + + Arguments: + module {obj} -- ansible module object + name {str} -- name of pod + """ + + self.module = module + self.module_params = module_params + self.name = name + self.stdout, self.stderr = '', '' + self.info = self.get_info() + self.infra_info = self.get_infra_info() + self.version = self._get_podman_version() + self.diff = {} + self.actions = [] + + @property + def exists(self): + """Check if pod exists.""" + return bool(self.info != {}) + + @property + def different(self): + """Check if pod is different.""" + diffcheck = PodmanPodDiff( + self.module, + self.module_params, + self.info, + self.infra_info, + self.version) + is_different = diffcheck.is_different() + diffs = diffcheck.diff + if self.module._diff and is_different and diffs['before'] and diffs['after']: + self.diff['before'] = "\n".join( + ["%s - %s" % (k, v) for k, v in sorted( + diffs['before'].items())]) + "\n" + self.diff['after'] = "\n".join( + ["%s - %s" % (k, v) for k, v in sorted( + diffs['after'].items())]) + "\n" + return is_different + + @property + def running(self): + """Return True if pod is running now.""" + if 'status' in self.info['State']: + return self.info['State']['status'] == 'Running' + return self.info['State'] == 'Running' + + @property + def paused(self): + """Return True if pod is paused now.""" + if 'status' in self.info['State']: + return self.info['State']['status'] == 'Paused' + return self.info['State'] == 'Paused' + + @property + def stopped(self): + """Return True if pod exists and is not running now.""" + if not self.exists: + return False + if 'status' in self.info['State']: + return not (self.info['State']['status'] == 'Running') + return not (self.info['State'] == 'Running') + + def get_info(self): + """Inspect pod and gather info about it.""" + # pylint: disable=unused-variable + rc, out, err = self.module.run_command( + [self.module_params['executable'], b'pod', b'inspect', self.name]) + return json.loads(out) if rc == 0 else {} + + def get_infra_info(self): + """Inspect pod and gather info about it.""" + if not self.info: + return {} + if 'InfraContainerID' in self.info: + infra_container_id = self.info['InfraContainerID'] + elif 'State' in self.info and 'infraContainerID' in self.info['State']: + infra_container_id = self.info['State']['infraContainerID'] + else: + return {} + # pylint: disable=unused-variable + rc, out, err = self.module.run_command( + [self.module_params['executable'], b'inspect', infra_container_id]) + return json.loads(out)[0] if rc == 0 else {} + + def _get_podman_version(self): + # pylint: disable=unused-variable + rc, out, err = self.module.run_command( + [self.module_params['executable'], b'--version']) + if rc != 0 or not out or "version" not in out: + self.module.fail_json(msg="%s run failed!" % self.module_params['executable']) + return out.split("version")[1].strip() + + def _perform_action(self, action): + """Perform action with pod. + + Arguments: + action {str} -- action to perform - start, create, stop, pause + unpause, delete, restart, kill + """ + b_command = PodmanPodModuleParams(action, + self.module_params, + self.version, + self.module, + ).construct_command_from_params() + full_cmd = " ".join([self.module_params['executable'], 'pod'] + + [to_native(i) for i in b_command]) + self.module.log("PODMAN-POD-DEBUG: %s" % full_cmd) + self.actions.append(full_cmd) + if not self.module.check_mode: + rc, out, err = self.module.run_command( + [self.module_params['executable'], b'pod'] + b_command, + expand_user_and_vars=False) + self.stdout = out + self.stderr = err + if rc != 0: + self.module.fail_json( + msg="Can't %s pod %s" % (action, self.name), + stdout=out, stderr=err) + + def delete(self): + """Delete the pod.""" + self._perform_action('delete') + + def stop(self): + """Stop the pod.""" + self._perform_action('stop') + + def start(self): + """Start the pod.""" + self._perform_action('start') + + def create(self): + """Create the pod.""" + self._perform_action('create') + + def recreate(self): + """Recreate the pod.""" + self.delete() + self.create() + + def restart(self): + """Restart the pod.""" + self._perform_action('restart') + + def kill(self): + """Kill the pod.""" + self._perform_action('kill') + + def pause(self): + """Pause the pod.""" + self._perform_action('pause') + + def unpause(self): + """Unpause the pod.""" + self._perform_action('unpause') + + +class PodmanPodManager: + """Module manager class. + + Defines according to parameters what actions should be applied to pod + """ + + def __init__(self, module, params): + """Initialize PodmanManager class. + + Arguments: + module {obj} -- ansible module object + """ + + self.module = module + self.module_params = params + self.results = { + 'changed': False, + 'actions': [], + 'pod': {}, + } + self.name = self.module_params['name'] + self.executable = \ + self.module.get_bin_path(self.module_params['executable'], + required=True) + self.state = self.module_params['state'] + self.recreate = self.module_params['recreate'] + self.pod = PodmanPod(self.module, self.name, self.module_params) + + def update_pod_result(self, changed=True): + """Inspect the current pod, update results with last info, exit. + + Keyword Arguments: + changed {bool} -- whether any action was performed + (default: {True}) + """ + facts = self.pod.get_info() if changed else self.pod.info + out, err = self.pod.stdout, self.pod.stderr + self.results.update({'changed': changed, 'pod': facts, + 'podman_actions': self.pod.actions}, + stdout=out, stderr=err) + if self.pod.diff: + self.results.update({'diff': self.pod.diff}) + if self.module.params['debug'] or self.module_params['debug']: + self.results.update({'podman_version': self.pod.version}) + + def execute(self): + """Execute the desired action according to map of actions & states.""" + states_map = { + 'created': self.make_created, + 'started': self.make_started, + 'stopped': self.make_stopped, + 'absent': self.make_absent, + 'killed': self.make_killed, + 'paused': self.make_paused, + 'unpaused': self.make_unpaused, + + } + process_action = states_map[self.state] + process_action() + return self.results + + def _create_or_recreate_pod(self): + """Ensure pod exists and is exactly as it should be by input params.""" + changed = False + if self.pod.exists: + if self.pod.different or self.recreate: + self.pod.recreate() + self.results['actions'].append('recreated %s' % self.pod.name) + changed = True + elif not self.pod.exists: + self.pod.create() + self.results['actions'].append('created %s' % self.pod.name) + changed = True + return changed + + def make_created(self): + """Run actions if desired state is 'created'.""" + if self.pod.exists and not self.pod.different: + self.update_pod_result(changed=False) + return + self._create_or_recreate_pod() + self.update_pod_result() + + def make_killed(self): + """Run actions if desired state is 'killed'.""" + self._create_or_recreate_pod() + self.pod.kill() + self.results['actions'].append('killed %s' % self.pod.name) + self.update_pod_result() + + def make_paused(self): + """Run actions if desired state is 'paused'.""" + changed = self._create_or_recreate_pod() + if self.pod.paused: + self.update_pod_result(changed=changed) + return + self.pod.pause() + self.results['actions'].append('paused %s' % self.pod.name) + self.update_pod_result() + + def make_unpaused(self): + """Run actions if desired state is 'unpaused'.""" + changed = self._create_or_recreate_pod() + if not self.pod.paused: + self.update_pod_result(changed=changed) + return + self.pod.unpause() + self.results['actions'].append('unpaused %s' % self.pod.name) + self.update_pod_result() + + def make_started(self): + """Run actions if desired state is 'started'.""" + changed = self._create_or_recreate_pod() + if not changed and self.pod.running: + self.update_pod_result(changed=changed) + return + + # self.pod.unpause() TODO(sshnaidm): to unpause if state == started? + self.pod.start() + self.results['actions'].append('started %s' % self.pod.name) + self.update_pod_result() + + def make_stopped(self): + """Run actions if desired state is 'stopped'.""" + changed = self._create_or_recreate_pod() + if changed or self.pod.stopped: + self.update_pod_result(changed=changed) + return + elif self.pod.running: + self.pod.stop() + self.results['actions'].append('stopped %s' % self.pod.name) + self.update_pod_result() + + def make_absent(self): + """Run actions if desired state is 'absent'.""" + if not self.pod.exists: + self.results.update({'changed': False}) + elif self.pod.exists: + self.pod.delete() + self.results['actions'].append('deleted %s' % self.pod.name) + self.results.update({'changed': True}) + self.results.update({'pod': {}, + 'podman_actions': self.pod.actions}) diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/__init__.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/__init__.py diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_container.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_container.py new file mode 100644 index 00000000..99b14a39 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_container.py @@ -0,0 +1,916 @@ +#!/usr/bin/python +# Copyright (c) 2020 Red Hat +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# flake8: noqa: E501 + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = r""" +module: podman_container +author: + - "Sagi Shnaidman (@sshnaidm)" +version_added: '1.0.0' +short_description: Manage podman containers +notes: [] +description: + - Start, stop, restart and manage Podman containers +requirements: + - podman +options: + name: + description: + - Name of the container + required: True + type: str + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the + machine running C(podman) + default: 'podman' + type: str + state: + description: + - I(absent) - A container matching the specified name will be stopped and + removed. + - I(present) - Asserts the existence of a container matching the name and + any provided configuration parameters. If no container matches the + name, a container will be created. If a container matches the name but + the provided configuration does not match, the container will be + updated, if it can be. If it cannot be updated, it will be removed and + re-created with the requested config. Image version will be taken into + account when comparing configuration. Use the recreate option to force + the re-creation of the matching container. + - I(started) - Asserts there is a running container matching the name and + any provided configuration. If no container matches the name, a + container will be created and started. Use recreate to always re-create + a matching container, even if it is running. Use force_restart to force + a matching container to be stopped and restarted. + - I(stopped) - Asserts that the container is first I(present), and then + if the container is running moves it to a stopped state. + type: str + default: started + choices: + - absent + - present + - stopped + - started + image: + description: + - Repository path (or image name) and tag used to create the container. + If an image is not found, the image will be pulled from the registry. + If no tag is included, C(latest) will be used. + - Can also be an image ID. If this is the case, the image is assumed to + be available locally. + type: str + annotation: + description: + - Add an annotation to the container. The format is key value, multiple + times. + type: dict + authfile: + description: + - Path of the authentication file. Default is + ``${XDG_RUNTIME_DIR}/containers/auth.json`` + (Not available for remote commands) You can also override the default + path of the authentication file by setting the ``REGISTRY_AUTH_FILE`` + environment variable. ``export REGISTRY_AUTH_FILE=path`` + type: path + blkio_weight: + description: + - Block IO weight (relative weight) accepts a weight value between 10 and + 1000 + type: int + blkio_weight_device: + description: + - Block IO weight (relative device weight, format DEVICE_NAME[:]WEIGHT). + type: dict + cap_add: + description: + - List of capabilities to add to the container. + type: list + elements: str + aliases: + - capabilities + cap_drop: + description: + - List of capabilities to drop from the container. + type: list + elements: str + cgroup_parent: + description: + - Path to cgroups under which the cgroup for the container will be + created. + If the path is not absolute, the path is considered to be relative to + the cgroups path of the init process. Cgroups will be created if they + do not already exist. + type: path + cgroupns: + description: + - Path to cgroups under which the cgroup for the container will be + created. + type: str + cgroups: + description: + - Determines whether the container will create CGroups. + Valid values are enabled and disabled, which the default being enabled. + The disabled option will force the container to not create CGroups, + and thus conflicts with CGroup options cgroupns and cgroup-parent. + type: str + choices: + - default + - disabled + cidfile: + description: + - Write the container ID to the file + type: path + cmd_args: + description: + - Any additional command options you want to pass to podman command, + cmd_args - ['--other-param', 'value'] + Be aware module doesn't support idempotency if this is set. + type: list + elements: str + conmon_pidfile: + description: + - Write the pid of the conmon process to a file. + conmon runs in a separate process than Podman, + so this is necessary when using systemd to restart Podman containers. + type: path + command: + description: + - Override command of container. Can be a string or a list. + type: raw + cpu_period: + description: + - Limit the CPU real-time period in microseconds + type: int + cpu_rt_period: + description: + - Limit the CPU real-time period in microseconds. + Limit the container's Real Time CPU usage. This flag tell the kernel to + restrict the container's Real Time CPU usage to the period you specify. + type: int + cpu_rt_runtime: + description: + - Limit the CPU real-time runtime in microseconds. + This flag tells the kernel to limit the amount of time in a given CPU + period Real Time tasks may consume. + type: int + cpu_shares: + description: + - CPU shares (relative weight) + type: int + cpus: + description: + - Number of CPUs. The default is 0.0 which means no limit. + type: str + cpuset_cpus: + description: + - CPUs in which to allow execution (0-3, 0,1) + type: str + cpuset_mems: + description: + - Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only + effective on NUMA systems. + type: str + detach: + description: + - Run container in detach mode + type: bool + default: True + debug: + description: + - Return additional information which can be helpful for investigations. + type: bool + default: False + detach_keys: + description: + - Override the key sequence for detaching a container. Format is a single + character or ctrl-value + type: str + device: + description: + - Add a host device to the container. + The format is <device-on-host>[:<device-on-container>][:<permissions>] + (e.g. device /dev/sdc:/dev/xvdc:rwm) + type: list + elements: str + device_read_bps: + description: + - Limit read rate (bytes per second) from a device + (e.g. device-read-bps /dev/sda:1mb) + type: list + device_read_iops: + description: + - Limit read rate (IO per second) from a device + (e.g. device-read-iops /dev/sda:1000) + type: list + device_write_bps: + description: + - Limit write rate (bytes per second) to a device + (e.g. device-write-bps /dev/sda:1mb) + type: list + device_write_iops: + description: + - Limit write rate (IO per second) to a device + (e.g. device-write-iops /dev/sda:1000) + type: list + dns: + description: + - Set custom DNS servers + type: list + elements: str + aliases: + - dns_servers + dns_option: + description: + - Set custom DNS options + type: str + aliases: + - dns_opts + dns_search: + description: + - Set custom DNS search domains (Use dns_search with '' if you don't wish + to set the search domain) + type: str + aliases: + - dns_search_domains + entrypoint: + description: + - Overwrite the default ENTRYPOINT of the image + type: str + env: + description: + - Set environment variables. + This option allows you to specify arbitrary environment variables that + are available for the process that will be launched inside of the + container. + type: dict + env_file: + description: + - Read in a line delimited file of environment variables + type: path + env_host: + description: + - Use all current host environment variables in container. + Defaults to false. + type: bool + etc_hosts: + description: + - Dict of host-to-IP mappings, where each host name is a key in the + dictionary. Each host name will be added to the container's + ``/etc/hosts`` file. + type: dict + aliases: + - add_hosts + expose: + description: + - Expose a port, or a range of ports (e.g. expose "3300-3310") to set up + port redirection on the host system. + type: list + elements: str + aliases: + - exposed + - exposed_ports + force_restart: + description: + - Force restart of container. + type: bool + default: False + aliases: + - restart + gidmap: + description: + - Run the container in a new user namespace using the supplied mapping. + type: list + elements: str + group_add: + description: + - Add additional groups to run as + type: list + aliases: + - groups + healthcheck: + description: + - Set or alter a healthcheck command for a container. + type: str + healthcheck_interval: + description: + - Set an interval for the healthchecks + (a value of disable results in no automatic timer setup) + (default "30s") + type: str + healthcheck_retries: + description: + - The number of retries allowed before a healthcheck is considered to be + unhealthy. The default value is 3. + type: int + healthcheck_start_period: + description: + - The initialization time needed for a container to bootstrap. + The value can be expressed in time format like 2m3s. The default value + is 0s + type: str + healthcheck_timeout: + description: + - The maximum time allowed to complete the healthcheck before an interval + is considered failed. Like start-period, the value can be expressed in + a time format such as 1m22s. The default value is 30s + type: str + hostname: + description: + - Container host name. Sets the container host name that is available + inside the container. + type: str + http_proxy: + description: + - By default proxy environment variables are passed into the container if + set for the podman process. This can be disabled by setting the + http_proxy option to false. The environment variables passed in + include http_proxy, https_proxy, ftp_proxy, no_proxy, and also the + upper case versions of those. + Defaults to true + type: bool + image_volume: + description: + - Tells podman how to handle the builtin image volumes. + The options are bind, tmpfs, or ignore (default bind) + type: str + choices: + - 'bind' + - 'tmpfs' + - 'ignore' + image_strict: + description: + - Whether to compare images in idempotency by taking into account a full + name with registry and namespaces. + type: bool + default: False + init: + description: + - Run an init inside the container that forwards signals and reaps + processes. The default is false. + type: bool + init_path: + description: + - Path to the container-init binary. + type: str + interactive: + description: + - Keep STDIN open even if not attached. The default is false. + When set to true, keep stdin open even if not attached. + The default is false. + type: bool + ip: + description: + - Specify a static IP address for the container, for example + '10.88.64.128'. + Can only be used if no additional CNI networks to join were specified + via 'network:', and if the container is not joining another container's + network namespace via 'network container:<name|id>'. + The address must be within the default CNI network's pool + (default 10.88.0.0/16). + type: str + ipc: + description: + - Default is to create a private IPC namespace (POSIX SysV IPC) for the + container + type: str + aliases: + - ipc_mode + kernel_memory: + description: + - Kernel memory limit + (format <number>[<unit>], where unit = b, k, m or g) + Note - idempotency is supported for integers only. + type: str + label: + description: + - Add metadata to a container, pass dictionary of label names and values + aliases: + - labels + type: dict + label_file: + description: + - Read in a line delimited file of labels + type: str + log_driver: + description: + - Logging driver. Used to set the log driver for the container. + For example log_driver "k8s-file". + type: str + choices: + - k8s-file + - journald + - json-file + log_level: + description: + - Logging level for Podman. Log messages above specified level + ("debug"|"info"|"warn"|"error"|"fatal"|"panic") (default "error") + type: str + choices: + - debug + - info + - warn + - error + - fatal + - panic + log_opt: + description: + - Logging driver specific options. Used to set the path to the container + log file. + type: dict + aliases: + - log_options + suboptions: + path: + description: + - Specify a path to the log file (e.g. /var/log/container/mycontainer.json). + type: str + required: false + max_size: + description: + - Specify a max size of the log file (e.g 10mb). + type: str + required: false + tag: + description: + - Specify a custom log tag for the container. + type: str + required: false + + mac_address: + description: + - Specify a MAC address for the container, for example + '92:d0:c6:0a:29:33'. + Don't forget that it must be unique within one Ethernet network. + type: str + memory: + description: + - Memory limit (format 10k, where unit = b, k, m or g) + Note - idempotency is supported for integers only. + type: str + memory_reservation: + description: + - Memory soft limit (format 100m, where unit = b, k, m or g) + Note - idempotency is supported for integers only. + type: str + memory_swap: + description: + - A limit value equal to memory plus swap. Must be used with the -m + (--memory) flag. + The swap LIMIT should always be larger than -m (--memory) value. + By default, the swap LIMIT will be set to double the value of --memory + Note - idempotency is supported for integers only. + type: str + memory_swappiness: + description: + - Tune a container's memory swappiness behavior. Accepts an integer + between 0 and 100. + type: int + mount: + description: + - Attach a filesystem mount to the container. bind or tmpfs + For example mount + "type=bind,source=/path/on/host,destination=/path/in/container" + type: str + network: + description: + - Set the Network mode for the container + * bridge create a network stack on the default bridge + * none no networking + * container:<name|id> reuse another container's network stack + * host use the podman host network stack. + * <network-name>|<network-id> connect to a user-defined network + * ns:<path> path to a network namespace to join + * slirp4netns use slirp4netns to create a user network stack. + This is the default for rootless containers + type: list + elements: str + aliases: + - net + - network_mode + no_hosts: + description: + - Do not create /etc/hosts for the container + Default is false. + type: bool + oom_kill_disable: + description: + - Whether to disable OOM Killer for the container or not. + Default is false. + type: bool + oom_score_adj: + description: + - Tune the host's OOM preferences for containers (accepts -1000 to 1000) + type: int + pid: + description: + - Set the PID mode for the container + type: str + aliases: + - pid_mode + pids_limit: + description: + - Tune the container's PIDs limit. Set -1 to have unlimited PIDs for the + container. + type: str + pod: + description: + - Run container in an existing pod. + If you want podman to make the pod for you, preference the pod name + with "new:" + type: str + privileged: + description: + - Give extended privileges to this container. The default is false. + type: bool + publish: + description: + - Publish a container's port, or range of ports, to the host. + Format - ip:hostPort:containerPort | ip::containerPort | + hostPort:containerPort | containerPort + In case of only containerPort is set, the hostPort will chosen + randomly by Podman. + type: list + elements: str + aliases: + - ports + - published + - published_ports + publish_all: + description: + - Publish all exposed ports to random ports on the host interfaces. The + default is false. + type: bool + read_only: + description: + - Mount the container's root filesystem as read only. Default is false + type: bool + read_only_tmpfs: + description: + - If container is running in --read-only mode, then mount a read-write + tmpfs on /run, /tmp, and /var/tmp. The default is true + type: bool + recreate: + description: + - Use with present and started states to force the re-creation of an + existing container. + type: bool + default: False + restart_policy: + description: + - Restart policy to follow when containers exit. + Restart policy will not take effect if a container is stopped via the + podman kill or podman stop commands. Valid values are + * no - Do not restart containers on exit + * on-failure[:max_retries] - Restart containers when they exit with a + non-0 exit code, retrying indefinitely + or until the optional max_retries count is hit + * always - Restart containers when they exit, regardless of status, + retrying indefinitely + type: str + rm: + description: + - Automatically remove the container when it exits. The default is false. + type: bool + aliases: + - remove + - auto_remove + rootfs: + description: + - If true, the first argument refers to an exploded container on the file + system. The default is false. + type: bool + security_opt: + description: + - Security Options. For example security_opt "seccomp=unconfined" + type: list + elements: str + shm_size: + description: + - Size of /dev/shm. The format is <number><unit>. number must be greater + than 0. + Unit is optional and can be b (bytes), k (kilobytes), m(megabytes), or + g (gigabytes). + If you omit the unit, the system uses bytes. If you omit the size + entirely, the system uses 64m + type: str + sig_proxy: + description: + - Proxy signals sent to the podman run command to the container process. + SIGCHLD, SIGSTOP, and SIGKILL are not proxied. The default is true. + type: bool + stop_signal: + description: + - Signal to stop a container. Default is SIGTERM. + type: int + stop_timeout: + description: + - Timeout (in seconds) to stop a container. Default is 10. + type: int + subgidname: + description: + - Run the container in a new user namespace using the map with 'name' in + the /etc/subgid file. + type: str + subuidname: + description: + - Run the container in a new user namespace using the map with 'name' in + the /etc/subuid file. + type: str + sysctl: + description: + - Configure namespaced kernel parameters at runtime + type: dict + systemd: + description: + - Run container in systemd mode. The default is true. + type: bool + tmpfs: + description: + - Create a tmpfs mount. For example tmpfs + "/tmp" "rw,size=787448k,mode=1777" + type: dict + tty: + description: + - Allocate a pseudo-TTY. The default is false. + type: bool + uidmap: + description: + - Run the container in a new user namespace using the supplied mapping. + type: list + elements: str + ulimit: + description: + - Ulimit options + type: list + aliases: + - ulimits + user: + description: + - Sets the username or UID used and optionally the groupname or GID for + the specified command. + type: str + userns: + description: + - Set the user namespace mode for the container. + It defaults to the PODMAN_USERNS environment variable. + An empty value means user namespaces are disabled. + type: str + aliases: + - userns_mode + uts: + description: + - Set the UTS mode for the container + type: str + volume: + description: + - Create a bind mount. If you specify, volume /HOST-DIR:/CONTAINER-DIR, + podman bind mounts /HOST-DIR in the host to /CONTAINER-DIR in the + podman container. + type: list + elements: str + aliases: + - volumes + volumes_from: + description: + - Mount volumes from the specified container(s). + type: list + elements: str + workdir: + description: + - Working directory inside the container. + The default working directory for running binaries within a container + is the root directory (/). + type: str + aliases: + - working_dir +""" + +EXAMPLES = r""" +- name: Run container + containers.podman.podman_container: + name: container + image: quay.io/bitnami/wildfly + state: started + +- name: Create a data container + containers.podman.podman_container: + name: mydata + image: busybox + volume: + - /tmp/data + +- name: Re-create a redis container + containers.podman.podman_container: + name: myredis + image: redis + command: redis-server --appendonly yes + state: present + recreate: yes + expose: + - 6379 + volumes_from: + - mydata + +- name: Restart a container + containers.podman.podman_container: + name: myapplication + image: redis + state: started + restart: yes + etc_hosts: + other: "127.0.0.1" + restart_policy: "no" + device: "/dev/sda:/dev/xvda:rwm" + ports: + - "8080:9000" + - "127.0.0.1:8081:9001/udp" + env: + SECRET_KEY: "ssssh" + BOOLEAN_KEY: "yes" + +- name: Container present + containers.podman.podman_container: + name: mycontainer + state: present + image: ubuntu:14.04 + command: "sleep 1d" + +- name: Stop a container + containers.podman.podman_container: + name: mycontainer + state: stopped + +- name: Start 4 load-balanced containers + containers.podman.podman_container: + name: "container{{ item }}" + recreate: yes + image: someuser/anotherappimage + command: sleep 1d + with_sequence: count=4 + +- name: remove container + containers.podman.podman_container: + name: ohno + state: absent + +- name: Writing output + containers.podman.podman_container: + name: myservice + image: busybox + log_options: path=/var/log/container/mycontainer.json + log_driver: k8s-file +""" + +RETURN = r""" +container: + description: + - Facts representing the current state of the container. Matches the + podman inspection output. + - Note that facts are part of the registered vars since Ansible 2.8. For + compatibility reasons, the facts + are also accessible directly as C(podman_container). Note that the + returned fact will be removed in Ansible 2.12. + - Empty if C(state) is I(absent). + returned: always + type: dict + sample: '{ + "AppArmorProfile": "", + "Args": [ + "sh" + ], + "BoundingCaps": [ + "CAP_CHOWN", + ... + ], + "Config": { + "Annotations": { + "io.kubernetes.cri-o.ContainerType": "sandbox", + "io.kubernetes.cri-o.TTY": "false" + }, + "AttachStderr": false, + "AttachStdin": false, + "AttachStdout": false, + "Cmd": [ + "sh" + ], + "Domainname": "", + "Entrypoint": "", + "Env": [ + "PATH=/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + "HOSTNAME=", + "container=podman" + ], + "Hostname": "", + "Image": "docker.io/library/busybox:latest", + "Labels": null, + "OpenStdin": false, + "StdinOnce": false, + "StopSignal": 15, + "Tty": false, + "User": { + "gid": 0, + "uid": 0 + }, + "Volumes": null, + "WorkingDir": "/" + }, + "ConmonPidFile": "...", + "Created": "2019-06-17T19:13:09.873858307+03:00", + "Dependencies": [], + "Driver": "overlay", + "EffectiveCaps": [ + "CAP_CHOWN", + ... + ], + "ExecIDs": [], + "ExitCommand": [ + "/usr/bin/podman", + "--root", + ... + ], + "GraphDriver": { + ... + }, + "HostConfig": { + ... + }, + "HostnamePath": "...", + "HostsPath": "...", + "ID": "...", + "Image": "...", + "ImageName": "docker.io/library/busybox:latest", + "IsInfra": false, + "LogPath": "/tmp/container/mycontainer.json", + "MountLabel": "system_u:object_r:container_file_t:s0:c282,c782", + "Mounts": [ + ... + ], + "Name": "myservice", + "Namespace": "", + "NetworkSettings": { + "Bridge": "", + ... + }, + "Path": "sh", + "ProcessLabel": "system_u:system_r:container_t:s0:c282,c782", + "ResolvConfPath": "...", + "RestartCount": 0, + "Rootfs": "", + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "2019-06-17T19:13:10.157518963+03:00", + "Healthcheck": { + "FailingStreak": 0, + "Log": null, + "Status": "" + }, + "OOMKilled": false, + "OciVersion": "1.0.1-dev", + "Paused": false, + "Pid": 4083, + "Restarting": false, + "Running": false, + "StartedAt": "2019-06-17T19:13:10.152479729+03:00", + "Status": "exited" + }, + "StaticDir": "..." + ... + }' +""" + +from ansible.module_utils.basic import AnsibleModule # noqa: F402 +from ..module_utils.podman.podman_container_lib import PodmanManager # noqa: F402 +from ..module_utils.podman.podman_container_lib import ARGUMENTS_SPEC_CONTAINER # noqa: F402 + + +def main(): + module = AnsibleModule( + argument_spec=ARGUMENTS_SPEC_CONTAINER, + mutually_exclusive=( + ['no_hosts', 'etc_hosts'], + ), + supports_check_mode=True, + ) + + # work on input vars + if module.params['state'] in ['started', 'present'] and \ + not module.params['image']: + module.fail_json(msg="State '%s' required image to be configured!" % + module.params['state']) + + results = PodmanManager(module, module.params).execute() + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_container_info.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_container_info.py new file mode 100644 index 00000000..d3ec55c8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_container_info.py @@ -0,0 +1,405 @@ +#!/usr/bin/python +# Copyright (c) 2020 Red Hat +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +module: podman_container_info +author: + - Sagi Shnaidman (@sshnaidm) + - Emilien Macchi (@EmilienM) +short_description: Gather facts about containers using podman +notes: + - Podman may require elevated privileges in order to run properly. +description: + - Gather facts about containers using C(podman) +requirements: + - "Podman installed on host" +options: + name: + description: + - List of container names to gather facts about. If no name is given + return facts about all containers. + type: list + elements: str + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the + machine running C(podman) + default: 'podman' + type: str +''' + +EXAMPLES = r""" +- name: Gather facts for all containers + containers.podman.podman_container_info: + +- name: Gather facts on a specific container + containers.podman.podman_container_info: + name: web1 + +- name: Gather facts on several containers + containers.podman.podman_container_info: + name: + - redis + - web1 +""" + +RETURN = r""" +containers: + description: Facts from all or specificed containers + returned: always + type: list + elements: dict + sample: [ + { + "Id": "c5c39f9b80a6ea2ad665aa9946435934e478a0c5322da835f3883872f", + "Created": "2019-10-01T12:51:00.233106443Z", + "Path": "dumb-init", + "Args": [ + "--single-child", + "--", + "kolla_start" + ], + "State": { + "OciVersion": "1.0.1-dev", + "Status": "configured", + "Running": false, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 0, + "ExitCode": 0, + "Error": "", + "StartedAt": "0001-01-01T00:00:00Z", + "FinishedAt": "0001-01-01T00:00:00Z", + "Healthcheck": { + "Status": "", + "FailingStreak": 0, + "Log": null + } + }, + "Image": "0e267acda67d0ebd643e900d820a91b961d859743039e620191ca1", + "ImageName": "docker.io/tripleomaster/centos-haproxy:latest", + "Rootfs": "", + "Pod": "", + "ResolvConfPath": "", + "HostnamePath": "", + "HostsPath": "", + "OCIRuntime": "runc", + "Name": "haproxy", + "RestartCount": 0, + "Driver": "overlay", + "MountLabel": "system_u:object_r:svirt_sandbox_file_t:s0:c78,c866", + "ProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c785,c866", + "AppArmorProfile": "", + "EffectiveCaps": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "BoundingCaps": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "ExecIDs": [], + "GraphDriver": { + "Name": "overlay" + }, + "Mounts": [], + "Dependencies": [], + "NetworkSettings": { + "Bridge": "", + "SandboxID": "", + "HairpinMode": false, + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "Ports": [], + "SandboxKey": "", + "SecondaryIPAddresses": null, + "SecondaryIPv6Addresses": null, + "EndpointID": "", + "Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "IPAddress": "", + "IPPrefixLen": 0, + "IPv6Gateway": "", + "MacAddress": "" + }, + "ExitCommand": [ + "/usr/bin/podman", + "--root", + "/var/lib/containers/storage", + "--runroot", + "/var/run/containers/storage", + "--log-level", + "error", + "--cgroup-manager", + "systemd", + "--tmpdir", + "/var/run/libpod", + "--runtime", + "runc", + "--storage-driver", + "overlay", + "--events-backend", + "journald", + "container", + "cleanup", + "c9e813703f9b80a6ea2ad665aa9946435934e478a0c5322da835f3883872f" + ], + "Namespace": "", + "IsInfra": false, + "Config": { + "Hostname": "c5c39e813703", + "Domainname": "", + "User": "", + "AttachStdin": false, + "AttachStdout": false, + "AttachStderr": false, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": [ + "PATH=/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + "HOSTNAME=", + "container=oci", + "KOLLA_INSTALL_METATYPE=rdo", + "KOLLA_BASE_DISTRO=centos", + "KOLLA_INSTALL_TYPE=binary", + "KOLLA_DISTRO_PYTHON_VERSION=2.7", + "KOLLA_BASE_ARCH=x86_64" + ], + "Cmd": [ + "kolla_start" + ], + "Image": "docker.io/tripleomaster/centos-haproxy:latest", + "Volumes": null, + "WorkingDir": "/", + "Entrypoint": "dumb-init --single-child --", + "OnBuild": null, + "Labels": { + "build-date": "20190919", + "kolla_version": "8.1.0", + "name": "haproxy", + "org.label-schema.build-date": "20190801", + "org.label-schema.license": "GPLv2", + "org.label-schema.name": "CentOS Base Image", + "org.label-schema.schema-version": "1.0", + "org.label-schema.vendor": "CentOS" + }, + "Annotations": { + "io.kubernetes.cri-o.ContainerType": "sandbox", + "io.kubernetes.cri-o.TTY": "false", + "io.podman.annotations.autoremove": "FALSE", + "io.podman.annotations.init": "FALSE", + "io.podman.annotations.privileged": "FALSE", + "io.podman.annotations.publish-all": "FALSE" + }, + "StopSignal": 15 + }, + "HostConfig": { + "Binds": [], + "ContainerIDFile": "", + "LogConfig": { + "Type": "k8s-file", + "Config": null + }, + "NetworkMode": "default", + "PortBindings": {}, + "RestartPolicy": { + "Name": "", + "MaximumRetryCount": 0 + }, + "AutoRemove": false, + "VolumeDriver": "", + "VolumesFrom": null, + "CapAdd": [], + "CapDrop": [], + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": [], + "GroupAdd": [], + "IpcMode": "", + "Cgroup": "", + "Links": null, + "OomScoreAdj": 0, + "PidMode": "", + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyRootfs": false, + "SecurityOpt": [], + "Tmpfs": {}, + "UTSMode": "", + "UsernsMode": "", + "ShmSize": 65536000, + "Runtime": "oci", + "ConsoleSize": [ + 0, + 0 + ], + "Isolation": "", + "CpuShares": 0, + "Memory": 0, + "NanoCpus": 0, + "CgroupParent": "", + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "BlkioDeviceReadBps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteIOps": null, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpusetCpus": "", + "CpusetMems": "", + "Devices": [], + "DiskQuota": 0, + "KernelMemory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": -1, + "OomKillDisable": false, + "PidsLimit": 0, + "Ulimits": [ + { + "Name": "RLIMIT_NOFILE", + "Soft": 1048576, + "Hard": 1048576 + }, + { + "Name": "RLIMIT_NPROC", + "Soft": 1048576, + "Hard": 1048576 + } + ], + "CpuCount": 0, + "CpuPercent": 0, + "IOMaximumIOps": 0, + "IOMaximumBandwidth": 0 + } + } + ] +""" + +import json +from ansible.module_utils.basic import AnsibleModule + + +def get_containers_facts(module, executable, name): + """Collect containers facts for all containers or for specified in 'name'. + + Arguments: + module {AnsibleModule} -- instance of AnsibleModule + executable {string} -- binary to execute when inspecting containers + name {list} -- list of names or None in case of all containers + + Returns: + list of containers info, stdout, stderr + """ + if not name: + all_names = [executable, 'container', 'ls', '-q', '-a'] + rc, out, err = module.run_command(all_names) + if rc != 0: + module.fail_json(msg="Unable to get list of containers: %s" % err) + name = out.split() + if not name: + return [], out, err + command = [executable, 'container', 'inspect'] + command.extend(name) + rc, out, err = module.run_command(command) + if rc == 0: + json_out = json.loads(out) if out else None + if json_out is None: + return [], out, err + return json_out, out, err + if rc != 0 and 'no such ' in err: + if len(name) < 2: + return [], out, err + return cycle_over(module, executable, name) + module.fail_json(msg="Unable to gather info for %s: %s" % (",".join(name), err)) + + +def cycle_over(module, executable, name): + """Inspect each container in a cycle in case some of them don't exist. + + Arguments: + module {AnsibleModule} -- instance of AnsibleModule + executable {string} -- binary to execute when inspecting containers + name {list} -- list of containers names to inspect + + Returns: + list of containers info, stdout as empty, stderr + """ + inspection = [] + stderrs = [] + for container in name: + command = [executable, 'container', 'inspect', container] + rc, out, err = module.run_command(command) + if rc != 0 and 'no such ' not in err: + module.fail_json(msg="Unable to gather info for %s: %s" % (container, err)) + if rc == 0 and out: + json_out = json.loads(out) + if json_out: + inspection += json_out + stderrs.append(err) + return inspection, "", "\n".join(stderrs) + + +def main(): + module = AnsibleModule( + argument_spec={ + 'executable': {'type': 'str', 'default': 'podman'}, + 'name': {'type': 'list', 'elements': 'str'}, + }, + supports_check_mode=True, + ) + + name = module.params['name'] + executable = module.get_bin_path(module.params['executable'], required=True) + # pylint: disable=unused-variable + inspect_results, out, err = get_containers_facts(module, executable, name) + + results = { + "changed": False, + "containers": inspect_results, + "stderr": err + } + + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_containers.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_containers.py new file mode 100644 index 00000000..75ebb057 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_containers.py @@ -0,0 +1,162 @@ +#!/usr/bin/python +# Copyright (c) 2020 Red Hat +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: podman_containers +author: + - "Sagi Shnaidman (@sshnaidm)" +version_added: '1.4.0' +short_description: Manage podman containers in a batch +description: + - Manage groups of podman containers +requirements: + - "podman" +options: + containers: + description: + - List of dictionaries with data for running containers for podman_container module. + required: True + type: list + elements: dict + debug: + description: + - Return additional information which can be helpful for investigations. + type: bool + default: False +''' + +EXAMPLES = ''' +- name: Run three containers at once + podman_containers: + containers: + - name: alpine + image: alpine + command: sleep 1d + - name: web + image: nginx + - name: test + image: python:3-alpine + command: python -V +''' + +from copy import deepcopy # noqa: F402 + +from ansible.module_utils.basic import AnsibleModule # noqa: F402 +from ..module_utils.podman.podman_container_lib import PodmanManager # noqa: F402 +from ..module_utils.podman.podman_container_lib import ARGUMENTS_SPEC_CONTAINER # noqa: F402 + + +def init_options(): + default = {} + opts = ARGUMENTS_SPEC_CONTAINER + for k, v in opts.items(): + if 'default' in v: + default[k] = v['default'] + else: + default[k] = None + return default + + +def update_options(opts_dict, container): + aliases = {} + for k, v in ARGUMENTS_SPEC_CONTAINER.items(): + if 'aliases' in v: + for alias in v['aliases']: + aliases[alias] = k + for k in list(container): + if k in aliases: + key = aliases[k] + opts_dict[key] = container[k] + container.pop(k) + opts_dict.update(container) + return opts_dict + + +def combine(results): + changed = any([i.get('changed', False) for i in results]) + failed = any([i.get('failed', False) for i in results]) + actions = [] + podman_actions = [] + containers = [] + podman_version = '' + diffs = {} + stderr = '' + stdout = '' + for i in results: + if 'actions' in i and i['actions']: + actions += i['actions'] + if 'podman_actions' in i and i['podman_actions']: + podman_actions += i['podman_actions'] + if 'container' in i and i['container']: + containers.append(i['container']) + if 'podman_version' in i: + podman_version = i['podman_version'] + if 'diff' in i: + diffs[i['container']['Name']] = i['diff'] + if 'stderr' in i: + stderr += i['stderr'] + if 'stdout' in i: + stdout += i['stdout'] + + total = { + 'changed': changed, + 'failed': failed, + 'actions': actions, + 'podman_actions': podman_actions, + 'containers': containers, + 'stdout': stdout, + 'stderr': stderr, + } + if podman_version: + total['podman_version'] = podman_version + if diffs: + before = after = '' + for k, v in diffs.items(): + before += "".join([str(k), ": ", str(v['before']), "\n"]) + after += "".join([str(k), ": ", str(v['after']), "\n"]) + total['diff'] = { + 'before': before, + 'after': after + } + return total + + +def check_input_strict(container): + if container['state'] in ['started', 'present'] and not container['image']: + return "State '%s' required image to be configured!" % container['state'] + + +def main(): + module = AnsibleModule( + argument_spec=dict( + containers=dict(type='list', elements='dict', required=True), + debug=dict(type='bool', default=False), + ), + supports_check_mode=True, + ) + # work on input vars + + results = [] + default_options_templ = init_options() + for container in module.params['containers']: + options_dict = deepcopy(default_options_templ) + options_dict = update_options(options_dict, container) + options_dict['debug'] = module.params['debug'] or options_dict['debug'] + test_input = check_input_strict(options_dict) + if test_input: + module.fail_json( + msg="Failed to run container %s because: %s" % (options_dict['name'], test_input)) + res = PodmanManager(module, options_dict).execute() + results.append(res) + total_results = combine(results) + module.exit_json(**total_results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_image.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_image.py new file mode 100644 index 00000000..186f0498 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_image.py @@ -0,0 +1,774 @@ +#!/usr/bin/python +# Copyright (c) 2018 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' + module: podman_image + author: + - Sam Doran (@samdoran) + short_description: Pull images for use by podman + notes: [] + description: + - Build, pull, or push images using Podman. + options: + name: + description: + - Name of the image to pull, push, or delete. It may contain a tag using the format C(image:tag). + required: True + type: str + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the machine running C(podman). + default: 'podman' + type: str + ca_cert_dir: + description: + - Path to directory containing TLS certificates and keys to use. + type: 'path' + tag: + description: + - Tag of the image to pull, push, or delete. + default: "latest" + type: str + pull: + description: Whether or not to pull the image. + default: True + type: bool + push: + description: Whether or not to push an image. + default: False + type: bool + path: + description: Path to directory containing the build file. + type: str + force: + description: + - Whether or not to force push or pull an image. + - When building, force the build even if the image already exists. + type: bool + default: False + state: + description: + - Whether an image should be present, absent, or built. + default: "present" + type: str + choices: + - present + - absent + - build + validate_certs: + description: + - Require HTTPS and validate certificates when pulling or pushing. Also used during build if a pull or push is necessary. + default: True + type: bool + aliases: + - tlsverify + - tls_verify + password: + description: + - Password to use when authenticating to remote registries. + type: str + username: + description: + - username to use when authenticating to remote registries. + type: str + auth_file: + description: + - Path to file containing authorization credentials to the remote registry. + aliases: + - authfile + type: path + build: + description: Arguments that control image build. + type: dict + aliases: + - build_args + - buildargs + suboptions: + volume: + description: + - Specify multiple volume / mount options to mount one or more mounts to a container. + type: list + elements: str + annotation: + description: + - Dictionary of key=value pairs to add to the image. Only works with OCI images. Ignored for Docker containers. + type: dict + force_rm: + description: + - Always remove intermediate containers after a build, even if the build is unsuccessful. + type: bool + default: False + format: + description: + - Format of the built image. + type: str + choices: + - docker + - oci + default: "oci" + cache: + description: + - Whether or not to use cached layers when building an image + type: bool + default: True + rm: + description: Remove intermediate containers after a successful build + type: bool + default: True + extra_args: + description: + - Extra args to pass to build, if executed. Does not idempotently check for new build args. + type: str + push_args: + description: Arguments that control pushing images. + type: dict + suboptions: + compress: + description: + - Compress tarball image layers when pushing to a directory using the 'dir' transport. + type: bool + format: + description: + - Manifest type to use when pushing an image using the 'dir' transport (default is manifest type of source). + type: str + choices: + - oci + - v2s1 + - v2s2 + remove_signatures: + description: Discard any pre-existing signatures in the image + type: bool + sign_by: + description: + - Path to a key file to use to sign the image. + type: str + dest: + description: Path or URL where image will be pushed. + type: str + aliases: + - destination + transport: + description: + - Transport to use when pushing in image. If no transport is set, will attempt to push to a remote registry. + type: str + choices: + - dir + - docker-archive + - docker-daemon + - oci-archive + - ostree +''' + +EXAMPLES = r""" +- name: Pull an image + container.podman.podman_image: + name: quay.io/bitnami/wildfly + +- name: Remove an image + container.podman.podman_image: + name: quay.io/bitnami/wildfly + state: absent + +- name: Pull a specific version of an image + container.podman.podman_image: + name: redis + tag: 4 + +- name: Build a basic OCI image + container.podman.podman_image: + name: nginx + path: /path/to/build/dir + +- name: Build a basic OCI image with advanced parameters + container.podman.podman_image: + name: nginx + path: /path/to/build/dir + build: + cache: no + force_rm: yes + format: oci + annotation: + app: nginx + function: proxy + info: Load balancer for my cool app + +- name: Build a Docker formatted image + container.podman.podman_image: + name: nginx + path: /path/to/build/dir + build: + format: docker + +- name: Build and push an image using existing credentials + container.podman.podman_image: + name: nginx + path: /path/to/build/dir + push: yes + push_args: + dest: quay.io/acme + +- name: Build and push an image using an auth file + container.podman.podman_image: + name: nginx + push: yes + auth_file: /etc/containers/auth.json + push_args: + dest: quay.io/acme + +- name: Build and push an image using username and password + container.podman.podman_image: + name: nginx + push: yes + username: bugs + password: "{{ vault_registry_password }}" + push_args: + dest: quay.io/acme + +- name: Build and push an image to multiple registries + container.podman.podman_image: + name: "{{ item }}" + path: /path/to/build/dir + push: yes + auth_file: /etc/containers/auth.json + loop: + - quay.io/acme/nginx + - docker.io/acme/nginx + +- name: Build and push an image to multiple registries with separate parameters + container.podman.podman_image: + name: "{{ item.name }}" + tag: "{{ item.tag }}" + path: /path/to/build/dir + push: yes + auth_file: /etc/containers/auth.json + push_args: + dest: "{{ item.dest }}" + loop: + - name: nginx + tag: 4 + dest: docker.io/acme + + - name: nginx + tag: 3 + dest: docker.io/acme +""" + +RETURN = r""" + image: + description: + - Image inspection results for the image that was pulled, pushed, or built. + returned: success + type: dict + sample: [ + { + "Annotations": {}, + "Architecture": "amd64", + "Author": "", + "Comment": "from Bitnami with love", + "ContainerConfig": { + "Cmd": [ + "/run.sh" + ], + "Entrypoint": [ + "/app-entrypoint.sh" + ], + "Env": [ + "PATH=/opt/bitnami/java/bin:/opt/bitnami/wildfly/bin:/opt/bitnami/nami/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "IMAGE_OS=debian-9", + "NAMI_VERSION=1.0.0-1", + "GPG_KEY_SERVERS_LIST=ha.pool.sks-keyservers.net", + "TINI_VERSION=v0.13.2", + "TINI_GPG_KEY=595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7", + "GOSU_VERSION=1.10", + "GOSU_GPG_KEY=B42F6819007F00F88E364FD4036A9C25BF357DD4", + "BITNAMI_IMAGE_VERSION=16.0.0-debian-9-r27", + "BITNAMI_PKG_CHMOD=-R g+rwX", + "BITNAMI_PKG_EXTRA_DIRS=/home/wildfly", + "HOME=/", + "BITNAMI_APP_NAME=wildfly", + "NAMI_PREFIX=/.nami", + "WILDFLY_HOME=/home/wildfly", + "WILDFLY_JAVA_HOME=", + "WILDFLY_JAVA_OPTS=", + "WILDFLY_MANAGEMENT_HTTP_PORT_NUMBER=9990", + "WILDFLY_PASSWORD=bitnami", + "WILDFLY_PUBLIC_CONSOLE=true", + "WILDFLY_SERVER_AJP_PORT_NUMBER=8009", + "WILDFLY_SERVER_HTTP_PORT_NUMBER=8080", + "WILDFLY_SERVER_INTERFACE=0.0.0.0", + "WILDFLY_USERNAME=user", + "WILDFLY_WILDFLY_HOME=/home/wildfly", + "WILDFLY_WILDFLY_OPTS=-Dwildfly.as.deployment.ondemand=false" + ], + "ExposedPorts": { + "8080/tcp": {}, + "9990/tcp": {} + }, + "Labels": { + "maintainer": "Bitnami <containers@bitnami.com>" + }, + "User": "1001" + }, + "Created": "2019-04-10T05:48:03.553887623Z", + "Digest": "sha256:5a8ab28e314c2222de3feaf6dac94a0436a37fc08979d2722c99d2bef2619a9b", + "GraphDriver": { + "Data": { + "LowerDir": "/var/lib/containers/storage/overlay/142c1beadf1bb09fbd929465ec98c9dca3256638220450efb4214727d0d0680e/diff:/var/lib/containers/s", + "MergedDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99723ed787878dbfea/merged", + "UpperDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99723ed787878dbfea/diff", + "WorkDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99723ed787878dbfea/work" + }, + "Name": "overlay" + }, + "History": [ + { + "comment": "from Bitnami with love", + "created": "2019-04-09T22:27:40.659377677Z" + }, + { + "created": "2019-04-09T22:38:53.86336555Z", + "created_by": "/bin/sh -c #(nop) LABEL maintainer=Bitnami <containers@bitnami.com>", + "empty_layer": true + }, + { + "created": "2019-04-09T22:38:54.022778765Z", + "created_by": "/bin/sh -c #(nop) ENV IMAGE_OS=debian-9", + "empty_layer": true + }, + ], + "Id": "ace34da54e4af2145e1ad277005adb235a214e4dfe1114c2db9ab460b840f785", + "Labels": { + "maintainer": "Bitnami <containers@bitnami.com>" + }, + "ManifestType": "application/vnd.docker.distribution.manifest.v1+prettyjws", + "Os": "linux", + "Parent": "", + "RepoDigests": [ + "quay.io/bitnami/wildfly@sha256:5a8ab28e314c2222de3feaf6dac94a0436a37fc08979d2722c99d2bef2619a9b" + ], + "RepoTags": [ + "quay.io/bitnami/wildfly:latest" + ], + "RootFS": { + "Layers": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "Type": "layers" + }, + "Size": 466180019, + "User": "1001", + "Version": "18.09.3", + "VirtualSize": 466180019 + } + ] +""" + +import json +import re +import shlex + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.containers.podman.plugins.module_utils.podman.common import run_podman_command + + +class PodmanImageManager(object): + + def __init__(self, module, results): + + super(PodmanImageManager, self).__init__() + + self.module = module + self.results = results + self.name = self.module.params.get('name') + self.executable = self.module.get_bin_path(module.params.get('executable'), required=True) + self.tag = self.module.params.get('tag') + self.pull = self.module.params.get('pull') + self.push = self.module.params.get('push') + self.path = self.module.params.get('path') + self.force = self.module.params.get('force') + self.state = self.module.params.get('state') + self.validate_certs = self.module.params.get('validate_certs') + self.auth_file = self.module.params.get('auth_file') + self.username = self.module.params.get('username') + self.password = self.module.params.get('password') + self.ca_cert_dir = self.module.params.get('ca_cert_dir') + self.build = self.module.params.get('build') + self.push_args = self.module.params.get('push_args') + + repo, repo_tag = parse_repository_tag(self.name) + if repo_tag: + self.name = repo + self.tag = repo_tag + + self.image_name = '{name}:{tag}'.format(name=self.name, tag=self.tag) + + if self.state in ['present', 'build']: + self.present() + + if self.state in ['absent']: + self.absent() + + def _run(self, args, expected_rc=0, ignore_errors=False): + return run_podman_command( + module=self.module, + executable=self.executable, + args=args, + expected_rc=expected_rc, + ignore_errors=ignore_errors) + + def _get_id_from_output(self, lines, startswith=None, contains=None, split_on=' ', maxsplit=1): + layer_ids = [] + for line in lines.splitlines(): + if startswith and line.startswith(startswith) or contains and contains in line: + splitline = line.rsplit(split_on, maxsplit) + layer_ids.append(splitline[1]) + + # Podman 1.4 changed the output to only include the layer id when run in quiet mode + if not layer_ids: + layer_ids = lines.splitlines() + + return(layer_ids[-1]) + + def present(self): + image = self.find_image() + + if image: + digest_before = image[0].get('Digest', image[0].get('digest')) + else: + digest_before = None + + if not image or self.force: + if self.path: + # Build the image + self.results['actions'].append('Built image {image_name} from {path}'.format(image_name=self.image_name, path=self.path)) + if not self.module.check_mode: + image = self.results['image'] = self.build_image() + else: + # Pull the image + self.results['actions'].append('Pulled image {image_name}'.format(image_name=self.image_name)) + if not self.module.check_mode: + image = self.results['image'] = self.pull_image() + + if not image: + image = self.find_image() + digest_after = image[0].get('Digest', image[0].get('digest')) + self.results['changed'] = digest_before != digest_after + + if self.push: + # Push the image + if '/' in self.image_name: + push_format_string = 'Pushed image {image_name}' + else: + push_format_string = 'Pushed image {image_name} to {dest}' + self.results['actions'].append(push_format_string.format(image_name=self.image_name, dest=self.push_args['dest'])) + self.results['changed'] = True + if not self.module.check_mode: + self.results['image'] = self.push_image() + + def absent(self): + image = self.find_image() + + if image: + self.results['actions'].append('Removed image {name}'.format(name=self.name)) + self.results['changed'] = True + self.results['image']['state'] = 'Deleted' + if not self.module.check_mode: + self.remove_image() + + def find_image(self, image_name=None): + if image_name is None: + image_name = self.image_name + args = ['image', 'ls', image_name, '--format', 'json'] + rc, images, err = self._run(args, ignore_errors=True) + if len(images) > 0: + return json.loads(images) + else: + return None + + def inspect_image(self, image_name=None): + if image_name is None: + image_name = self.image_name + args = ['inspect', image_name, '--format', 'json'] + rc, image_data, err = self._run(args) + if len(image_data) > 0: + return json.loads(image_data) + else: + return None + + def pull_image(self, image_name=None): + if image_name is None: + image_name = self.image_name + + args = ['pull', image_name, '-q'] + + if self.auth_file: + args.extend(['--authfile', self.auth_file]) + + if self.username and self.password: + cred_string = '{user}:{password}'.format(user=self.username, password=self.password) + args.extend(['--creds', cred_string]) + + if self.validate_certs is not None: + if self.validate_certs: + args.append('--tls-verify') + else: + args.append('--tls-verify=false') + + if self.ca_cert_dir: + args.extend(['--cert-dir', self.ca_cert_dir]) + + rc, out, err = self._run(args, ignore_errors=True) + if rc != 0: + self.module.fail_json(msg='Failed to pull image {image_name}'.format(image_name=image_name)) + return self.inspect_image(out.strip()) + + def build_image(self): + args = ['build', '-q'] + args.extend(['-t', self.image_name]) + + if self.validate_certs is not None: + if self.validate_certs: + args.append('--tls-verify') + else: + args.append('--tls-verify=false') + + annotation = self.build.get('annotation') + if annotation: + for k, v in annotation.items(): + args.extend(['--annotation', '{k}={v}'.format(k=k, v=v)]) + + if self.ca_cert_dir: + args.extend(['--cert-dir', self.ca_cert_dir]) + + if self.build.get('force_rm'): + args.append('--force-rm') + + image_format = self.build.get('format') + if image_format: + args.extend(['--format', image_format]) + + if not self.build.get('cache'): + args.append('--no-cache') + + if self.build.get('rm'): + args.append('--rm') + + volume = self.build.get('volume') + if volume: + for v in volume: + args.extend(['--volume', v]) + + if self.auth_file: + args.extend(['--authfile', self.auth_file]) + + if self.username and self.password: + cred_string = '{user}:{password}'.format(user=self.username, password=self.password) + args.extend(['--creds', cred_string]) + + extra_args = self.build.get('extra_args') + if extra_args: + args.extend([arg for arg in shlex.split(extra_args)]) + + args.append(self.path) + + rc, out, err = self._run(args, ignore_errors=True) + if rc != 0: + self.module.fail_json(msg="Failed to build image {image}: {out} {err}".format(image=self.image_name, out=out, err=err)) + + last_id = self._get_id_from_output(out, startswith='-->') + return self.inspect_image(last_id) + + def push_image(self): + args = ['push'] + + if self.validate_certs is not None: + if self.validate_certs: + args.append('--tls-verify') + else: + args.append('--tls-verify=false') + + if self.ca_cert_dir: + args.extend(['--cert-dir', self.ca_cert_dir]) + + if self.username and self.password: + cred_string = '{user}:{password}'.format(user=self.username, password=self.password) + args.extend(['--creds', cred_string]) + + if self.auth_file: + args.extend(['--authfile', self.auth_file]) + + if self.push_args.get('compress'): + args.append('--compress') + + push_format = self.push_args.get('format') + if push_format: + args.extend(['--format', push_format]) + + if self.push_args.get('remove_signatures'): + args.append('--remove-signatures') + + sign_by_key = self.push_args.get('sign_by') + if sign_by_key: + args.extend(['--sign-by', sign_by_key]) + + args.append(self.image_name) + + # Build the destination argument + dest = self.push_args.get('dest') + dest_format_string = '{dest}/{image_name}' + regexp = re.compile(r'/{name}(:{tag})?'.format(name=self.name, tag=self.tag)) + if not dest: + if '/' not in self.name: + self.module.fail_json(msg="'push_args['dest']' is required when pushing images that do not have the remote registry in the image name") + + # If the push destination contains the image name and/or the tag + # remove it and warn since it's not needed. + elif regexp.search(dest): + dest = regexp.sub('', dest) + self.module.warn("Image name and tag are automatically added to push_args['dest']. Destination changed to {dest}".format(dest=dest)) + + if dest and dest.endswith('/'): + dest = dest[:-1] + + transport = self.push_args.get('transport') + if transport: + if not dest: + self.module.fail_json("'push_args['transport'] requires 'push_args['dest'] but it was not provided.") + if transport == 'docker': + dest_format_string = '{transport}://{dest}' + elif transport == 'ostree': + dest_format_string = '{transport}:{name}@{dest}' + else: + dest_format_string = '{transport}:{dest}' + + dest_string = dest_format_string.format(transport=transport, name=self.name, dest=dest, image_name=self.image_name,) + + # Only append the destination argument if the image name is not a URL + if '/' not in self.name: + args.append(dest_string) + + rc, out, err = self._run(args, ignore_errors=True) + if rc != 0: + self.module.fail_json(msg="Failed to push image {image_name}: {err}".format(image_name=self.image_name, err=err)) + last_id = self._get_id_from_output( + out + err, contains=':', split_on=':') + + return self.inspect_image(last_id) + + def remove_image(self, image_name=None): + if image_name is None: + image_name = self.image_name + + args = ['rmi', image_name] + if self.force: + args.append('--force') + rc, out, err = self._run(args, ignore_errors=True) + if rc != 0: + self.module.fail_json(msg='Failed to remove image {image_name}. {err}'.format(image_name=image_name, err=err)) + return out + + +def parse_repository_tag(repo_name): + parts = repo_name.rsplit('@', 1) + if len(parts) == 2: + return tuple(parts) + parts = repo_name.rsplit(':', 1) + if len(parts) == 2 and '/' not in parts[1]: + return tuple(parts) + return repo_name, None + + +def main(): + module = AnsibleModule( + argument_spec=dict( + name=dict(type='str', required=True), + tag=dict(type='str', default='latest'), + pull=dict(type='bool', default=True), + push=dict(type='bool', default=False), + path=dict(type='str'), + force=dict(type='bool', default=False), + state=dict(type='str', default='present', choices=['absent', 'present', 'build']), + validate_certs=dict(type='bool', default=True, aliases=['tlsverify', 'tls_verify']), + executable=dict(type='str', default='podman'), + auth_file=dict(type='path', aliases=['authfile']), + username=dict(type='str'), + password=dict(type='str', no_log=True), + ca_cert_dir=dict(type='path'), + build=dict( + type='dict', + aliases=['build_args', 'buildargs'], + default={}, + options=dict( + annotation=dict(type='dict'), + force_rm=dict(type='bool', default=False), + format=dict( + type='str', + choices=['oci', 'docker'], + default='oci' + ), + cache=dict(type='bool', default=True), + rm=dict(type='bool', default=True), + volume=dict(type='list', elements='str'), + extra_args=dict(type='str'), + ), + ), + push_args=dict( + type='dict', + default={}, + options=dict( + compress=dict(type='bool'), + format=dict(type='str', choices=['oci', 'v2s1', 'v2s2']), + remove_signatures=dict(type='bool'), + sign_by=dict(type='str'), + dest=dict(type='str', aliases=['destination'],), + transport=dict( + type='str', + choices=[ + 'dir', + 'docker-archive', + 'docker-daemon', + 'oci-archive', + 'ostree', + ] + ), + ), + ), + ), + supports_check_mode=True, + required_together=( + ['username', 'password'], + ), + mutually_exclusive=( + ['authfile', 'username'], + ['authfile', 'password'], + ), + ) + + results = dict( + changed=False, + actions=[], + image={}, + ) + + PodmanImageManager(module, results) + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_image_info.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_image_info.py new file mode 100644 index 00000000..4712c56a --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_image_info.py @@ -0,0 +1,234 @@ +#!/usr/bin/python +# Copyright (c) 2019 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +module: podman_image_info +author: + - Sam Doran (@samdoran) +short_description: Gather info about images using podman +notes: + - Podman may required elevated privileges in order to run properly. +description: + - Gather info about images using C(podman) +options: + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the machine running C(podman) + default: 'podman' + type: str + name: + description: + - List of tags or UID to gather info about. If no name is given return info about all images. + type: list + elements: str + +''' + +EXAMPLES = r""" +- name: Gather info for all images + containers.podman.podman_image_info: + +- name: Gather info on a specific image + containers.podman.podman_image_info: + name: nginx + +- name: Gather info on several images + containers.podman.podman_image_info: + name: + - redis + - quay.io/bitnami/wildfly +""" + +RETURN = r""" +images: + description: info from all or specified images + returned: always + type: dict + sample: [ + { + "Annotations": {}, + "Architecture": "amd64", + "Author": "", + "Comment": "from Bitnami with love", + "ContainerConfig": { + "Cmd": [ + "nami", + "start", + "--foreground", + "wildfly" + ], + "Entrypoint": [ + "/app-entrypoint.sh" + ], + "Env": [ + "PATH=/opt/bitnami/java/bin:/opt/bitnami/wildfly/bin:/opt/bitnami/nami/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "IMAGE_OS=debian-9", + "NAMI_VERSION=0.0.9-0", + "GPG_KEY_SERVERS_LIST=ha.pool.sks-keyservers.net \ +hkp://p80.pool.sks-keyservers.net:80 keyserver.ubuntu.com hkp://keyserver.ubuntu.com:80 pgp.mit.edu", + "TINI_VERSION=v0.13.2", + "TINI_GPG_KEY=595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7", + "GOSU_VERSION=1.10", + "GOSU_GPG_KEY=B42F6819007F00F88E364FD4036A9C25BF357DD4", + "BITNAMI_IMAGE_VERSION=14.0.1-debian-9-r12", + "BITNAMI_APP_NAME=wildfly", + "WILDFLY_JAVA_HOME=", + "WILDFLY_JAVA_OPTS=", + "WILDFLY_MANAGEMENT_HTTP_PORT_NUMBER=9990", + "WILDFLY_PASSWORD=bitnami", + "WILDFLY_PUBLIC_CONSOLE=true", + "WILDFLY_SERVER_AJP_PORT_NUMBER=8009", + "WILDFLY_SERVER_HTTP_PORT_NUMBER=8080", + "WILDFLY_SERVER_INTERFACE=0.0.0.0", + "WILDFLY_USERNAME=user", + "WILDFLY_WILDFLY_HOME=/home/wildfly", + "WILDFLY_WILDFLY_OPTS=-Dwildfly.as.deployment.ondemand=false" + ], + "ExposedPorts": { + "8080/tcp": {}, + "9990/tcp": {} + }, + "Labels": { + "maintainer": "Bitnami <containers@bitnami.com>" + } + }, + "Created": "2018-09-25T04:07:45.934395523Z", + "Digest": "sha256:5c7d8e2dd66dcf4a152a4032a1d3c5a33458c67e1c1335edd8d18d738892356b", + "GraphDriver": { + "Data": { + "LowerDir": "/var/lib/containers/storage/overlay/a9dbf5616cc16919a8ac0dfc60aff87a72b5be52994c4649fcc91a089a12931\ +f/diff:/var/lib/containers/storage/overlay/67129bd46022122a7d8b7acb490092af6c7ce244ce4fbd7d9e2d2b7f5979e090/diff:/var/lib/containers/storage/overlay/7c51242c\ +4c5db5c74afda76d7fdbeab6965d8b21804bb3fc597dee09c770b0ca/diff:/var/lib/containers/storage/overlay/f97315dc58a9c002ba0cabccb9933d4b0d2113733d204188c88d72f75569b57b/diff:/var/lib/containers/storage/overlay/1dbde2dd497ddde2b467727125b900958a051a72561e58d29abe3d660dcaa9a7/diff:/var/lib/containers/storage/overlay/4aad9d80f30c3f0608f58173558b7554d84dee4dc4479672926eca29f75e6e33/diff:/var/lib/containers/storage/overlay/6751fc9b6868254870c062d75a511543fc8cfda2ce6262f4945f107449219632/diff:/var/lib/containers/storage/overlay/a27034d79081347421dd24d7e9e776c18271cd9a6e51053cb39af4d3d9c400e8/diff:/var/lib/containers/storage/overlay/537cf0045ed9cd7989f7944e7393019c81b16c1799a2198d8348cd182665397f/diff:/var/lib/containers/storage/overlay/27578615c5ae352af4e8449862d61aaf5c11b105a7d5905af55bd01b0c656d6e/diff:/var/lib/containers/storage/overlay/566542742840fe3034b3596f7cb9e62a6274c95a69f368f9e713746f8712c0b6/diff", + "MergedDir": "/var/lib/containers/storage/overlay/72bb96d6\ +c53ad57a0b1e44cab226a6251598accbead40b23fac89c19ad8c25ca/merged", + "UpperDir": "/var/lib/containers/storage/overlay/72bb96d6c53ad57a0b1e44cab226a6251598accbead40b23fac89c19ad8c25ca/diff", + "WorkDir": "/var/lib/containers/storage/overlay/72bb96d6c53ad57a0b1e44cab226a6251598accbead40b23fac89c19ad8c25ca/work" + }, + "Name": "overlay" + }, + "Id": "bcacbdf7a119c0fa934661ca8af839e625ce6540d9ceb6827cdd389f823d49e0", + "Labels": { + "maintainer": "Bitnami <containers@bitnami.com>" + }, + "ManifestType": "application/vnd.docker.distribution.manifest.v1+prettyjws", + "Os": "linux", + "Parent": "", + "RepoDigests": [ + "quay.io/bitnami/wildfly@sha256:5c7d8e2dd66dcf4a152a4032a1d3c5a33458c67e1c1335edd8d18d738892356b" + ], + "RepoTags": [ + "quay.io/bitnami/wildfly:latest" + ], + "RootFS": { + "Layers": [ + "sha256:75391df2c87e076b0c2f72d20c95c57dc8be7ee684cc07273416cce622b43367", + "sha256:7dd303f041039bfe8f0833092673ac35f93137d10e0fbc4302021ea65ad57731", + "sha256:720d9edf0cd2a9bb56b88b80be9070dbfaad359514c70094c65066963fed485d", + "sha256:6a567ecbf97725501a634fcb486271999aa4591b633b4ae9932a46b40f5aaf47", + "sha256:59e9a6db8f178f3da868614564faabb2820cdfb69be32e63a4405d6f7772f68c", + "sha256:310a82ccb092cd650215ab375da8943d235a263af9a029b8ac26a281446c04db", + "sha256:36cb91cf4513543a8f0953fed785747ea18b675bc2677f3839889cfca0aac79e" + ], + "Type": "layers" + }, + "Size": 569919342, + "User": "", + "Version": "17.06.0-ce", + "VirtualSize": 569919342 + } + ] +""" + +import json + +from ansible.module_utils.basic import AnsibleModule + + +def image_exists(module, executable, name): + command = [executable, 'image', 'exists', name] + rc, out, err = module.run_command(command) + if rc == 1: + return False + elif 'Command "exists" not found' in err: + # The 'exists' test is available in podman >= 0.12.1 + command = [executable, 'image', 'ls', '-q', name] + rc2, out2, err2 = module.run_command(command) + if rc2 != 0: + return False + return True + + +def filter_invalid_names(module, executable, name): + valid_names = [] + names = name + if not isinstance(name, list): + names = [name] + + for name in names: + if image_exists(module, executable, name): + valid_names.append(name) + + return valid_names + + +def get_image_info(module, executable, name): + names = name + if not isinstance(name, list): + names = [name] + + if len(names) > 0: + command = [executable, 'image', 'inspect'] + command.extend(names) + rc, out, err = module.run_command(command) + + if rc != 0: + module.fail_json(msg="Unable to gather info for '{0}': {1}".format(', '.join(names), err)) + return out + + else: + return json.dumps([]) + + +def get_all_image_info(module, executable): + command = [executable, 'image', 'ls', '-q'] + rc, out, err = module.run_command(command) + name = out.strip().split('\n') + out = get_image_info(module, executable, name) + + return out + + +def main(): + module = AnsibleModule( + argument_spec=dict( + executable=dict(type='str', default='podman'), + name=dict(type='list', elements='str') + ), + supports_check_mode=True, + ) + + executable = module.params['executable'] + name = module.params.get('name') + executable = module.get_bin_path(executable, required=True) + + if name: + valid_names = filter_invalid_names(module, executable, name) + results = json.loads(get_image_info(module, executable, valid_names)) + else: + results = json.loads(get_all_image_info(module, executable)) + + results = dict( + changed=False, + images=results + ) + + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_login_info.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_login_info.py new file mode 100644 index 00000000..0ff72e43 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_login_info.py @@ -0,0 +1,117 @@ +#!/usr/bin/python +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r""" +module: podman_login_info +author: + - "Clemens Lange (@clelange)" +version_added: '1.0.0' +short_description: Return the logged-in user if any for a given registry +notes: [] +description: + - Return the logged-in user if any for a given registry. +requirements: + - "Podman installed on host" +options: + registry: + description: + - Registry server. + type: str + required: true + authfile: + description: + - Path of the authentication file. Default is + ``${XDG_RUNTIME_DIR}/containers/auth.json`` + (Not available for remote commands) You can also override the default + path of the authentication file by setting the ``REGISTRY_AUTH_FILE`` + environment variable. ``export REGISTRY_AUTH_FILE=path`` + type: path + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the + machine running C(podman) + default: 'podman' + type: str +""" + +EXAMPLES = r""" +- name: Return the logged-in user for docker hub registry + containers.podman.podman_login_info: + registry: docker.io + +- name: Return the logged-in user for quay.io registry + containers.podman.podman_login_info: + registry: quay.io +""" + +RETURN = r""" +login: + description: Logged in user for a registry + returned: always + type: dict + sample: { + "logged_in": true, + "registry": "docker.io", + "username": "clelange", + } +""" + +import json +from ansible.module_utils.basic import AnsibleModule + + +def get_login_info(module, executable, authfile, registry): + command = [executable, 'login', '--get-login'] + result = dict( + registry=registry, + username='', + logged_in=False, + ) + if authfile: + command.extend(['--authfile', authfile]) + if registry: + command.append(registry) + rc, out, err = module.run_command(command) + if rc != 0: + if 'Error: not logged into' in err: + # The error message is e.g. 'Error: not logged into docker.io' + # Therefore get last word to extract registry name + result["registry"] = err.split()[-1] + err = '' + return result + module.fail_json(msg="Unable to gather info for %s: %s" % (registry, err)) + result["username"] = out.strip() + result["logged_in"] = True + return result + + +def main(): + module = AnsibleModule( + argument_spec=dict( + executable=dict(type='str', default='podman'), + authfile=dict(type='path'), + registry=dict(type='str', required=True) + ), + supports_check_mode=True, + ) + + registry = module.params['registry'] + authfile = module.params['authfile'] + executable = module.get_bin_path(module.params['executable'], required=True) + + inspect_results = get_login_info(module, executable, authfile, registry) + + results = { + "changed": False, + "login": inspect_results, + } + + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_logout.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_logout.py new file mode 100644 index 00000000..35627e8e --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_logout.py @@ -0,0 +1,154 @@ +#!/usr/bin/python +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +module: podman_logout +author: + - "Clemens Lange (@clelange)" +short_description: Log out of a container registry using podman +notes: [] +description: + - Log out of a container registry server using the podman logout command + by deleting the cached credentials stored in the `auth.json` file. + If the registry is not specified, the first registry under + `[registries.search]` from `registries.conf `will be used. The path of + the authentication file can be overridden by the user by setting the + `authfile` flag. The default path used is + `${XDG_RUNTIME_DIR}/containers/auth.json`. + All the cached credentials can be removed by setting the `all` flag. + Warning - podman will use credentials in `${HOME}/.docker/config.json` + to authenticate in case they are not found in the default `authfile`. + However, the logout command will only removed credentials in the + `authfile` specified. +requirements: + - "Podman installed on host" +options: + registry: + description: + - Registry server. If the registry is not specified, + the first registry under `[registries.search]` from + `registries.conf` will be used. + type: str + authfile: + description: + - Path of the authentication file. Default is + ``${XDG_RUNTIME_DIR}/containers/auth.json`` + You can also override the default path of the authentication + file by setting the ``REGISTRY_AUTH_FILE`` environment + variable. ``export REGISTRY_AUTH_FILE=path`` + type: path + all: + description: + - Remove the cached credentials for all registries in the auth file. + type: bool + ignore_docker_credentials: + description: + - Credentials created using other tools such as `docker login` are not + removed unless the corresponding `authfile` is explicitly specified. + Since podman also uses existing credentials in these files by default + (for docker e.g. `${HOME}/.docker/config.json`), module execution will + fail if a docker login exists for the registry specified in any + `authfile` is used by podman. This can be ignored by setting + `ignore_docker_credentials` to `yes` - the credentials will be kept and + `changed` will be false. + This option cannot be used together with `all` since in this case + podman will not check for existing `authfiles` created by other tools. + type: bool + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the + machine running C(podman) + default: 'podman' + type: str +''' + +EXAMPLES = r""" +- name: Log out of default registry + podman_logout: + +- name: Log out of quay.io + podman_logout: + registry: quay.io + +- name: Log out of all registries in auth file + podman_logout: + all: yes + +- name: Log out of all registries in specified auth file + podman_logout: + authfile: $HOME/.docker/config.json + all: yes +""" +# noqa: F402 + +import json # noqa: F402 +from ansible.module_utils.basic import AnsibleModule + + +def logout(module, executable, registry, authfile, all_registries, ignore_docker_credentials): + command = [executable, 'logout'] + changed = False + if authfile: + command.extend(['--authfile', authfile]) + if registry: + command.append(registry) + if all_registries: + command.append("--all") + rc, out, err = module.run_command(command) + if rc != 0: + if 'Error: Not logged into' not in err: + module.fail_json(msg="Unable to gather info for %s: %s" % (registry, err)) + else: + # If the command is successful, we managed to log out + # Mind: This also applied if --all flag is used, while in this case + # there is no check whether one has been logged into any registry + changed = True + if 'Existing credentials were established via' in out: + # The command will return successfully but not log out the user if the + # credentials were initially created using docker. Catch this behaviour: + if not ignore_docker_credentials: + module.fail_json(msg="Unable to log out of %s: %s" % (registry, out)) + else: + changed = False + return changed, out, err + + +def main(): + module = AnsibleModule( + argument_spec=dict( + executable=dict(type='str', default='podman'), + registry=dict(type='str'), + authfile=dict(type='path'), + all=dict(type='bool'), + ignore_docker_credentials=dict(type='bool'), + ), + supports_check_mode=True, + mutually_exclusive=( + ['registry', 'all'], + ['ignore_docker_credentials', 'all'], + ), + ) + + registry = module.params['registry'] + authfile = module.params['authfile'] + all_registries = module.params['all'] + ignore_docker_credentials = module.params['ignore_docker_credentials'] + executable = module.get_bin_path(module.params['executable'], required=True) + + changed, out, err = logout(module, executable, registry, authfile, + all_registries, ignore_docker_credentials) + + results = { + "changed": changed, + "stdout": out, + "stderr": err, + } + + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_network.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_network.py new file mode 100644 index 00000000..590b4690 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_network.py @@ -0,0 +1,614 @@ +#!/usr/bin/python +# Copyright (c) 2020 Red Hat +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r""" +module: podman_network +author: + - "Sagi Shnaidman (@sshnaidm)" +version_added: '1.0.0' +short_description: Manage podman networks +notes: [] +description: + - Manage podman networks with podman network command. +requirements: + - podman +options: + name: + description: + - Name of the network + type: str + required: True + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the + machine running C(podman) + default: 'podman' + type: str + disable_dns: + description: + - disable dns plugin (default "false") + type: bool + driver: + description: + - Driver to manage the network (default "bridge") + type: str + gateway: + description: + - IPv4 or IPv6 gateway for the subnet + type: str + internal: + description: + - Restrict external access from this network (default "false") + type: bool + ip_range: + description: + - Allocate container IP from range + type: str + ipv6: + description: + - Enable IPv6 (Dual Stack) networking. You must pass a IPv6 subnet. + The subnet option must be used with the ipv6 option. + type: bool + subnet: + description: + - Subnet in CIDR format + type: str + macvlan: + description: + - Create a Macvlan connection based on this device + type: str + opt: + description: + - Add network options. Currently 'vlan' and 'mtu' are supported. + type: dict + suboptions: + mtu: + description: + - MTU size for bridge network interface. + type: int + required: false + vlan: + description: + - VLAN tag for bridge which enables vlan_filtering. + type: int + required: false + debug: + description: + - Return additional information which can be helpful for investigations. + type: bool + default: False + state: + description: + - State of network, default 'present' + type: str + default: present + choices: + - present + - absent + recreate: + description: + - Recreate network even if exists. + type: bool + default: false +""" + +EXAMPLES = r""" +- name: Create a podman network + containers.podman.podman_network: + name: podman_network + become: true + +- name: Create internal podman network + containers.podman.podman_network: + name: podman_internal + internal: true + ip_range: 192.168.22.128/25 + subnet: 192.168.22.0/24 + gateway: 192.168.22.1 + become: true +""" + +RETURN = r""" +network: + description: Facts from created or updated networks + returned: always + type: list + sample: [ + { + "cniVersion": "0.4.0", + "name": "podman", + "plugins": [ + { + "bridge": "cni-podman0", + "ipMasq": true, + "ipam": { + "ranges": [ + [ + { + "gateway": "10.88.0.1", + "subnet": "10.88.0.0/16" + } + ] + ], + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "type": "host-local" + }, + "isGateway": true, + "type": "bridge" + }, + { + "capabilities": { + "portMappings": true + }, + "type": "portmap" + }, + { + "backend": "iptables", + "type": "firewall" + } + ] + } + ] +""" + +import json # noqa: F402 +from distutils.version import LooseVersion # noqa: F402 +import os # noqa: F402 + +from ansible.module_utils.basic import AnsibleModule # noqa: F402 +from ansible.module_utils._text import to_bytes, to_native # noqa: F402 + +from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys + + +class PodmanNetworkModuleParams: + """Creates list of arguments for podman CLI command. + + Arguments: + action {str} -- action type from 'create', 'delete' + params {dict} -- dictionary of module parameters + + """ + + def __init__(self, action, params, podman_version, module): + self.params = params + self.action = action + self.podman_version = podman_version + self.module = module + + def construct_command_from_params(self): + """Create a podman command from given module parameters. + + Returns: + list -- list of byte strings for Popen command + """ + if self.action in ['delete']: + return self._simple_action() + if self.action in ['create']: + return self._create_action() + + def _simple_action(self): + if self.action == 'delete': + cmd = ['rm', '-f', self.params['name']] + return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + + def _create_action(self): + cmd = [self.action, self.params['name']] + all_param_methods = [func for func in dir(self) + if callable(getattr(self, func)) + and func.startswith("addparam")] + params_set = (i for i in self.params if self.params[i] is not None) + for param in params_set: + func_name = "_".join(["addparam", param]) + if func_name in all_param_methods: + cmd = getattr(self, func_name)(cmd) + return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + + def check_version(self, param, minv=None, maxv=None): + if minv and LooseVersion(minv) > LooseVersion( + self.podman_version): + self.module.fail_json(msg="Parameter %s is supported from podman " + "version %s only! Current version is %s" % ( + param, minv, self.podman_version)) + if maxv and LooseVersion(maxv) < LooseVersion( + self.podman_version): + self.module.fail_json(msg="Parameter %s is supported till podman " + "version %s only! Current version is %s" % ( + param, minv, self.podman_version)) + + def addparam_gateway(self, c): + return c + ['--gateway', self.params['gateway']] + + def addparam_driver(self, c): + return c + ['--driver', self.params['driver']] + + def addparam_subnet(self, c): + return c + ['--subnet', self.params['subnet']] + + def addparam_ip_range(self, c): + return c + ['--ip-range', self.params['ip_range']] + + def addparam_ipv6(self, c): + return c + ['--ipv6'] + + def addparam_macvlan(self, c): + return c + ['--macvlan', self.params['macvlan']] + + def addparam_internal(self, c): + return c + ['--internal=%s' % self.params['internal']] + + def addparam_opt(self, c): + for opt in self.params['opt'].items(): + c += ['--opt', + b"=".join([to_bytes(k, errors='surrogate_or_strict') + for k in opt])] + return c + + def addparam_disable_dns(self, c): + return c + ['--disable-dns=%s' % self.params['disable_dns']] + + +class PodmanNetworkDefaults: + def __init__(self, module, podman_version): + self.module = module + self.version = podman_version + self.defaults = { + 'driver': 'bridge', + 'disable_dns': False, + 'internal': False, + } + + def default_dict(self): + # make here any changes to self.defaults related to podman version + return self.defaults + + +class PodmanNetworkDiff: + def __init__(self, module, info, podman_version): + self.module = module + self.version = podman_version + self.default_dict = None + self.info = lower_keys(info) + self.params = self.defaultize() + self.diff = {'before': {}, 'after': {}} + self.non_idempotent = {} + + def defaultize(self): + params_with_defaults = {} + self.default_dict = PodmanNetworkDefaults( + self.module, self.version).default_dict() + for p in self.module.params: + if self.module.params[p] is None and p in self.default_dict: + params_with_defaults[p] = self.default_dict[p] + else: + params_with_defaults[p] = self.module.params[p] + return params_with_defaults + + def _diff_update_and_compare(self, param_name, before, after): + if before != after: + self.diff['before'].update({param_name: before}) + self.diff['after'].update({param_name: after}) + return True + return False + + def diffparam_disable_dns(self): + dns_installed = False + for f in [ + '/usr/libexec/cni/dnsname', + '/usr/lib/cni/dnsname', + '/opt/cni/bin/dnsname', + '/opt/bridge/bin/dnsname' + ]: + if os.path.exists(f): + dns_installed = True + before = not bool( + [k for k in self.info['plugins'] if 'domainname' in k]) + after = self.params['disable_dns'] + # If dnsname plugin is not installed, default is disable_dns=True + if not dns_installed and self.module.params['disable_dns'] is None: + after = True + return self._diff_update_and_compare('disable_dns', before, after) + + def diffparam_driver(self): + # Currently only bridge is supported + before = after = 'bridge' + return self._diff_update_and_compare('driver', before, after) + + def diffparam_gateway(self): + try: + before = self.info['plugins'][0]['ipam']['ranges'][0][0]['gateway'] + except (IndexError, KeyError): + before = '' + after = before + if self.params['gateway'] is not None: + after = self.params['gateway'] + return self._diff_update_and_compare('gateway', before, after) + + def diffparam_internal(self): + try: + before = not self.info['plugins'][0]['isgateway'] + except (IndexError, KeyError): + before = False + after = self.params['internal'] + return self._diff_update_and_compare('internal', before, after) + + def diffparam_ip_range(self): + # TODO(sshnaidm): implement IP to CIDR convert and vice versa + before = after = '' + return self._diff_update_and_compare('ip_range', before, after) + + def diffparam_subnet(self): + try: + before = self.info['plugins'][0]['ipam']['ranges'][0][0]['subnet'] + except (IndexError, KeyError): + before = '' + after = before + if self.params['subnet'] is not None: + after = self.params['subnet'] + return self._diff_update_and_compare('subnet', before, after) + + def diffparam_macvlan(self): + before = after = '' + return self._diff_update_and_compare('macvlan', before, after) + + def diffparam_opt(self): + vlan_before = self.info['plugins'][0].get('vlan') + vlan_after = self.params['opt'].get('vlan') if self.params['opt'] else None + if vlan_before or vlan_after: + before, after = {'vlan': vlan_before}, {'vlan': vlan_after} + else: + before, after = {}, {} + mtu_before = self.info['plugins'][0].get('mtu') + mtu_after = self.params['opt'].get('mtu') if self.params['opt'] else None + if mtu_before or mtu_after: + before.update({'mtu': mtu_before}) + after.update({'mtu': mtu_after}) + return self._diff_update_and_compare('opt', before, after) + + def is_different(self): + diff_func_list = [func for func in dir(self) + if callable(getattr(self, func)) and func.startswith( + "diffparam")] + fail_fast = not bool(self.module._diff) + different = False + for func_name in diff_func_list: + dff_func = getattr(self, func_name) + if dff_func(): + if fail_fast: + return True + different = True + # Check non idempotent parameters + for p in self.non_idempotent: + if self.module.params[p] is not None and self.module.params[p] not in [{}, [], '']: + different = True + return different + + +class PodmanNetwork: + """Perform network tasks. + + Manages podman network, inspects it and checks its current state + """ + + def __init__(self, module, name): + """Initialize PodmanNetwork class. + + Arguments: + module {obj} -- ansible module object + name {str} -- name of network + """ + + super(PodmanNetwork, self).__init__() + self.module = module + self.name = name + self.stdout, self.stderr = '', '' + self.info = self.get_info() + self.version = self._get_podman_version() + self.diff = {} + self.actions = [] + + @property + def exists(self): + """Check if network exists.""" + return bool(self.info != {}) + + @property + def different(self): + """Check if network is different.""" + diffcheck = PodmanNetworkDiff( + self.module, + self.info, + self.version) + is_different = diffcheck.is_different() + diffs = diffcheck.diff + if self.module._diff and is_different and diffs['before'] and diffs['after']: + self.diff['before'] = "\n".join( + ["%s - %s" % (k, v) for k, v in sorted( + diffs['before'].items())]) + "\n" + self.diff['after'] = "\n".join( + ["%s - %s" % (k, v) for k, v in sorted( + diffs['after'].items())]) + "\n" + return is_different + + def get_info(self): + """Inspect network and gather info about it.""" + # pylint: disable=unused-variable + rc, out, err = self.module.run_command( + [self.module.params['executable'], b'network', b'inspect', self.name]) + return json.loads(out)[0] if rc == 0 else {} + + def _get_podman_version(self): + # pylint: disable=unused-variable + rc, out, err = self.module.run_command( + [self.module.params['executable'], b'--version']) + if rc != 0 or not out or "version" not in out: + self.module.fail_json(msg="%s run failed!" % + self.module.params['executable']) + return out.split("version")[1].strip() + + def _perform_action(self, action): + """Perform action with network. + + Arguments: + action {str} -- action to perform - create, stop, delete + """ + b_command = PodmanNetworkModuleParams(action, + self.module.params, + self.version, + self.module, + ).construct_command_from_params() + full_cmd = " ".join([self.module.params['executable'], 'network'] + + [to_native(i) for i in b_command]) + self.module.log("PODMAN-NETWORK-DEBUG: %s" % full_cmd) + self.actions.append(full_cmd) + if not self.module.check_mode: + rc, out, err = self.module.run_command( + [self.module.params['executable'], b'network'] + b_command, + expand_user_and_vars=False) + self.stdout = out + self.stderr = err + if rc != 0: + self.module.fail_json( + msg="Can't %s network %s" % (action, self.name), + stdout=out, stderr=err) + + def delete(self): + """Delete the network.""" + self._perform_action('delete') + + def create(self): + """Create the network.""" + self._perform_action('create') + + def recreate(self): + """Recreate the network.""" + self.delete() + self.create() + + +class PodmanNetworkManager: + """Module manager class. + + Defines according to parameters what actions should be applied to network + """ + + def __init__(self, module): + """Initialize PodmanManager class. + + Arguments: + module {obj} -- ansible module object + """ + + super(PodmanNetworkManager, self).__init__() + + self.module = module + self.results = { + 'changed': False, + 'actions': [], + 'network': {}, + } + self.name = self.module.params['name'] + self.executable = \ + self.module.get_bin_path(self.module.params['executable'], + required=True) + self.state = self.module.params['state'] + self.recreate = self.module.params['recreate'] + self.network = PodmanNetwork(self.module, self.name) + + def update_network_result(self, changed=True): + """Inspect the current network, update results with last info, exit. + + Keyword Arguments: + changed {bool} -- whether any action was performed + (default: {True}) + """ + facts = self.network.get_info() if changed else self.network.info + out, err = self.network.stdout, self.network.stderr + self.results.update({'changed': changed, 'network': facts, + 'podman_actions': self.network.actions}, + stdout=out, stderr=err) + if self.network.diff: + self.results.update({'diff': self.network.diff}) + if self.module.params['debug']: + self.results.update({'podman_version': self.network.version}) + self.module.exit_json(**self.results) + + def execute(self): + """Execute the desired action according to map of actions & states.""" + states_map = { + 'present': self.make_present, + 'absent': self.make_absent, + } + process_action = states_map[self.state] + process_action() + self.module.fail_json(msg="Unexpected logic error happened, " + "please contact maintainers ASAP!") + + def make_present(self): + """Run actions if desired state is 'started'.""" + if not self.network.exists: + self.network.create() + self.results['actions'].append('created %s' % self.network.name) + self.update_network_result() + elif self.recreate or self.network.different: + self.network.recreate() + self.results['actions'].append('recreated %s' % + self.network.name) + self.update_network_result() + else: + self.update_network_result(changed=False) + + def make_absent(self): + """Run actions if desired state is 'absent'.""" + if not self.network.exists: + self.results.update({'changed': False}) + elif self.network.exists: + self.network.delete() + self.results['actions'].append('deleted %s' % self.network.name) + self.results.update({'changed': True}) + self.results.update({'network': {}, + 'podman_actions': self.network.actions}) + self.module.exit_json(**self.results) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + state=dict(type='str', default="present", + choices=['present', 'absent']), + name=dict(type='str', required=True), + disable_dns=dict(type='bool', required=False), + driver=dict(type='str', required=False), + gateway=dict(type='str', required=False), + internal=dict(type='bool', required=False), + ip_range=dict(type='str', required=False), + ipv6=dict(type='bool', required=False), + subnet=dict(type='str', required=False), + macvlan=dict(type='str', required=False), + opt=dict(type='dict', required=False, + options=dict( + mtu=dict(type='int', required=False), + vlan=dict(type='int', required=False))), + executable=dict(type='str', required=False, default='podman'), + debug=dict(type='bool', default=False), + recreate=dict(type='bool', default=False), + ), + required_by=dict( # for IP range and GW to set 'subnet' is required + ip_range=('subnet'), + gateway=('subnet'), + )) + + PodmanNetworkManager(module).execute() + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_network_info.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_network_info.py new file mode 100644 index 00000000..a9e18cd4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_network_info.py @@ -0,0 +1,138 @@ +#!/usr/bin/python +# Copyright (c) 2020 Red Hat +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r""" +module: podman_network_info +author: + - "Sagi Shnaidman (@sshnaidm)" +version_added: '1.0.0' +short_description: Gather info about podman networks +notes: [] +description: + - Gather info about podman networks with podman inspect command. +requirements: + - "Podman installed on host" +options: + name: + description: + - Name of the network + type: str + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the + machine running C(podman) + default: 'podman' + type: str +""" + +EXAMPLES = r""" +- name: Gather info about all present networks + containers.podman.podman_network_info: + +- name: Gather info about specific network + containers.podman.podman_network_info: + name: podman +""" + +RETURN = r""" +networks: + description: Facts from all or specified networks + returned: always + type: list + sample: [ + { + "cniVersion": "0.4.0", + "name": "podman", + "plugins": [ + { + "bridge": "cni-podman0", + "ipMasq": true, + "ipam": { + "ranges": [ + [ + { + "gateway": "10.88.0.1", + "subnet": "10.88.0.0/16" + } + ] + ], + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "type": "host-local" + }, + "isGateway": true, + "type": "bridge" + }, + { + "capabilities": { + "portMappings": true + }, + "type": "portmap" + }, + { + "backend": "iptables", + "type": "firewall" + } + ] + } + ] +""" + +import json +from ansible.module_utils.basic import AnsibleModule + + +def get_network_info(module, executable, name): + command = [executable, 'network', 'inspect'] + if not name: + all_names = [executable, 'network', 'ls', '-q'] + rc, out, err = module.run_command(all_names) + if rc != 0: + module.fail_json(msg="Unable to get list of networks: %s" % err) + name = out.split() + if not name: + return [], out, err + command += name + else: + command.append(name) + rc, out, err = module.run_command(command) + if rc != 0 or 'unable to find network configuration' in err: + module.fail_json(msg="Unable to gather info for %s: %s" % (name, err)) + if not out or json.loads(out) is None: + return [], out, err + return json.loads(out), out, err + + +def main(): + module = AnsibleModule( + argument_spec=dict( + executable=dict(type='str', default='podman'), + name=dict(type='str') + ), + supports_check_mode=True, + ) + + name = module.params['name'] + executable = module.get_bin_path(module.params['executable'], required=True) + + inspect_results, out, err = get_network_info(module, executable, name) + + results = { + "changed": False, + "networks": inspect_results, + "stderr": err + } + + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_pod.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_pod.py new file mode 100644 index 00000000..01dbc627 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_pod.py @@ -0,0 +1,232 @@ +#!/usr/bin/python +# Copyright (c) 2020 Red Hat +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# flake8: noqa: E501 +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: podman_pod +short_description: Manage Podman pods +author: + - "Sagi Shnaidman (@sshnaidm)" +version_added: '1.0.0' +description: + - Manage podman pods. +options: + state: + description: + - This variable is set for state + type: str + default: created + choices: + - created + - killed + - restarted + - absent + - started + - stopped + - paused + - unpaused + recreate: + description: + - Use with present and started states to force the re-creation of an + existing pod. + type: bool + default: False + add_host: + description: + - Add a host to the /etc/hosts file shared between all containers in the pod. + type: list + elements: str + required: false + cgroup_parent: + description: + - Path to cgroups under which the cgroup for the pod will be created. If the path + is not absolute, he path is considered to be relative to the cgroups path of the + init process. Cgroups will be created if they do not already exist. + type: str + required: false + dns: + description: + - Set custom DNS servers in the /etc/resolv.conf file that will be shared between + all containers in the pod. A special option, "none" is allowed which disables + creation of /etc/resolv.conf for the pod. + type: list + elements: str + required: false + dns_opt: + description: + - Set custom DNS options in the /etc/resolv.conf file that will be shared between + all containers in the pod. + type: list + elements: str + required: false + dns_search: + description: + - Set custom DNS search domains in the /etc/resolv.conf file that will be shared + between all containers in the pod. + type: list + elements: str + required: false + hostname: + description: + - Set a hostname to the pod + type: str + required: false + infra: + description: + - Create an infra container and associate it with the pod. An infra container is + a lightweight container used to coordinate the shared kernel namespace of a pod. + Default is true. + type: bool + required: false + infra_conmon_pidfile: + description: + - Write the pid of the infra container's conmon process to a file. As conmon runs + in a separate process than Podman, this is necessary when using systemd to manage + Podman containers and pods. + type: str + required: false + infra_command: + description: + - The command that will be run to start the infra container. Default is "/pause". + type: str + required: false + infra_image: + description: + - The image that will be created for the infra container. Default is "k8s.gcr.io/pause:3.1". + type: str + required: false + ip: + description: + - Set a static IP for the pod's shared network. + type: str + required: false + label: + description: + - Add metadata to a pod, pass dictionary of label keys and values. + type: dict + required: false + label_file: + description: + - Read in a line delimited file of labels. + type: str + required: false + mac_address: + description: + - Set a static MAC address for the pod's shared network. + type: str + required: false + name: + description: + - Assign a name to the pod. + type: str + required: true + network: + description: + - Set network mode for the pod. Supported values are bridge (the default), host + (do not create a network namespace, all containers in the pod will use the host's + network), or a comma-separated list of the names of CNI networks the pod should + join. + type: str + required: false + no_hosts: + description: + - Disable creation of /etc/hosts for the pod. + type: bool + required: false + pod_id_file: + description: + - Write the pod ID to the file. + type: str + required: false + publish: + description: + - Publish a port or range of ports from the pod to the host. + type: list + elements: str + required: false + aliases: + - ports + share: + description: + - A comma delimited list of kernel namespaces to share. If none or "" is specified, + no namespaces will be shared. The namespaces to choose from are ipc, net, pid, + user, uts. + type: str + required: false + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the + machine running C(podman) + default: 'podman' + type: str + debug: + description: + - Return additional information which can be helpful for investigations. + type: bool + default: False + +requirements: + - "podman" + +''' + +RETURN = ''' +pod: + description: Pod inspection results for the given pod + built. + returned: always + type: dict + sample: + Config: + cgroupParent: /libpod_parent + created: '2020-06-14T15:16:12.230818767+03:00' + hostname: newpod + id: a5a5c6cdf8c72272fc5c33f787e8d7501e2fa0c1e92b2b602860defdafeeec58 + infraConfig: + infraPortBindings: null + makeInfraContainer: true + labels: {} + lockID: 515 + name: newpod + sharesCgroup: true + sharesIpc: true + sharesNet: true + sharesUts: true + Containers: + - id: dc70a947c7ae15198ec38b3c817587584085dee3919cbeb9969e3ab77ba10fd2 + state: configured + State: + cgroupPath: /libpod_parent/a5a5c6cdf8c72272fc5c33f787e8d7501e2fa0c1e92b2b602860defdafeeec58 + infraContainerID: dc70a947c7ae15198ec38b3c817587584085dee3919cbeb9969e3ab77ba10fd2 + status: Created + +''' + +EXAMPLES = ''' +# What modules does for example +- podman_pod: + name: pod1 + state: started + ports: + - 4444:5555 +''' +from ansible.module_utils.basic import AnsibleModule # noqa: F402 +from ..module_utils.podman.podman_pod_lib import PodmanPodManager # noqa: F402 +from ..module_utils.podman.podman_pod_lib import ARGUMENTS_SPEC_POD # noqa: F402 + + +def main(): + module = AnsibleModule( + argument_spec=ARGUMENTS_SPEC_POD + ) + results = PodmanPodManager(module, module.params).execute() + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_pod_info.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_pod_info.py new file mode 100644 index 00000000..c02e503c --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_pod_info.py @@ -0,0 +1,145 @@ +#!/usr/bin/python +# Copyright (c) 2020 Red Hat +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r""" +module: podman_pod_info +author: + - "Sagi Shnaidman (@sshnaidm)" +version_added: '1.0.0' +short_description: Gather info about podman pods +notes: [] +description: + - Gather info about podman pods with podman inspect command. +requirements: + - "Podman installed on host" +options: + name: + description: + - Name of the pod + type: str + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the + machine running C(podman) + default: 'podman' + type: str +""" + +EXAMPLES = r""" +- name: Gather info about all present pods + containers.podman.podman_pod_info: + +- name: Gather info about specific pods + containers.podman.podman_pod_info: + name: special_pod +""" + +RETURN = r""" +pods: + description: Facts from all or specified pods + returned: always + type: list + sample: [ + { + "Config": { + "id": "d9cb6dbb0....", + "name": "pod1", + "hostname": "pod1host", + "labels": { + }, + "cgroupParent": "/libpod_parent", + "sharesCgroup": true, + "sharesIpc": true, + "sharesNet": true, + "sharesUts": true, + "infraConfig": { + "makeInfraContainer": true, + "infraPortBindings": [ + { + "hostPort": 7777, + "containerPort": 7111, + "protocol": "tcp", + "hostIP": "" + } + ] + }, + "created": "2020-07-13T20:29:12.572282186+03:00", + "lockID": 682 + }, + "State": { + "cgroupPath": "/libpod_parent/d9cb6dbb0....", + "infraContainerID": "ad46737bf....", + "status": "Created" + }, + "Containers": [ + { + "id": "ad46737bf....", + "state": "configured" + } + ] + } + ] +""" + +import json +from ansible.module_utils.basic import AnsibleModule + + +def get_pod_info(module, executable, name): + command = [executable, 'pod', 'inspect'] + pods = [name] + result = [] + errs = [] + rcs = [] + if not name: + all_names = [executable, 'pod', 'ls', '-q'] + rc, out, err = module.run_command(all_names) + if rc != 0: + module.fail_json(msg="Unable to get list of pods: %s" % err) + name = out.split() + if not name: + return [], out, err + pods = name + for pod in pods: + rc, out, err = module.run_command(command + [pod]) + errs.append(err.strip()) + rcs += [rc] + if not out or json.loads(out) is None: + continue + result.append(json.loads(out)) + return result, errs, rcs + + +def main(): + module = AnsibleModule( + argument_spec=dict( + executable=dict(type='str', default='podman'), + name=dict(type='str') + ), + supports_check_mode=True, + ) + + name = module.params['name'] + executable = module.get_bin_path(module.params['executable'], required=True) + + inspect_results, errs, rcs = get_pod_info(module, executable, name) + + if len(rcs) > 1 and 0 not in rcs: + module.fail_json(msg="Failed to inspect pods", stderr="\n".join(errs)) + + results = { + "changed": False, + "pods": inspect_results, + "stderr": "\n".join(errs), + } + + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_volume.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_volume.py new file mode 100644 index 00000000..d74e39d3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_volume.py @@ -0,0 +1,477 @@ +#!/usr/bin/python +# Copyright (c) 2020 Red Hat +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# flake8: noqa: E501 +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: podman_volume +short_description: Manage Podman volumes +author: + - "Sagi Shnaidman (@sshnaidm)" +version_added: '1.1.0' +description: + - Manage Podman volumes +options: + state: + description: + - State of volume, default 'present' + type: str + default: present + choices: + - present + - absent + recreate: + description: + - Recreate volume even if exists. + type: bool + default: false + name: + description: + - Name of volume. + type: str + required: true + label: + description: + - Add metadata to a pod volume (e.g., label com.example.key=value). + type: dict + required: false + driver: + description: + - Specify volume driver name (default local). + type: str + required: false + options: + description: + - Set driver specific options. For example 'device=tpmfs', 'type=tmpfs'. + type: list + elements: str + required: false + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the + machine running C(podman) + default: 'podman' + type: str + debug: + description: + - Return additional information which can be helpful for investigations. + type: bool + default: False + +requirements: + - "podman" + +''' + +RETURN = ''' +volume: + description: Volume inspection results if exists. + returned: always + type: dict + sample: + CreatedAt: '2020-06-05T16:38:55.277628769+03:00' + Driver: local + Labels: + key.com: value + key.org: value2 + Mountpoint: /home/user/.local/share/containers/storage/volumes/test/_data + Name: test + Options: {} + Scope: local + +''' + +EXAMPLES = ''' +# What modules does for example +- podman_volume: + state: present + name: volume1 + label: + key: value + key2: value2 + options: + - "device=/dev/loop1" + - "type=ext4" +''' +# noqa: F402 +import json # noqa: F402 +from distutils.version import LooseVersion # noqa: F402 + +from ansible.module_utils.basic import AnsibleModule # noqa: F402 +from ansible.module_utils._text import to_bytes, to_native # noqa: F402 + +from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys + + +class PodmanVolumeModuleParams: + """Creates list of arguments for podman CLI command. + + Arguments: + action {str} -- action type from 'create', 'delete' + params {dict} -- dictionary of module parameters + + """ + + def __init__(self, action, params, podman_version, module): + self.params = params + self.action = action + self.podman_version = podman_version + self.module = module + + def construct_command_from_params(self): + """Create a podman command from given module parameters. + + Returns: + list -- list of byte strings for Popen command + """ + if self.action in ['delete']: + return self._simple_action() + if self.action in ['create']: + return self._create_action() + + def _simple_action(self): + if self.action == 'delete': + cmd = ['rm', '-f', self.params['name']] + return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + + def _create_action(self): + cmd = [self.action, self.params['name']] + all_param_methods = [func for func in dir(self) + if callable(getattr(self, func)) + and func.startswith("addparam")] + params_set = (i for i in self.params if self.params[i] is not None) + for param in params_set: + func_name = "_".join(["addparam", param]) + if func_name in all_param_methods: + cmd = getattr(self, func_name)(cmd) + return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + + def check_version(self, param, minv=None, maxv=None): + if minv and LooseVersion(minv) > LooseVersion( + self.podman_version): + self.module.fail_json(msg="Parameter %s is supported from podman " + "version %s only! Current version is %s" % ( + param, minv, self.podman_version)) + if maxv and LooseVersion(maxv) < LooseVersion( + self.podman_version): + self.module.fail_json(msg="Parameter %s is supported till podman " + "version %s only! Current version is %s" % ( + param, minv, self.podman_version)) + + def addparam_label(self, c): + for label in self.params['label'].items(): + c += ['--label', b'='.join( + [to_bytes(l, errors='surrogate_or_strict') for l in label])] + return c + + def addparam_driver(self, c): + return c + ['--driver', self.params['driver']] + + def addparam_options(self, c): + for opt in self.params['options']: + c += ['--opt', opt] + return c + + +class PodmanVolumeDefaults: + def __init__(self, module, podman_version): + self.module = module + self.version = podman_version + self.defaults = { + 'driver': 'local', + 'label': {}, + 'options': {} + } + + def default_dict(self): + # make here any changes to self.defaults related to podman version + return self.defaults + + +class PodmanVolumeDiff: + def __init__(self, module, info, podman_version): + self.module = module + self.version = podman_version + self.default_dict = None + self.info = lower_keys(info) + self.params = self.defaultize() + self.diff = {'before': {}, 'after': {}} + self.non_idempotent = {} + + def defaultize(self): + params_with_defaults = {} + self.default_dict = PodmanVolumeDefaults( + self.module, self.version).default_dict() + for p in self.module.params: + if self.module.params[p] is None and p in self.default_dict: + params_with_defaults[p] = self.default_dict[p] + else: + params_with_defaults[p] = self.module.params[p] + return params_with_defaults + + def _diff_update_and_compare(self, param_name, before, after): + if before != after: + self.diff['before'].update({param_name: before}) + self.diff['after'].update({param_name: after}) + return True + return False + + def diffparam_label(self): + before = self.info['labels'] if 'labels' in self.info else {} + after = self.params['label'] + return self._diff_update_and_compare('label', before, after) + + def diffparam_driver(self): + before = self.info['driver'] + after = self.params['driver'] + return self._diff_update_and_compare('driver', before, after) + + def diffparam_options(self): + before = self.info['options'] if 'options' in self.info else {} + before = ["=".join((k, v)) for k, v in before.items()] + after = self.params['options'] + # Gor UID, GID + ids = [] + if self.info['uid']: + before += ['uid=%s' % str(self.info['uid'])] + if self.info['gid']: + before += ['gid=%s' % str(self.info['gid'])] + if self.params['options']: + for opt in self.params['options']: + if 'uid=' in opt or 'gid=' in opt: + ids += opt.split("o=")[1].split(",") + after = [i for i in after if 'gid' not in i and 'uid' not in i] + after += ids + before, after = sorted(list(set(before))), sorted(list(set(after))) + return self._diff_update_and_compare('options', before, after) + + def is_different(self): + diff_func_list = [func for func in dir(self) + if callable(getattr(self, func)) and func.startswith( + "diffparam")] + fail_fast = not bool(self.module._diff) + different = False + for func_name in diff_func_list: + dff_func = getattr(self, func_name) + if dff_func(): + if fail_fast: + return True + else: + different = True + # Check non idempotent parameters + for p in self.non_idempotent: + if self.module.params[p] is not None and self.module.params[p] not in [{}, [], '']: + different = True + return different + + +class PodmanVolume: + """Perform volume tasks. + + Manages podman volume, inspects it and checks its current state + """ + + def __init__(self, module, name): + """Initialize PodmanVolume class. + + Arguments: + module {obj} -- ansible module object + name {str} -- name of volume + """ + + super(PodmanVolume, self).__init__() + self.module = module + self.name = name + self.stdout, self.stderr = '', '' + self.info = self.get_info() + self.version = self._get_podman_version() + self.diff = {} + self.actions = [] + + @property + def exists(self): + """Check if volume exists.""" + return bool(self.info != {}) + + @property + def different(self): + """Check if volume is different.""" + diffcheck = PodmanVolumeDiff( + self.module, + self.info, + self.version) + is_different = diffcheck.is_different() + diffs = diffcheck.diff + if self.module._diff and is_different and diffs['before'] and diffs['after']: + self.diff['before'] = "\n".join( + ["%s - %s" % (k, v) for k, v in sorted( + diffs['before'].items())]) + "\n" + self.diff['after'] = "\n".join( + ["%s - %s" % (k, v) for k, v in sorted( + diffs['after'].items())]) + "\n" + return is_different + + def get_info(self): + """Inspect volume and gather info about it.""" + # pylint: disable=unused-variable + rc, out, err = self.module.run_command( + [self.module.params['executable'], b'volume', b'inspect', self.name]) + return json.loads(out)[0] if rc == 0 else {} + + def _get_podman_version(self): + # pylint: disable=unused-variable + rc, out, err = self.module.run_command( + [self.module.params['executable'], b'--version']) + if rc != 0 or not out or "version" not in out: + self.module.fail_json(msg="%s run failed!" % + self.module.params['executable']) + return out.split("version")[1].strip() + + def _perform_action(self, action): + """Perform action with volume. + + Arguments: + action {str} -- action to perform - create, stop, delete + """ + b_command = PodmanVolumeModuleParams(action, + self.module.params, + self.version, + self.module, + ).construct_command_from_params() + full_cmd = " ".join([self.module.params['executable'], 'volume'] + + [to_native(i) for i in b_command]) + self.module.log("PODMAN-VOLUME-DEBUG: %s" % full_cmd) + self.actions.append(full_cmd) + if not self.module.check_mode: + rc, out, err = self.module.run_command( + [self.module.params['executable'], b'volume'] + b_command, + expand_user_and_vars=False) + self.stdout = out + self.stderr = err + if rc != 0: + self.module.fail_json( + msg="Can't %s volume %s" % (action, self.name), + stdout=out, stderr=err) + + def delete(self): + """Delete the volume.""" + self._perform_action('delete') + + def create(self): + """Create the volume.""" + self._perform_action('create') + + def recreate(self): + """Recreate the volume.""" + self.delete() + self.create() + + +class PodmanVolumeManager: + """Module manager class. + + Defines according to parameters what actions should be applied to volume + """ + + def __init__(self, module): + """Initialize PodmanManager class. + + Arguments: + module {obj} -- ansible module object + """ + + super(PodmanVolumeManager, self).__init__() + + self.module = module + self.results = { + 'changed': False, + 'actions': [], + 'volume': {}, + } + self.name = self.module.params['name'] + self.executable = \ + self.module.get_bin_path(self.module.params['executable'], + required=True) + self.state = self.module.params['state'] + self.recreate = self.module.params['recreate'] + self.volume = PodmanVolume(self.module, self.name) + + def update_volume_result(self, changed=True): + """Inspect the current volume, update results with last info, exit. + + Keyword Arguments: + changed {bool} -- whether any action was performed + (default: {True}) + """ + facts = self.volume.get_info() if changed else self.volume.info + out, err = self.volume.stdout, self.volume.stderr + self.results.update({'changed': changed, 'volume': facts, + 'podman_actions': self.volume.actions}, + stdout=out, stderr=err) + if self.volume.diff: + self.results.update({'diff': self.volume.diff}) + if self.module.params['debug']: + self.results.update({'podman_version': self.volume.version}) + self.module.exit_json(**self.results) + + def execute(self): + """Execute the desired action according to map of actions & states.""" + states_map = { + 'present': self.make_present, + 'absent': self.make_absent, + } + process_action = states_map[self.state] + process_action() + self.module.fail_json(msg="Unexpected logic error happened, " + "please contact maintainers ASAP!") + + def make_present(self): + """Run actions if desired state is 'started'.""" + if not self.volume.exists: + self.volume.create() + self.results['actions'].append('created %s' % self.volume.name) + self.update_volume_result() + elif self.recreate or self.volume.different: + self.volume.recreate() + self.results['actions'].append('recreated %s' % + self.volume.name) + self.update_volume_result() + else: + self.update_volume_result(changed=False) + + def make_absent(self): + """Run actions if desired state is 'absent'.""" + if not self.volume.exists: + self.results.update({'changed': False}) + elif self.volume.exists: + self.volume.delete() + self.results['actions'].append('deleted %s' % self.volume.name) + self.results.update({'changed': True}) + self.results.update({'volume': {}, + 'podman_actions': self.volume.actions}) + self.module.exit_json(**self.results) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + state=dict(type='str', default="present", + choices=['present', 'absent']), + name=dict(type='str', required=True), + label=dict(type='dict', required=False), + driver=dict(type='str', required=False), + options=dict(type='list', elements='str', required=False), + recreate=dict(type='bool', default=False), + executable=dict(type='str', required=False, default='podman'), + debug=dict(type='bool', default=False), + )) + + PodmanVolumeManager(module).execute() + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_volume_info.py b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_volume_info.py new file mode 100644 index 00000000..97b43b3c --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/plugins/modules/podman_volume_info.py @@ -0,0 +1,100 @@ +#!/usr/bin/python +# Copyright (c) 2020 Red Hat +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +module: podman_volume_info +author: + - "Sagi Shnaidman (@sshnaidm)" +short_description: Gather info about podman volumes +notes: [] +description: + - Gather info about podman volumes with podman inspect command. +requirements: + - "Podman installed on host" +options: + name: + description: + - Name of the volume + type: str + executable: + description: + - Path to C(podman) executable if it is not in the C($PATH) on the + machine running C(podman) + default: 'podman' + type: str +''' + +EXAMPLES = r""" +- name: Gather info about all present volumes + podman_volume_info: + +- name: Gather info about specific volume + podman_volume_info: + name: specific_volume +""" + +RETURN = r""" +volumes: + description: Facts from all or specified volumes + returned: always + type: list + sample: [ + { + "name": "testvolume", + "labels": {}, + "mountPoint": "/home/ansible/.local/share/testvolume/_data", + "driver": "local", + "options": {}, + "scope": "local" + } + ] +""" + +import json +from ansible.module_utils.basic import AnsibleModule + + +def get_volume_info(module, executable, name): + command = [executable, 'volume', 'inspect'] + if name: + command.append(name) + else: + command.append("--all") + rc, out, err = module.run_command(command) + if rc != 0 or 'no such volume' in err: + module.fail_json(msg="Unable to gather info for %s: %s" % (name or 'all volumes', err)) + if not out or json.loads(out) is None: + return [], out, err + return json.loads(out), out, err + + +def main(): + module = AnsibleModule( + argument_spec=dict( + executable=dict(type='str', default='podman'), + name=dict(type='str') + ), + supports_check_mode=True, + ) + + name = module.params['name'] + executable = module.get_bin_path(module.params['executable'], required=True) + + inspect_results, out, err = get_volume_info(module, executable, name) + + results = { + "changed": False, + "volumes": inspect_results, + "stderr": err + } + + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/containers/podman/setup.cfg b/collections-debian-merged/ansible_collections/containers/podman/setup.cfg new file mode 100644 index 00000000..def4acc9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/setup.cfg @@ -0,0 +1,38 @@ +[metadata] +name = ansible-podman-collections.containers +summary = Ansible collections for Podman +description-file = + README.md + +author = Sagi Shnaidman (@sshnaidm) +author-email = einarum@gmail.com +home-page = https://github.com/containers/ansible-podman-collections/ +classifier = + License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + Intended Audience :: System Administrators + Intended Audience :: Information Technology + Topic :: System :: Systems Administration + Topic :: Utilities + +[global] +setup-hooks = + pbr.hooks.setup_hook + +[files] +data_files = + share/ansible/collections/ansible_collections/containers/podman/ = README.md + share/ansible/collections/ansible_collections/containers/podman/roles/ = roles/* + share/ansible/collections/ansible_collections/containers/podman/plugins/ = plugins/* + share/ansible/collections/ansible_collections/containers/podman/playbooks/ = playbooks/* + share/ansible/collections/ansible_collections/containers/podman/scripts/ = scripts/* + share/ansible/collections/ansible_collections/containers/podman/docs/ = docs/* + share/ansible/collections/ansible_collections/containers/podman/meta/ = meta/* + +[wheel] +universal = 1 + +[pbr] +skip_authors = True +skip_changelog = True diff --git a/collections-debian-merged/ansible_collections/containers/podman/setup.py b/collections-debian-merged/ansible_collections/containers/podman/setup.py new file mode 100644 index 00000000..d6d2ea1b --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/setup.py @@ -0,0 +1,8 @@ +# Copyright Red Hat, Inc. All Rights Reserved. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +import setuptools + +setuptools.setup( + setup_requires=['pbr'], + pbr=True) diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/.gitignore b/collections-debian-merged/ansible_collections/containers/podman/tests/.gitignore new file mode 100644 index 00000000..ea1472ec --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/__init__.py b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/__init__.py diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection/test_connection.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection/test_connection.yml new file mode 100644 index 00000000..21699422 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection/test_connection.yml @@ -0,0 +1,43 @@ +- hosts: "{{ target_hosts }}" + gather_facts: no + serial: 1 + tasks: + + ### raw with unicode arg and output + + - name: raw with unicode arg and output + raw: echo æ±‰è¯ + register: command + - name: check output of raw with unicode arg and output + assert: + that: + - "'汉è¯' in command.stdout" + - command is changed # as of 2.2, raw should default to changed: true for consistency w/ shell/command/script modules + + ### copy local file with unicode filename and content + + - name: create local file with unicode filename and content + local_action: lineinfile dest={{ local_tmp }}-汉è¯/汉è¯.txt create=true line=æ±‰è¯ + - name: remove remote file with unicode filename and content + action: "{{ action_prefix }}file path={{ remote_tmp }}-汉è¯/汉è¯.txt state=absent" + - name: create remote directory with unicode name + action: "{{ action_prefix }}file path={{ remote_tmp }}-æ±‰è¯ state=directory" + - name: copy local file with unicode filename and content + action: "{{ action_prefix }}copy src={{ local_tmp }}-汉è¯/汉è¯.txt dest={{ remote_tmp }}-汉è¯/汉è¯.txt" + + ### fetch remote file with unicode filename and content + + - name: remove local file with unicode filename and content + local_action: file path={{ local_tmp }}-汉è¯/汉è¯.txt state=absent + - name: fetch remote file with unicode filename and content + fetch: src={{ remote_tmp }}-汉è¯/汉è¯.txt dest={{ local_tmp }}-汉è¯/汉è¯.txt fail_on_missing=true validate_checksum=true flat=true + + ### remove local and remote temp files + + - name: remove local temp file + local_action: file path={{ local_tmp }}-æ±‰è¯ state=absent + - name: remove remote temp file + action: "{{ action_prefix }}file path={{ remote_tmp }}-æ±‰è¯ state=absent" + + ### test wait_for_connection plugin + - wait_for_connection: diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_buildah/runme.sh b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_buildah/runme.sh new file mode 100755 index 00000000..8da6526b --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_buildah/runme.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -o pipefail +set -eux + +function run_ansible { + ${SUDO:-} ${ANSIBLECMD:-ansible-playbook} ../connection/test_connection.yml -i "test_connection.inventory" \ + -e target_hosts="buildah" \ + -e action_prefix= \ + -e local_tmp=/tmp/ansible-local \ + -e remote_tmp=/tmp/ansible-remote \ + "$@" + +} + +# First run as root +run_ansible "$@" + +# Create a normal user +${SUDO:-} ansible all -i "test_connection.inventory" -m "user" -a 'name="testuser"' + +# Second run as normal user +ANSIBLE_VERBOSITY=4 ANSIBLE_REMOTE_USER="testuser" run_ansible "$@" | tee check_log +${SUDO:-} grep -q "Using buildah connection from collection" check_log +${SUDO:-} rm -f check_log diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_buildah/test_connection.inventory b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_buildah/test_connection.inventory new file mode 100644 index 00000000..e6d090ee --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_buildah/test_connection.inventory @@ -0,0 +1,12 @@ +[buildah] +buildah-container ansible_ssh_pipelining=true +[buildah:vars] +# 1. install buildah +# 2. create container: +# $ sudo buildah from --name=buildah-container python:2 +# 3. run test: +# $ ansible-test integration connection_buildah +# 6. remove container +# $ sudo buildah rm buildah-container +ansible_host=buildah-container +ansible_connection=containers.podman.buildah diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_podman/runme.sh b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_podman/runme.sh new file mode 100755 index 00000000..eda49cf5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_podman/runme.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -o pipefail +set -eux + +function run_ansible { + ${SUDO:-} ${ANSIBLECMD:-ansible-playbook} ../connection/test_connection.yml -i "test_connection.inventory" \ + -e target_hosts="podman" \ + -e action_prefix= \ + -e local_tmp=/tmp/ansible-local \ + -e remote_tmp=/tmp/ansible-remote \ + "$@" + +} + +run_ansible "$@" +LC_ALL=C LANG=C run_ansible "$@" +ANSIBLE_VERBOSITY=4 ANSIBLE_REMOTE_TMP="/tmp" ANSIBLE_REMOTE_USER="1000" run_ansible "$@" | tee check_log +${SUDO:-} grep -q "Using podman connection from collection" check_log +${SUDO:-} rm -f check_log +set +o pipefail +ANSIBLE_PODMAN_EXECUTABLE=fakepodman run_ansible "$@" 2>&1 | grep "fakepodman command not found in PATH" +set -o pipefail +ANSIBLE_PODMAN_EXECUTABLE=fakepodman run_ansible "$@" && { + echo "Playbook with fakepodman should fail!" + exit 1 +} +ANSIBLE_VERBOSITY=4 ANSIBLE_PODMAN_EXTRA_ARGS=" --log-level debug " run_ansible "$@" | grep "level=debug msg=" diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_podman/test_connection.inventory b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_podman/test_connection.inventory new file mode 100644 index 00000000..c64c399f --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/connection_podman/test_connection.inventory @@ -0,0 +1,15 @@ +[podman] +podman-container +[podman:vars] +# 1. install podman +# 2. create container: +# podman pull python:3-alpine +# podman run -d --name podman-container python:3-alpine sleep 999999 +# 3. run test: +# ./bin/ansible-test integration connection_podman +# 6. remove container +# podman stop podman-container +# podman rm podman-container +ansible_host=podman-container +ansible_connection=containers.podman.podman +ansible_python_interpreter=/usr/local/bin/python diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container/tasks/main.yml new file mode 100644 index 00000000..edd721c7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container/tasks/main.yml @@ -0,0 +1,462 @@ +- name: Test podman_container + block: + - name: Delete all container leftovers from tests + containers.podman.podman_container: + name: "{{ item }}" + state: absent + loop: + - "alpine:3.7" + - "container" + - "container2" + + - name: Test no image with default action + containers.podman.podman_container: + name: container + ignore_errors: true + register: no_image + + - name: Test no image with state 'started' + containers.podman.podman_container: + name: container + state: started + ignore_errors: true + register: no_image1 + + - name: Test no image with state 'present' + containers.podman.podman_container: + name: container + state: present + ignore_errors: true + register: no_image2 + + - name: Check no image + assert: + that: + - no_image is failed + - no_image1 is failed + - no_image2 is failed + - no_image.msg == "State 'started' required image to be configured!" + - no_image1.msg == "State 'started' required image to be configured!" + - no_image2.msg == "State 'present' required image to be configured!" + fail_msg: No image test failed! + success_msg: No image test passed! + + - name: Ensure image doesn't exist + containers.podman.podman_image: + name: alpine:3.7 + state: absent + + - name: Check pulling image + containers.podman.podman_container: + name: container + image: alpine:3.7 + state: present + command: sleep 1d + register: image + + - name: Check using already pulled image + containers.podman.podman_container: + name: container2 + image: alpine:3.7 + state: present + command: sleep 1d + register: image2 + + - name: Check output is correct + assert: + that: + - image is changed + - image.container is defined + - image.container['State']['Running'] + - "'pulled image alpine:3.7' in image.actions" + - "'started container' in image.actions" + - image2 is changed + - image2.container is defined + - image2.container['State']['Running'] + - "'pulled image alpine:3.7' not in image2.actions" + - "'started container2' in image2.actions" + fail_msg: Pulling image test failed! + success_msg: Pulling image test passed! + + - name: Check failed image pull + containers.podman.podman_container: + name: container + image: ineverneverneverexist + state: present + command: sleep 1d + register: imagefail + ignore_errors: true + + - name: Check output is correct + assert: + that: + - imagefail is failed + - imagefail.msg == "Can't pull image ineverneverneverexist" + + + - name: Force container recreate + containers.podman.podman_container: + name: container + image: alpine + state: present + command: sleep 1d + recreate: true + register: recreated + + - name: Check output is correct + assert: + that: + - recreated is changed + - recreated.container is defined + - recreated.container['State']['Running'] + - "'recreated container' in recreated.actions" + fail_msg: Force recreate test failed! + success_msg: Force recreate test passed! + + - name: Stop container + containers.podman.podman_container: + name: container + state: stopped + register: stopped + + - name: Stop the same container again (idempotency) + containers.podman.podman_container: + name: container + state: stopped + register: stopped_again + + - name: Check output is correct + assert: + that: + - stopped is changed + - stopped.container is defined + - not stopped.container['State']['Running'] + - "'stopped container' in stopped.actions" + - stopped_again is not changed + - stopped_again.container is defined + - not stopped_again.container['State']['Running'] + - stopped_again.actions == [] + fail_msg: Stopping container test failed! + success_msg: Stopping container test passed! + + - name: Delete stopped container + containers.podman.podman_container: + name: container + state: absent + register: deleted + + - name: Delete again container (idempotency) + containers.podman.podman_container: + name: container + state: absent + register: deleted_again + + - name: Check output is correct + assert: + that: + - deleted is changed + - deleted.container is defined + - deleted.container == {} + - "'deleted container' in deleted.actions" + - deleted_again is not changed + - deleted_again.container is defined + - deleted_again.container == {} + - deleted_again.actions == [] + fail_msg: Deleting stopped container test failed! + success_msg: Deleting stopped container test passed! + + - name: Create container, but don't run + containers.podman.podman_container: + name: container + image: alpine:3.7 + state: stopped + command: sleep 1d + register: created + + - name: Check output is correct + assert: + that: + - created is changed + - created.container is defined + - created.container != {} + - not created.container['State']['Running'] + - "'created container' in created.actions" + fail_msg: "Creating stopped container test failed!" + success_msg: "Creating stopped container test passed!" + + - name: Delete created container + containers.podman.podman_container: + name: container + state: absent + + - name: Start container that was deleted + containers.podman.podman_container: + name: container + image: alpine:3.7 + state: started + command: sleep 1d + register: started + + - name: Check output is correct + assert: + that: + - started is changed + - started.container is defined + - started.container['State']['Running'] + - "'pulled image alpine:3.7' not in started.actions" + + - name: Delete started container + containers.podman.podman_container: + name: container + state: absent + register: deleted + + - name: Delete again container (idempotency) + containers.podman.podman_container: + name: container + state: absent + register: deleted_again + + - name: Check output is correct + assert: + that: + - deleted is changed + - deleted.container is defined + - deleted.container == {} + - "'deleted container' in deleted.actions" + - deleted_again is not changed + - deleted_again.container is defined + - deleted_again.container == {} + - deleted_again.actions == [] + fail_msg: Deleting started container test failed! + success_msg: Deleting started container test passed! + + - name: Recreate container with parameters + containers.podman.podman_container: + name: container + image: docker.io/alpine:3.7 + state: started + command: sleep 1d + recreate: true + etc_hosts: + host1: 127.0.0.1 + host2: 127.0.0.1 + annotation: + this: "annotation_value" + dns: + - 1.1.1.1 + - 8.8.4.4 + dns_search: example.com + cap_add: + - SYS_TIME + - NET_ADMIN + publish: + - "9000:80" + - "9001:8000" + workdir: "/bin" + env: + FOO: bar=1 + BAR: foo + TEST: 1 + BOOL: false + label: + somelabel: labelvalue + otheralbe: othervalue + volumes: + - /tmp:/data + register: test + + - name: Check output is correct + assert: + that: + - test is changed + - test.container is defined + - test.container != {} + - test.container['State']['Running'] + # test capabilities + - "'CAP_SYS_TIME' in test.container['BoundingCaps']" + - "'CAP_NET_ADMIN' in test.container['BoundingCaps']" + # test annotations + - test.container['Config']['Annotations']['this'] is defined + - test.container['Config']['Annotations']['this'] == "annotation_value" + # test DNS + - >- + (test.container['HostConfig']['Dns'] is defined and + test.container['HostConfig']['Dns'] == ['1.1.1.1', '8.8.4.4']) or + (test.container['HostConfig']['DNS'] is defined and + test.container['HostConfig']['DNS'] == ['1.1.1.1', '8.8.4.4']) + # test ports + - test.container['NetworkSettings']['Ports']|length == 2 + # test working dir + - test.container['Config']['WorkingDir'] == "/bin" + # test dns search + - >- + (test.container['HostConfig']['DnsSearch'] is defined and + test.container['HostConfig']['DnsSearch'] == ['example.com']) or + (test.container['HostConfig']['DNSSearch'] is defined and + test.container['HostConfig']['DNSSearch'] == ['example.com']) + # test environment variables + - "'FOO=bar=1' in test.container['Config']['Env']" + - "'BAR=foo' in test.container['Config']['Env']" + - "'TEST=1' in test.container['Config']['Env']" + - "'BOOL=False' in test.container['Config']['Env']" + # test labels + - test.container['Config']['Labels'] | length == 2 + - test.container['Config']['Labels']['somelabel'] == "labelvalue" + - test.container['Config']['Labels']['otheralbe'] == "othervalue" + # test mounts + - >- + (test.container['Mounts'][0]['Destination'] is defined and + '/data' in test.container['Mounts'] | map(attribute='Destination') | list) or + (test.container['Mounts'][0]['destination'] is defined and + '/data' in test.container['Mounts'] | map(attribute='destination') | list) + - >- + (test.container['Mounts'][0]['Source'] is defined and + '/tmp' in test.container['Mounts'] | map(attribute='Source') | list) or + (test.container['Mounts'][0]['source'] is defined and + '/tmp' in test.container['Mounts'] | map(attribute='source') | list) + fail_msg: Parameters container test failed! + success_msg: Parameters container test passed! + + - name: Check basic idempotency of running container + containers.podman.podman_container: + name: testidem + image: docker.io/alpine + state: present + command: sleep 20m + + - name: Check basic idempotency of running container - run it again + containers.podman.podman_container: + name: testidem + image: alpine:latest + state: present + command: sleep 20m + register: idem + + - name: Check that nothing was changed + assert: + that: + - not idem.changed + + - name: Check force restart option - run again and force restart + containers.podman.podman_container: + name: testidem + image: alpine:latest + state: present + command: sleep 20m + force_restart: true + register: idem_r + + - name: Check that task was changed + assert: + that: + - idem_r is changed + + - name: Check removing force_restart option + containers.podman.podman_container: + name: testidem + image: alpine:latest + state: present + command: sleep 20m + register: idem_r1 + + - name: Check that task was not changed + assert: + that: + - idem_r1 is not changed + + - name: Run changed container (with tty enabled) + containers.podman.podman_container: + name: testidem + image: alpine + state: present + command: sleep 20m + tty: true + register: idem1 + + - name: Check that container is recreated when changed + assert: + that: + - idem1 is changed + + - name: Run changed container without specifying an option, use defaults + containers.podman.podman_container: + name: testidem + image: alpine + state: present + command: sleep 20m + register: idem2 + + - name: Check that container is recreated when changed to default value + assert: + that: + - idem2 is changed + + - name: Remove container + containers.podman.podman_container: + name: testidem + state: absent + register: remove + + - name: Check podman_actions + assert: + that: + - "'podman rm -f testidem' in remove.podman_actions" + + # - name: Create a pod + # shell: podman pod create --name testidempod + + - name: Check basic idempotency of pod container + containers.podman.podman_container: + name: testidem-pod + image: docker.io/alpine + state: present + command: sleep 20m + pod: "new:testidempod" + + - name: Check basic idempotency of pod container - run it again + containers.podman.podman_container: + name: testidem-pod + image: alpine:latest + state: present + command: sleep 20m + pod: testidempod + register: idem3 + + - name: Check that nothing was changed in pod containers + assert: + that: + - not idem3.changed + + - name: Run changed pod container (with tty enabled) + containers.podman.podman_container: + name: testidem-pod + image: alpine + state: present + command: sleep 20m + tty: true + pod: testidempod + register: idem4 + + - name: Check that container is recreated when changed + assert: + that: + - idem4 is changed + + - name: Remove container + containers.podman.podman_container: + name: testidem-pod + state: absent + + always: + - name: Delete all container leftovers from tests + containers.podman.podman_container: + name: "{{ item }}" + state: absent + loop: + - "alpine:3.7" + - "container" + - "container2" + + - name: Remove pod + shell: podman pod rm -f testidempod diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/files/Dockerfile b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/files/Dockerfile new file mode 100644 index 00000000..8d32e4a3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/files/Dockerfile @@ -0,0 +1,32 @@ +FROM alpine + +LABEL "key"="amazing value" +LABEL nobody=cares + +ARG build_arg + +ENV password root +ENV username root + +WORKDIR /work + +RUN adduser -D user && \ + adduser -D user2 + +COPY start.sh /start + +RUN chmod a+rwx /start + +EXPOSE 80 +EXPOSE 8080/tcp +VOLUME ["/data", "/data2"] +USER user +STOPSIGNAL SIGKILL + +# problem with OS w/o systemd +# HEALTHCHECK --interval=5m --timeout=3s \ +# CMD date + +CMD ["1d"] +ENTRYPOINT ["/start"] + diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/files/start.sh b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/files/start.sh new file mode 100755 index 00000000..1217239e --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/files/start.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +s=${1:-"3h"} +sleep "$s" + diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/build_test_container.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/build_test_container.yml new file mode 100644 index 00000000..a9ecb039 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/build_test_container.yml @@ -0,0 +1,30 @@ +--- +- name: Create directory for user build images + file: + path: /tmp/usr_img + state: directory + +- name: Copy files to container build directory + copy: + src: "{{ item }}" + dest: "/tmp/usr_img/{{ item }}" + mode: 777 + loop: + - Dockerfile + - start.sh + +- name: Build test docker image for regular user + containers.podman.podman_image: + name: "{{ idem_image }}" + path: /tmp/usr_img + build: + format: docker + extra_args: --cgroup-manager=cgroupfs + +- name: Build test docker image for root user + containers.podman.podman_image: + name: "{{ idem_image }}" + path: /tmp/usr_img + build: + format: docker + become: true diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_all.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_all.yml new file mode 100644 index 00000000..15a4d658 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_all.yml @@ -0,0 +1,224 @@ +# Other settings +- name: Remove leftovers from other tests + containers.podman.podman_container: + name: idempotency + state: absent + +- name: Run container + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + +- name: Run container again + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test1 + +- name: Check info when running container again + assert: + that: test1 is not changed + +- name: Run container with environment vars + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + env: + mykey: "amazing value" + ENV1: "one=two=three" + command: 1h + register: test2 + +- name: Check info with environment vars + assert: + that: test2 is changed + +- name: Run container with environment vars again + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + env: + mykey: "amazing value" + ENV1: "one=two=three" + command: 1h + register: test3 + +- name: Check info with environment vars again + assert: + that: test3 is not changed + +- name: Run container with changed environment vars + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + env: + mykey: "amazing value1" + ENV1: "one=two=three" + command: 1h + register: test4 + +- name: Check info with changed environment vars + assert: + that: test4 is changed + +- name: Run container with log level + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + log_level: debug + command: 1h + register: test5 + +- name: Check info with log level + assert: + that: test5 is changed + +- name: Run container with log level again + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + log_level: debug + command: 1h + register: test6 + +- name: Check info with log level again + assert: + that: test6 is not changed + +- name: Run container with changed log level + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + log_level: error + command: 1h + register: test7 + +- name: Check info with changed log level + assert: + that: test7 is changed + +- name: Run container with default log level + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test8 + +- name: Check info with default log level + assert: + that: test8 is not changed + +- name: Run container with log opt tag + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + log_opt: + tag: nonotag + log_driver: journald + command: 1h + register: test9 + +- name: Check info with log opt tag + assert: + that: test9 is changed + +- name: Run container with log opt tag - again + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + log_opt: + tag: nonotag + log_driver: journald + command: 1h + register: test10 + +- name: Check info with log opt tag - again + assert: + that: test10 is not changed + +- name: Run container with default log opt tag + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + log_driver: journald + register: test11 + +- name: Check info with default log opt tag + assert: + that: test11 is changed + +- name: Run container with log opt path + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + log_opt: + path: /tmp/container.log + log_driver: journald + command: 1h + register: test12 + +- name: Check info with log opt path + assert: + that: test12 is changed + +- name: Run container with changed log opt path + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + log_opt: + path: /tmp/container2.log + log_driver: journald + command: 1h + register: test13 + +- name: Check info with changed log opt path + assert: + that: test13 is changed + +- name: Run container with default log opt path + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + log_driver: journald + command: 1h + register: test14 + +# We can't guess the default log path +- name: Check info with default log opt path + assert: + that: test14 is not changed + +- name: Run container with all log-opts + containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + log_driver: journald + log_opt: + path: /tmp/container3.log + max_size: 100mb + tag: sometag + command: 1h + +- name: Remove test container + containers.podman.podman_container: + name: idempotency + state: absent diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_labels.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_labels.yml new file mode 100644 index 00000000..b7627fc7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_labels.yml @@ -0,0 +1,179 @@ +# Labels +- containers.podman.podman_container: + name: idempotency + state: absent + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test1 + +- name: check test1 + assert: + that: test1 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + label: + key: "amazing value" + nobody: "cares" + command: 1h + register: test2 + +- name: check test2 + assert: + that: test2 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test3 + +- name: check test3 + assert: + that: test3 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + label: + haha: kukuku + llala: wiwiwiw + command: 1h + register: test4 + +- name: check test4 + assert: + that: test4 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + label: + haha: kukuku + llala: wiwiwiw + command: 1h + register: test5 + +- name: check test5 + assert: + that: test5 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test6 + +- name: check test6 + assert: + that: test6 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + label: + test: notest + command: 1h + register: test7 + +- name: check test7 + assert: + that: test7 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + label: + key: "amazing value" + nobody: "cares" + command: 1h + register: test8 + +- name: check test8 + assert: + that: test8 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test9 + +- name: check test9 + assert: + that: test9 is not changed + +- containers.podman.podman_container: + name: idempotency1 + state: absent + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test10 + +- name: check test10 + assert: + that: test10 is not changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + label: + razraz: dva + command: sleep 1h + register: test11 + +- name: check test11 + assert: + that: test11 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test12 + +- name: check test12 + assert: + that: test12 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test13 + +- name: check test13 + assert: + that: test13 is not changed diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_networks.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_networks.yml new file mode 100644 index 00000000..5a21aac4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_networks.yml @@ -0,0 +1,40 @@ +- name: Remove container netcontainer + containers.podman.podman_container: + name: netcontainer + state: absent + +- name: Run container with {{ item.first_net }} + containers.podman.podman_container: + name: netcontainer + image: "{{ idem_image }}" + command: 1h + state: present + network: "{{ item.first_net }}" + +- name: Run container again with {{ item.first_net }} + containers.podman.podman_container: + name: netcontainer + image: "{{ idem_image }}" + command: 1h + state: present + network: "{{ item.first_net }}" + register: info + +- name: Check info for 2 runs of {{ item.first_net }} + assert: + that: + - info is not changed + +- name: Run changed container with {{ item.next_net }} + containers.podman.podman_container: + name: netcontainer + image: "{{ idem_image }}" + command: 1h + state: present + network: "{{ item.next_net }}" + register: info1 + +- name: Check info + assert: + that: + - info1 is changed diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_pods.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_pods.yml new file mode 100644 index 00000000..aae118e1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_pods.yml @@ -0,0 +1,75 @@ +- name: Remove testing pod + containers.podman.podman_pod: + name: testpod + state: absent + +- name: Remove test container + containers.podman.podman_container: + name: testpod_container1 + state: absent + +- name: Create pod + containers.podman.podman_pod: + name: testpod + publish: + - "11111:11111" + +- name: Start test container + containers.podman.podman_container: + image: "{{ idem_image }}" + name: testpod_container1 + pod: testpod + state: started + label: + key: value + env: + test: test2 + volumes: + - /tmp:/data + +- name: Start test container again + containers.podman.podman_container: + image: "{{ idem_image }}" + name: testpod_container1 + pod: testpod + state: started + label: + key: value + env: + test: test2 + volumes: + - /tmp:/data + register: info + +- name: Check starting container + assert: + that: info is not changed + +- name: Start test container changed + containers.podman.podman_container: + image: "{{ idem_image }}" + name: testpod_container1 + pod: testpod + state: started + register: info1 + +- name: Check starting container changed + assert: + that: info1 is changed + +- name: Start test container again + containers.podman.podman_container: + image: "{{ idem_image }}" + name: testpod_container1 + pod: testpod + state: started + register: info2 + +- name: Check starting container again + assert: + that: info2 is not changed + +- name: Remove testing pod + containers.podman.podman_pod: + name: testpod + state: absent diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_ports.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_ports.yml new file mode 100644 index 00000000..9e029b80 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_ports.yml @@ -0,0 +1,240 @@ +# Ports +- containers.podman.podman_container: + name: idempotency + state: absent + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test1 + +- name: check test1 + assert: + that: test1 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + ports: + - "4444:4444/tcp" + - "1212:5555" + - "8888:19191/udp" + - "1900:1900/udp" + - "127.0.0.1:7671:7676/udp" + - "127.0.0.1:12122:8876/udp" + - "127.0.0.1:13122:8871/tcp" + - "127.0.0.1:43423:8872" + register: test2 + +- name: check test2 + assert: + that: test2 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + ports: + - "4444:4444/tcp" + - "1212:5555" + - "8888:19191/udp" + - "1900:1900/udp" + - "127.0.0.1:7671:7676/udp" + - "127.0.0.1:12122:8876/udp" + - "127.0.0.1:13122:8871/tcp" + - "127.0.0.1:43423:8872" + register: test3 + +- name: check test3 + assert: + that: test3 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + publish_all: true + command: 1h + register: test4 + +- name: check test4 + assert: + that: test4 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + publish_all: true + command: 1h + register: test5 + +- name: check test5 + assert: + that: test5 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test6 + +- name: check test6 + assert: + that: test6 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + ports: + - 10000:8080 + command: 1h + register: test7 + +- name: check test7 + assert: + that: test7 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + ports: + - 10001:8080 + command: 1h + register: test8 + +- name: check test8 + assert: + that: test8 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + ports: + - 10001:8080/tcp + command: 1h + register: test9 + +- name: check test9 + assert: + that: test9 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + ports: + - 10001:8080/tcp + publish_all: false + command: 1h + register: test9a + +- name: check test9a + assert: + that: test9a is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test9b + +- name: check test9b + assert: + that: test9b is changed + +- containers.podman.podman_container: + name: idempotency1 + state: absent + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test10 + +- name: check test10 + assert: + that: test10 is not changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + publish_all: false + command: sleep 1h + register: test11 + +- name: check test11 + assert: + that: test11 is not changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + publish_all: true + command: sleep 1h + register: test11a + +- name: check test11a + assert: + that: test11a is not changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + ports: + - 10000:8080 + command: sleep 1h + register: test12 + +- name: check test12 + assert: + that: test12 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test13 + +- name: check test13 + assert: + that: test13 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test14 + +- name: check test14 + assert: + that: test14 is not changed diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_stopsignal.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_stopsignal.yml new file mode 100644 index 00000000..c4b2ded5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_stopsignal.yml @@ -0,0 +1,204 @@ +# Stop signal +- containers.podman.podman_container: + name: idempotency + state: absent + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test1 + +- name: check test1 + assert: + that: test1 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + stop_signal: 9 + command: 1h + register: test2 + +- name: check test2 + assert: + that: test2 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test3 + +- name: check test3 + assert: + that: test3 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + stop_signal: 10 + command: 1h + register: test4 + +- name: check test4 + assert: + that: test4 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + stop_signal: 10 + command: 1h + register: test5 + +- name: check test5 + assert: + that: test5 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test6 + +- name: check test6 + assert: + that: test6 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + stop_signal: 15 + command: 1h + register: test7 + +- name: check test7 + assert: + that: test7 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + stop_signal: 9 + command: 1h + register: test8 + +- name: check test8 + assert: + that: test8 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test9 + +- name: check test9 + assert: + that: test9 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + stop_signal: 15 + command: 1h + register: test9a + +- name: check test9a + assert: + that: test9a is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test9b + +- name: check test9b + assert: + that: test9b is changed + +- containers.podman.podman_container: + name: idempotency1 + state: absent + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test10 + +- name: check test10 + assert: + that: test10 is not changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + stop_signal: 15 + command: sleep 1h + register: test11 + +- name: check test11 + assert: + that: test11 is not changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + stop_signal: 10 + command: sleep 1h + register: test12 + +- name: check test12 + assert: + that: test12 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test13 + +- name: check test13 + assert: + that: test13 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test14 + +- name: check test14 + assert: + that: test14 is not changed diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_users.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_users.yml new file mode 100644 index 00000000..ad1f103a --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_users.yml @@ -0,0 +1,169 @@ +# Users +- containers.podman.podman_container: + name: idempotency + state: absent + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test1 + +- name: check test1 + assert: + that: test1 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + user: user + command: 1h + register: test2 + +- name: check test2 + assert: + that: test2 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test3 + +- name: check test3 + assert: + that: test3 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + user: user2 + command: 1h + register: test4 + +- name: check test4 + assert: + that: test4 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + user: user2 + command: 1h + register: test5 + +- name: check test5 + assert: + that: test5 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test6 + +- name: check test6 + assert: + that: test6 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + user: user2 + command: 1h + register: test7 + +- name: check test7 + assert: + that: test7 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + user: user + command: 1h + register: test8 + +- name: check test8 + assert: + that: test8 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test9 + +- name: check test9 + assert: + that: test9 is not changed + +- containers.podman.podman_container: + name: idempotency1 + state: absent + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test10 + +- name: check test10 + assert: + that: test10 is not changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + user: nobody + command: sleep 1h + register: test11 + +- name: check test11 + assert: + that: test11 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test12 + +- name: check test12 + assert: + that: test12 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test13 + +- name: check test13 + assert: + that: test13 is not changed diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_volumes.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_volumes.yml new file mode 100644 index 00000000..475e4494 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_volumes.yml @@ -0,0 +1,234 @@ +# Volumes +- containers.podman.podman_container: + name: idempotency + state: absent + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test1 + +- name: check test1 + assert: + that: test1 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test2 + +- name: check test2 + assert: + that: test2 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test3 + +- name: check test3 + assert: + that: test3 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + volumes: + - /opt:/somedir + command: 1h + register: test4 + +- name: check test4 + assert: + that: test4 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + volumes: + - /opt/://somedir + command: 1h + register: test5 + +- name: check test5 + assert: + that: test5 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test6 + +- name: check test6 + assert: + that: test6 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + volumes: + - /opt:/somedir + - /data + command: 1h + register: test7 + +- name: check test7 + assert: + that: test7 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + volumes: + - /data + command: 1h + register: test8 + +- name: check test8 + assert: + that: test8 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test9 + +- name: check test9 + assert: + that: test9 is not changed + +- name: Create volumes + shell: | + podman volume inspect local_volume1 || podman volume create local_volume1 + podman volume inspect local_volume2 || podman volume create local_volume2 + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + volumes: + - "/opt:/anotherdir" + - "local_volume1:/data" + register: test10 + +- name: check test10 + assert: + that: test10 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + volumes: + - "/opt//:/anotherdir" + - "local_volume1:/data/" + register: test11 + +- name: check test11 + assert: + that: test11 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + volumes: + - "/opt:/anotherdir" + - "local_volume2:/data" + register: test12 + +- name: check test12 + assert: + that: test12 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + volumes: + - "/opt:/anotherdir" + register: test13 + +- name: check test13 + assert: + that: test13 is not changed + +- containers.podman.podman_container: + name: idempotency1 + state: absent + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test14 + +- name: check test14 + assert: + that: test14 is not changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + volumes: + - /opt:/data + command: sleep 1h + register: test15 + +- name: check test15 + assert: + that: test15 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test16 + +- name: check test16 + assert: + that: test16 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test17 + +- name: check test17 + assert: + that: test17 is not changed diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_workdir.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_workdir.yml new file mode 100644 index 00000000..b6d0fa35 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/idem_workdir.yml @@ -0,0 +1,204 @@ +# Workdir +- containers.podman.podman_container: + name: idempotency + state: absent + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test1 + +- name: check test1 + assert: + that: test1 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + workdir: /work + command: 1h + register: test2 + +- name: check test2 + assert: + that: test2 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test3 + +- name: check test3 + assert: + that: test3 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + workdir: /app + command: 1h + register: test4 + +- name: check test4 + assert: + that: test4 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + workdir: /app + command: 1h + register: test5 + +- name: check test5 + assert: + that: test5 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test6 + +- name: check test6 + assert: + that: test6 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + workdir: /app + command: 1h + register: test7 + +- name: check test7 + assert: + that: test7 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + workdir: /work + command: 1h + register: test8 + +- name: check test8 + assert: + that: test8 is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test9 + +- name: check test9 + assert: + that: test9 is not changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + workdir: / + command: 1h + register: test9a + +- name: check test9a + assert: + that: test9a is changed + +- containers.podman.podman_container: + image: "{{ idem_image }}" + name: idempotency + state: present + command: 1h + register: test9b + +- name: check test9b + assert: + that: test9b is changed + +- containers.podman.podman_container: + name: idempotency1 + state: absent + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test10 + +- name: check test10 + assert: + that: test10 is not changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + workdir: / + command: sleep 1h + register: test11 + +- name: check test11 + assert: + that: test11 is not changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + workdir: /app + command: sleep 1h + register: test12 + +- name: check test12 + assert: + that: test12 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test13 + +- name: check test13 + assert: + that: test13 is changed + +- containers.podman.podman_container: + image: alpine + name: idempotency1 + state: present + command: sleep 1h + register: test14 + +- name: check test14 + assert: + that: test14 is not changed diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/main.yml new file mode 100644 index 00000000..b207f73b --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/main.yml @@ -0,0 +1,46 @@ +--- +- name: Prepare a container + include_tasks: build_test_container.yml + +- name: Test idempotency of users + include_tasks: idem_users.yml + +- name: Test idempotency of workdir + include_tasks: idem_workdir.yml + +- name: Test idempotency of labels + include_tasks: idem_labels.yml + +- name: Test idempotency of stop signal + include_tasks: idem_stopsignal.yml + +- name: Test idempotency of ports + include_tasks: idem_ports.yml + +- name: Test idempotency of volumes + include_tasks: idem_volumes.yml + +- name: Test idempotency of containers in pods + include_tasks: idem_pods.yml + +- name: Test idempotency of other settings + include_tasks: idem_all.yml + +- name: Test idempotency for root containers + include_tasks: root-podman.yml + vars: + ansible_python_interpreter: "/usr/bin/python" + args: + apply: + become: true + +- name: Test idempotency for root network containers + include_tasks: root-podman-network.yml + vars: + ansible_python_interpreter: "/usr/bin/python" + args: + apply: + become: true + +- name: Test idempotency for root network containers + include_tasks: rootless-podman-network.yml diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/root-podman-network.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/root-podman-network.yml new file mode 100644 index 00000000..bdcbd157 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/root-podman-network.yml @@ -0,0 +1,67 @@ +- name: Test podman rootful container with networks + block: + + - name: Remove container + containers.podman.podman_container: + name: netcontainer + state: absent + + - name: Create network testnet + command: podman network create testnet --subnet 10.92.92.0/24 + + - name: Create network anothernet + command: podman network create anothernet --subnet 10.72.72.0/24 + + - name: List current networks + command: podman network ls + + - name: Set test data + set_fact: + testdata: + - first_net: host + next_net: bridge + - first_net: bridge + next_net: host + - first_net: none + next_net: host + - first_net: host + next_net: none + - first_net: anothernet + next_net: testnet + - first_net: testnet + next_net: + - testnet + - anothernet + - first_net: + - testnet + - anothernet + next_net: anothernet + - first_net: + - testnet + - anothernet + next_net: bridge + - first_net: + - testnet + - anothernet + next_net: host + - first_net: host + next_net: anothernet + - first_net: bridge + next_net: + - anothernet + - testnet + + - include_tasks: idem_networks.yml + loop: "{{ testdata }}" + + always: + + - name: Delete all pods leftovers from tests + containers.podman.podman_container: + name: netcontainer + state: absent + + - name: Delete all network leftovers from tests + shell: | + podman network rm -f anothernet + podman network rm -f testnet diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/root-podman.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/root-podman.yml new file mode 100644 index 00000000..eccc4e89 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/root-podman.yml @@ -0,0 +1,153 @@ +--- +# Ulimits testing +- name: Make sure container doesn't exist + containers.podman.podman_container: + name: root-idempotency + state: absent + +- name: Run container as is + containers.podman.podman_container: + image: "{{ idem_image }}" + name: root-idempotency + state: present + command: 1h + +- name: Run container as is again + containers.podman.podman_container: + image: "{{ idem_image }}" + name: root-idempotency + state: present + command: 1h + register: info_a + +- name: Check that it is not recreated + assert: + that: + - info_a is not changed + +- name: Run containers with ulimits + containers.podman.podman_container: + image: "{{ idem_image }}" + name: root-idempotency + state: present + command: 1h + ulimit: + - 'nofile=55535:55535' + - 'memlock=-1:-1' + register: info + +- name: Check that it is recreated + assert: + that: + - info is changed + +- name: Run containers with ulimits - idempotency + containers.podman.podman_container: + image: "{{ idem_image }}" + name: root-idempotency + state: present + command: 1h + ulimit: + - 'nofile=55535:55535' + - 'memlock=-1:-1' + register: info1 + +- name: Check that it is recreated + assert: + that: + - info1 is not changed + +- name: Run containers with changed ulimits + containers.podman.podman_container: + image: "{{ idem_image }}" + name: root-idempotency + state: present + command: 1h + ulimit: + - 'nofile=55535:65535' + - 'memlock=-1:-1' + register: info2 + +- name: Check that it is recreated + assert: + that: + - info2 is changed + +- name: Run containers with changed ulimits - idempotency + containers.podman.podman_container: + image: "{{ idem_image }}" + name: root-idempotency + state: present + command: 1h + ulimit: + - 'nofile=55535:65535' + - 'memlock=-1:-1' + register: info3 + +- name: Check that it is recreated + assert: + that: + - info3 is not changed + +- name: Run default container + containers.podman.podman_container: + image: "{{ idem_image }}" + name: root-idempotency + state: present + command: 1h + +- name: Run containers with MAC address + containers.podman.podman_container: + image: "{{ idem_image }}" + name: root-idempotency + state: present + command: 1h + mac_address: 44:55:66:77:88:99 + register: info4 + +- name: Check that it is not recreated + assert: + that: + - info4 is changed + - info4.container['NetworkSettings']['MacAddress'] == '44:55:66:77:88:99' + +- name: Run containers with MAC address again - idempotency + containers.podman.podman_container: + image: "{{ idem_image }}" + name: root-idempotency + state: present + command: 1h + mac_address: 44:55:66:77:88:99 + register: info5 + +- name: Check that it is not recreated + assert: + that: + - info5 is not changed + +- name: Run containers with MAC address changed + containers.podman.podman_container: + image: "{{ idem_image }}" + name: root-idempotency + state: present + command: 1h + mac_address: 44:55:66:77:88:33 + register: info6 + +- name: Check that it is recreated + assert: + that: + - info6 is changed + +- name: Run again default container + containers.podman.podman_container: + image: "{{ idem_image }}" + name: root-idempotency + state: present + command: 1h + register: info7 + +- name: Check that it is recreated + assert: + that: + - info7 is not changed diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/rootless-podman-network.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/rootless-podman-network.yml new file mode 100644 index 00000000..2178e522 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_idempotency/tasks/rootless-podman-network.yml @@ -0,0 +1,103 @@ +- name: Test podman rootful container with networks + block: + + - name: Remove container rootlessnet + containers.podman.podman_container: + name: rootlessnet + state: absent + + - name: Run container with no specified networks + containers.podman.podman_container: + name: rootlessnet + image: "{{ idem_image }}" + command: 1h + state: present + + - name: Run container again with no specified networks + containers.podman.podman_container: + name: rootlessnet + image: "{{ idem_image }}" + command: 1h + state: present + register: info + + - name: Check info for no specified networks + assert: + that: + - info is not changed + + - name: Run container with network mode host + containers.podman.podman_container: + name: rootlessnet + image: "{{ idem_image }}" + command: 1h + state: present + network: host + register: info1 + + - name: Check info with network mode host + assert: + that: + - info1 is changed + + - name: Run container with network mode host again + containers.podman.podman_container: + name: rootlessnet + image: "{{ idem_image }}" + command: 1h + state: present + network: host + register: info2 + + - name: Check info with network mode host again + assert: + that: + - info2 is not changed + + - name: Run container without network at all + containers.podman.podman_container: + name: rootlessnet + image: "{{ idem_image }}" + command: 1h + state: present + network: none + register: info3 + + - name: Check info without network at all + assert: + that: + - info3 is changed + + - name: Run container without network at all again + containers.podman.podman_container: + name: rootlessnet + image: "{{ idem_image }}" + command: 1h + state: present + network: none + register: info4 + + - name: Check info without network at all again + assert: + that: + - info4 is not changed + + - name: Run container with default network mode + containers.podman.podman_container: + name: rootlessnet + image: "{{ idem_image }}" + command: 1h + state: present + register: info5 + + - name: Check info with default network mode + assert: + that: + - info5 is changed + + always: + + - name: Delete all pods leftovers from tests + containers.podman.podman_container: + name: rootlessnet + state: absent diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_info/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_info/tasks/main.yml new file mode 100644 index 00000000..7ad1e44c --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_container_info/tasks/main.yml @@ -0,0 +1,88 @@ +- name: Test podman_container_info + block: + + - name: Generate random value for container name + set_fact: + container_name: "{{ 'ansible-test-podman-%0x' % ((2**32) | random) }}" + + - name: Make sure container doesn't exist + command: podman container rm -f {{ container_name }} + ignore_errors: true + + - name: Get missing container info + containers.podman.podman_container_info: + name: "{{ container_name }}" + register: nonexist + + - name: Check results of missing container info + assert: + that: + - "'containers' in nonexist" + - nonexist is succeeded + - nonexist.containers == [] + + - name: Get missing multiple container info + containers.podman.podman_container_info: + name: + - "{{ container_name }}" + - neverexist + - whatever + register: nonexist2 + ignore_errors: true + + - name: Check results of missing multiple container info + assert: + that: + - "'containers' in nonexist2" + - nonexist2 is succeeded + - nonexist2.containers == [] + + - name: Make sure container exists + command: podman container run -d --name {{ container_name }} alpine sleep 15m + + - name: Get existing container info + containers.podman.podman_container_info: + name: "{{ container_name }}" + register: existing_container + + - name: Get mixed existing and non-existing container info + containers.podman.podman_container_info: + name: + - "{{ container_name }}" + - whatever + register: mixed_existing_container + + - name: Get all containers info + containers.podman.podman_container_info: + register: all_containers + + - name: Dump podman container inspect result + debug: var=existing_container + + - name: Comparison with 'podman container inspect' + command: podman container inspect "{{ container_name }}" + register: podman_inspect + + - name: Convert podman inspect output to JSON + set_fact: + podman_inspect_result: "{{ podman_inspect.stdout | from_json }}" + + - name: Cleanup + command: podman container rm -f {{ container_name }} + + - name: Make checks + assert: + that: + - "'containers' in existing_container" + - existing_container.containers + - "existing_container.containers == podman_inspect_result" + - all_containers.containers == existing_container.containers + - "'containers' in mixed_existing_container" + - mixed_existing_container.containers + - existing_container.containers == mixed_existing_container.containers + + always: + + - name: Cleanup + command: podman container rm -f {{ container_name }} + ignore_errors: true diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_containers/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_containers/tasks/main.yml new file mode 100644 index 00000000..2703518c --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_containers/tasks/main.yml @@ -0,0 +1,642 @@ +- name: Test multiple podman_containers + block: + - name: Delete all containers leftovers from tests + containers.podman.podman_containers: + containers: + - name: "alpine:3.7" + state: absent + - name: "container" + state: absent + - name: "container1" + state: absent + - name: "container2" + state: absent + - name: "container3" + state: absent + - name: "container4" + state: absent + - name: "testidem" + state: absent + - name: "testidem1" + state: absent + - name: "testidem2" + state: absent + - name: "testidem3" + state: absent + - name: "testidem-pod" + state: absent + - name: "testidem-pod2" + state: absent + + - name: Test no image with default action + containers.podman.podman_containers: + containers: + - name: "container" + - name: "container2" + - name: "container3" + image: alpine + ignore_errors: true + register: no_image + + - name: Test no image with state 'started' + containers.podman.podman_containers: + containers: + - name: "container" + state: started + - name: "container2" + state: started + ignore_errors: true + register: no_image1 + + - name: Test no image with state 'present' + containers.podman.podman_containers: + containers: + - name: "container" + state: present + - name: "container2" + state: present + - name: "container3" + state: present + image: alpine + ignore_errors: true + register: no_image2 + + - name: Check no image + assert: + that: + - no_image is failed + - no_image1 is failed + - no_image2 is failed + - no_image.msg is search("State 'started' required image to be configured!") + - no_image1.msg is search ("State 'started' required image to be configured!") + - no_image2.msg is search("State 'present' required image to be configured!") + fail_msg: No-image test failed! + success_msg: No-image test passed! + + - name: Ensure image doesn't exist + containers.podman.podman_image: + name: alpine:3.7 + state: absent + + - name: Check pulling image + containers.podman.podman_containers: + containers: + - name: container + image: alpine:3.7 + state: present + command: sleep 1d + - name: container1 + image: alpine:3.7 + state: present + command: sleep 1d + register: image + + - name: Check using already pulled image + containers.podman.podman_containers: + containers: + - name: container1 + image: alpine:3.7 + state: present + command: sleep 1d + - name: container3 + image: alpine:3.7 + state: present + command: sleep 1d + register: image2 + + - name: Check output is correct + assert: + that: + - image is changed + - image.containers[0] is defined + - image.containers[0]['State']['Running'] + - image.containers[1] is defined + - image.containers[1]['State']['Running'] + - "'pulled image alpine:3.7' in image.actions" + - "'started container' in image.actions" + - "'started container1' in image.actions" + - image2 is changed + - image2.containers is defined + - image2.containers[0]['State']['Running'] + - image2.containers[1]['State']['Running'] + - "'pulled image alpine:3.7' not in image2.actions" + - "'started container3' in image2.actions" + fail_msg: Pulling image test failed! + success_msg: Pulling image test passed! + + - name: Check failed image pull + containers.podman.podman_containers: + containers: + - name: container1 + image: alpine:3.7 + state: present + command: sleep 1d + - name: container + image: ineverneverneverexist + state: present + command: sleep 1d + register: imagefail + ignore_errors: true + + - name: Check output is correct + assert: + that: + - imagefail is failed + - imagefail.msg == "Can't pull image ineverneverneverexist" + + - name: Force containers recreate + containers.podman.podman_containers: + containers: + - name: container1 + image: alpine:3.7 + state: present + command: sleep 1d + - name: container + image: alpine + state: present + command: sleep 1d + recreate: true + register: recreated + + - name: Check output is correct + assert: + that: + - recreated is changed + - recreated.containers is defined + - recreated.containers[1]['State']['Running'] + - "'recreated container' in recreated.actions" + fail_msg: Force recreate test failed! + success_msg: Force recreate test passed! + + - name: Stop containers + containers.podman.podman_containers: + containers: + - name: container + state: stopped + - name: container1 + state: stopped + register: stopped + + - name: Stop the same containers again (idempotency) + containers.podman.podman_containers: + containers: + - name: container + state: stopped + - name: container1 + state: stopped + register: stopped_again + + - name: Check output is correct + assert: + that: + - stopped is changed + - stopped.containers is defined + - not stopped.containers[0]['State']['Running'] + - not stopped.containers[1]['State']['Running'] + - "'stopped container' in stopped.actions" + - stopped_again is not changed + - stopped_again.containers is defined + - not stopped_again.containers[0]['State']['Running'] + - not stopped_again.containers[1]['State']['Running'] + - stopped_again.actions == [] + fail_msg: Stopping container test failed! + success_msg: Stopping container test passed! + + - name: Delete stopped containers + containers.podman.podman_containers: + containers: + - name: container + state: absent + - name: container1 + state: absent + register: deleted + + - name: Delete again containers (idempotency) + containers.podman.podman_containers: + containers: + - name: container + state: absent + - name: container1 + state: absent + register: deleted_again + + - name: Check output is correct + assert: + that: + - deleted is changed + - deleted.containers is defined + - deleted.containers == [] + - "'deleted container' in deleted.actions" + - "'deleted container1' in deleted.actions" + - deleted_again is not changed + - deleted_again.containers is defined + - deleted_again.containers == [] + - deleted_again.actions == [] + fail_msg: Deleting stopped container test failed! + success_msg: Deleting stopped container test passed! + + - name: Create containers, but don't run + containers.podman.podman_containers: + containers: + - name: container + image: alpine:3.7 + state: stopped + command: sleep 1d + - name: container1 + image: alpine:3.7 + state: stopped + command: sleep 1d + register: created + + - name: Create containers, but don't run again + containers.podman.podman_containers: + containers: + - name: container + image: alpine:3.7 + state: stopped + command: sleep 1d + - name: container1 + image: alpine:3.7 + state: stopped + command: sleep 1d + register: created_again + + - name: Check output is correct + assert: + that: + - created is changed + - created.containers is defined + - created.containers != [] + - not created.containers[0]['State']['Running'] + - not created.containers[1]['State']['Running'] + - "'created container' in created.actions" + fail_msg: "Creating stopped container test failed!" + success_msg: "Creating stopped container test passed!" + + - name: Delete created containers + containers.podman.podman_containers: + containers: + - name: container + state: absent + - name: container1 + state: absent + + - name: Start containers that were deleted + containers.podman.podman_containers: + containers: + - name: container + image: alpine:3.7 + state: started + command: sleep 1d + - name: container1 + image: alpine:3.7 + state: started + command: sleep 1d + register: started + + - name: Check output is correct + assert: + that: + - started.containers is defined + - started.containers[0]['State']['Running'] + - started.containers[1]['State']['Running'] + - "'started container' in started.actions" + - "'pulled image alpine:3.7' not in started.actions" + + - name: Delete started container + containers.podman.podman_containers: + containers: + - name: container + state: absent + - name: container1 + state: absent + register: deleted + + - name: Delete again container (idempotency) + containers.podman.podman_containers: + containers: + - name: container + state: absent + - name: container1 + state: absent + register: deleted_again + + - name: Check output is correct + assert: + that: + - deleted is changed + - deleted.containers is defined + - deleted.containers == [] + - "'deleted container' in deleted.actions" + - "'deleted container1' in deleted.actions" + - deleted_again is not changed + - deleted_again.containers is defined + - deleted_again.containers == [] + - deleted_again.actions == [] + fail_msg: Deleting started container test failed! + success_msg: Deleting started container test passed! + + - name: Recreate container with parameters + containers.podman.podman_containers: + containers: + - name: container + image: docker.io/alpine:3.7 + state: started + command: sleep 1d + recreate: true + etc_hosts: + host1: 127.0.0.1 + host2: 127.0.0.1 + annotation: + this: "annotation_value" + dns_servers: + - 1.1.1.1 + - 8.8.4.4 + dns_search_domains: example.com + capabilities: + - SYS_TIME + - NET_ADMIN + ports: + - "9000:80" + - "9001:8000" + workdir: "/bin" + env: + FOO: bar=1 + BAR: foo + TEST: 1 + BOOL: false + label: + somelabel: labelvalue + otheralbe: othervalue + volumes: + - /tmp:/data + - name: container1 + image: docker.io/alpine:3.7 + state: started + command: sleep 1d + recreate: true + etc_hosts: + host1: 127.0.0.1 + host2: 127.0.0.1 + annotation: + this: "annotation_value" + dns_servers: + - 1.1.1.1 + - 8.8.4.4 + dns_search_domains: example.com + capabilities: + - SYS_TIME + - NET_ADMIN + ports: + - "9002:80" + - "9003:8000" + workdir: "/bin" + env: + FOO: bar=1 + BAR: foo + TEST: 1 + BOOL: false + label: + somelabel: labelvalue + otheralbe: othervalue + volumes: + - /tmp:/data + register: test + + - name: Check output is correct + assert: + that: + - test is changed + - test.containers is defined + - test.containers != [] + - test.containers[0]['State']['Running'] + # test capabilities + - "'CAP_SYS_TIME' in test.containers[0]['BoundingCaps']" + - "'CAP_NET_ADMIN' in test.containers[0]['BoundingCaps']" + # test annotations + - test.containers[0]['Config']['Annotations']['this'] is defined + - test.containers[0]['Config']['Annotations']['this'] == "annotation_value" + # test DNS + - >- + (test.containers[0]['HostConfig']['Dns'] is defined and + test.containers[0]['HostConfig']['Dns'] == ['1.1.1.1', '8.8.4.4']) or + (test.containers[0]['HostConfig']['DNS'] is defined and + test.containers[0]['HostConfig']['DNS'] == ['1.1.1.1', '8.8.4.4']) + # test ports + - test.containers[0]['NetworkSettings']['Ports']|length == 2 + # test working dir + - test.containers[0]['Config']['WorkingDir'] == "/bin" + # test dns search + - >- + (test.containers[0]['HostConfig']['DnsSearch'] is defined and + test.containers[0]['HostConfig']['DnsSearch'] == ['example.com']) or + (test.containers[0]['HostConfig']['DNSSearch'] is defined and + test.containers[0]['HostConfig']['DNSSearch'] == ['example.com']) + # test environment variables + - "'FOO=bar=1' in test.containers[0]['Config']['Env']" + - "'BAR=foo' in test.containers[0]['Config']['Env']" + - "'TEST=1' in test.containers[0]['Config']['Env']" + - "'BOOL=False' in test.containers[0]['Config']['Env']" + # test labels + - test.containers[0]['Config']['Labels'] | length == 2 + - test.containers[0]['Config']['Labels']['somelabel'] == "labelvalue" + - test.containers[0]['Config']['Labels']['otheralbe'] == "othervalue" + # test mounts + - >- + (test.containers[0]['Mounts'][0]['Destination'] is defined and + '/data' in test.containers[0]['Mounts'] | map(attribute='Destination') | list) or + (test.containers[0]['Mounts'][0]['destination'] is defined and + '/data' in test.containers[0]['Mounts'] | map(attribute='destination') | list) + - >- + (test.containers[0]['Mounts'][0]['Source'] is defined and + '/tmp' in test.containers[0]['Mounts'] | map(attribute='Source') | list) or + (test.containers[0]['Mounts'][0]['source'] is defined and + '/tmp' in test.containers[0]['Mounts'] | map(attribute='source') | list) + fail_msg: Parameters container test failed! + success_msg: Parameters container test passed! + + - name: Check basic idempotency of running container + containers.podman.podman_containers: + containers: + - name: testidem + image: docker.io/alpine + state: present + command: sleep 20m + - name: testidem2 + image: docker.io/alpine + state: present + command: sleep 21m + - name: testidem3 + image: docker.io/alpine + state: present + command: sleep 22m + + - name: Check basic idempotency of running container - run it again + containers.podman.podman_containers: + containers: + - name: testidem + image: docker.io/alpine + state: present + command: sleep 20m + - name: testidem2 + image: docker.io/alpine + state: present + command: sleep 21m + - name: testidem3 + image: docker.io/alpine + state: present + command: sleep 22m + register: idem + + - name: Check that nothing was changed + assert: + that: + - not idem.changed + + - name: Run changed container (with tty enabled) + containers.podman.podman_containers: + containers: + - name: testidem + image: docker.io/alpine + state: present + command: sleep 20m + tty: true + - name: testidem2 + image: docker.io/alpine + state: present + command: sleep 21m + - name: testidem3 + image: docker.io/alpine + state: present + command: sleep 22m + register: idem1 + + - name: Check that container is recreated when changed + assert: + that: + - idem1 is changed + + - name: Run changed container without specifying an option, use defaults + containers.podman.podman_containers: + containers: + - name: testidem + image: docker.io/alpine + state: present + command: sleep 20m + - name: testidem2 + image: docker.io/alpine + state: present + command: sleep 21m + - name: testidem3 + image: docker.io/alpine + state: present + command: sleep 22m + register: idem2 + + - name: Check that container is recreated when changed to default value + assert: + that: + - idem2 is changed + + - name: Remove container + containers.podman.podman_containers: + containers: + - name: testidem + state: absent + register: remove + + - name: Check podman_actions + assert: + that: + - "'podman rm -f testidem' in remove.podman_actions" + + - name: Create a pod + containers.podman.podman_pod: + name: testidempod + + - name: Check basic idempotency of pod container + containers.podman.podman_containers: + containers: + - name: testidem-pod + image: docker.io/alpine + state: present + command: sleep 20m + pod: "testidempod" + - name: testidem-pod2 + image: docker.io/alpine + state: present + command: sleep 20m + pod: testidempod + + - name: Check basic idempotency of pod container - run it again + containers.podman.podman_containers: + containers: + - name: testidem-pod + image: alpine:latest + state: present + command: sleep 20m + pod: testidempod + - name: testidem-pod2 + image: docker.io/alpine + state: present + command: sleep 20m + pod: testidempod + register: idem + + - name: Check that nothing was changed in pod containers + assert: + that: + - not idem.changed + + - name: Run changed pod container (with tty enabled) + containers.podman.podman_containers: + containers: + - name: testidem-pod + image: alpine + state: present + command: sleep 20m + tty: true + pod: testidempod + - name: testidem-pod2 + image: alpine + state: present + command: sleep 20m + pod: testidempod + register: idem1 + + - name: Check that container is recreated when changed + assert: + that: + - idem1 is changed + + - name: Remove container + containers.podman.podman_containers: + containers: + - name: testidem-pod + state: absent + - name: testidem-pod2 + state: absent + + always: + - name: Delete all container leftovers from tests + containers.podman.podman_container: + name: "{{ item }}" + state: absent + loop: + - "alpine:3.7" + - "container" + - "container1" + - "container2" + - "container3" + - "container4" + - "testidem" + - "testidem1" + - "testidem2" + - "testidem3" + - "testidem-pod" + - "testidem-pod2" + + - name: Remove pod + containers.podman.podman_pod: + name: testidempod + state: absent diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_image/files/Containerfile b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_image/files/Containerfile new file mode 100644 index 00000000..d4bd8edb --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_image/files/Containerfile @@ -0,0 +1,3 @@ +FROM quay.io/coreos/alpine-sh +ENV VAR testing +WORKDIR ${VAR} diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_image/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_image/tasks/main.yml new file mode 100644 index 00000000..e0eb1961 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_image/tasks/main.yml @@ -0,0 +1,190 @@ +- name: Test podman_image + block: + - name: Pull image + containers.podman.podman_image: + name: quay.io/coreos/alpine-sh + register: pull1 + + - name: Pull image again + containers.podman.podman_image: + name: quay.io/coreos/alpine-sh + register: pull2 + + - name: Pull image from docker.io with short url + containers.podman.podman_image: + name: docker.io/alpine + register: pull3 + + - name: Pull image from docker.io with short url again + containers.podman.podman_image: + name: docker.io/alpine + register: pull4 + + - name: Pull image from docker.io with official/normalised url again + containers.podman.podman_image: + name: docker.io/library/alpine + register: pull5 + + - name: List images + command: podman image ls + register: images + + - name: Ensure image were pulled properly + assert: + that: + - pull1 is changed + - pull2 is not changed + - pull3 is changed + - pull4 is changed + - pull5 is not changed + - "'alpine-sh' in images.stdout" + - "'library/alpine' in images.stdout" + + - name: add another tag (repository url) + command: + argv: + - podman + - tag + - quay.io/coreos/alpine-sh + - quay.io/coreos/library/alpine-sh + + - name: Remove image (tag) + containers.podman.podman_image: + name: quay.io/coreos/alpine-sh + state: absent + register: rmi1 + + - name: Remove image again + containers.podman.podman_image: + name: quay.io/coreos/alpine-sh + state: absent + register: rmi2 + + - name: Remove image using new repository url + containers.podman.podman_image: + name: quay.io/coreos/library/alpine-sh + state: absent + register: rmi3 + + - name: Try to remove docker.io image using short url + containers.podman.podman_image: + name: docker.io/alpine + state: absent + register: rmi4 + + - name: Remove docker.io image using normalised url + containers.podman.podman_image: + name: docker.io/library/alpine + state: absent + register: rmi5 + + - name: List images + command: podman image ls + register: images + + - name: Ensure image were removed properly + assert: + that: + - rmi1 is changed + - rmi2 is not changed + - rmi3 is changed + - rmi4 is not changed + - rmi5 is changed + - "'alpine-sh' not in images.stdout" + - "'library/alpine' not in images.stdout" + + - name: Pull a specific version of an image + containers.podman.podman_image: + name: quay.io/coreos/etcd + tag: v3.3.11 + register: specific_image1 + + - name: Pull a specific version of an image again + containers.podman.podman_image: + name: quay.io/coreos/etcd + tag: v3.3.11 + register: specific_image2 + + - name: List images + command: podman image ls + register: images + + - name: Ensure specific image was pulled properly + assert: + that: + - specific_image1 is changed + - specific_image2 is not changed + - "'v3.3.11' in images.stdout" + + - name: Create a build dir + file: + path: /var/tmp/build + state: directory + + - name: Copy Containerfile + copy: + src: Containerfile + dest: /var/tmp/build/Dockerfile + + - name: Build OCI image + containers.podman.podman_image: + name: testimage + path: /var/tmp/build + register: oci_build1 + + - name: Build OCI image again + containers.podman.podman_image: + name: testimage + path: /var/tmp/build + register: oci_build2 + + - name: Inspect build image + containers.podman.podman_image_info: + name: testimage + register: testimage_info + + - name: Ensure OCI image was built properly + assert: + that: + - oci_build1 is changed + - oci_build2 is not changed + - "'localhost/testimage:latest' in testimage_info.images[0]['RepoTags'][0]" + + - name: Build Docker image + containers.podman.podman_image: + name: dockerimage + path: /var/tmp/build + build: + format: docker + register: docker_build1 + + - name: Build Docker image again + containers.podman.podman_image: + name: dockerimage + path: /var/tmp/build + build: + format: docker + register: docker_build2 + + - name: Inspect build image + containers.podman.podman_image_info: + name: dockerimage + register: dockerimage_info + + - name: Ensure Docker image was built properly + assert: + that: + - docker_build1 is changed + - docker_build2 is not changed + - "'localhost/dockerimage:latest' in dockerimage_info.images[0]['RepoTags'][0]" + + always: + - name: Cleanup images + containers.podman.podman_image: + name: "{{ item }}" + state: absent + loop: + - quay.io/coreos/alpine-sh + - quay.io/coreos/etcd:v3.3.11 + - localhost/testimage + - localhost/dockerimage diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_image_info/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_image_info/tasks/main.yml new file mode 100644 index 00000000..c9bd0f83 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_image_info/tasks/main.yml @@ -0,0 +1,50 @@ +- name: Test podman_image_info + block: + - name: Pull image + command: podman pull quay.io/coreos/etcd + + - name: Get info on all images + containers.podman.podman_image_info: + register: all_image_result + + - name: Pull another image + command: podman pull quay.io/coreos/dnsmasq + + - name: Get info on specific image + containers.podman.podman_image_info: + name: dnsmasq + register: named_image_result + + - name: + assert: + that: + - all_image_result.images | length > 0 + - named_image_result.images | length == 1 + - "'dnsmasq' in named_image_result.images[0]['RepoTags'][0]" + + - name: Get info on single image that does not exist + containers.podman.podman_image_info: + name: nope + register: single_nonexistant + + - name: Get info on multiple images that do not exist + containers.podman.podman_image_info: + name: + - nope + - reallynope + register: multiple_nonexistant + + - name: Get info with one image that does not exist + containers.podman.podman_image_info: + name: + - dnsmasq + - nope + - etcd + register: mixed_nonexistant + + - name: Ensure image info was returned when non-existant image info was requisted + assert: + that: + - single_nonexistant.images | length == 0 + - multiple_nonexistant.images | length == 0 + - mixed_nonexistant.images | length == 2 diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_login_info/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_login_info/tasks/main.yml new file mode 100644 index 00000000..020c8446 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_login_info/tasks/main.yml @@ -0,0 +1,62 @@ +- name: Test podman_login_info + block: + + - name: Print podman version + command: podman version + + - name: Get login info with invalid executable + containers.podman.podman_login_info: + executable: podman_invalid + registry: quay.io + register: invalid_executable + ignore_errors: yes + + - name: Check invalid executable results + assert: + that: + - invalid_executable is failed + + - name: Get login info without specifying registry + containers.podman.podman_login_info: + executable: podman_invalid + register: missing_registry + ignore_errors: yes + + - name: Check missing registry results + assert: + that: + - missing_registry is failed + + - name: Get login info for a non-existing registry + containers.podman.podman_login_info: + registry: non-existing.registry + register: non_existing_registry + + - name: Check non-existing registry results + assert: + that: + - "'login' in non_existing_registry" + - non_existing_registry.login + - "'registry' in non_existing_registry.login" + - "'username' in non_existing_registry.login" + - "'logged_in' in non_existing_registry.login" + - "non_existing_registry.login.registry == 'non-existing.registry'" + - "non_existing_registry.login.username == ''" + - "non_existing_registry.login.logged_in == False" + + - name: Get login info for a non-existing authfile + # This will return not logged in even if logged in via different authfile + containers.podman.podman_login_info: + registry: quay.io + authfile: non-existing.authfile.json + register: non_existing_authfile + + - name: Check non-existing authfile results + assert: + that: + - "'login' in non_existing_authfile" + - non_existing_authfile.login + - "'username' in non_existing_authfile.login" + - "'logged_in' in non_existing_authfile.login" + - "non_existing_authfile.login.username == ''" + - "non_existing_authfile.login.logged_in == False" diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_logout/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_logout/tasks/main.yml new file mode 100644 index 00000000..c2f7d47f --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_logout/tasks/main.yml @@ -0,0 +1,52 @@ +- name: Test podman_logout + block: + + - name: Print podman version + command: podman version + + - name: Logout from docker if it exists + command: docker logout + ignore_errors: true + + - name: Log out with invalid executable + containers.podman.podman_logout: + executable: podman_invalid + register: invalid_executable + ignore_errors: yes + + - name: Check invalid executable results + assert: + that: + - invalid_executable is failed + + - name: Log out of non-existing registry + containers.podman.podman_logout: + register: non_existing_registry + + - name: Check results + assert: + that: + - "'changed' in non_existing_registry" + - "non_existing_registry.changed == False" + + - name: Log out with invalid authfile + containers.podman.podman_logout: + authfile: authfile_invalid.json + register: invalid_authfile + ignore_errors: yes + + - name: Check invalid authfile results + assert: + that: + - invalid_authfile is failed + + - name: Log out of all registries + containers.podman.podman_logout: + all: yes + register: all_registries + + - name: Check results + assert: + that: + - "'changed' in all_registries" + - "all_registries.changed == True" diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_network/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_network/tasks/main.yml new file mode 100644 index 00000000..488ad2b3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_network/tasks/main.yml @@ -0,0 +1,224 @@ +- name: Test podman_network + become: true + block: + + - name: Print podman version + command: podman version + + - name: Check if dnsname plugin is installed + block: + + - name: Check if plugin is installed + stat: + path: "{{ item }}" + loop: + - /usr/libexec/cni/dnsname + - /usr/lib/cni/dnsname + - /opt/cni/bin/dnsname + - /opt/bridge/bin/dnsname + register: plugin_results + + - name: Set plugin fact + set_fact: + dns_plugin: "{{ true in plugin_results.results|map(attribute='stat.exists') }}" + + - name: Generate random value for network name + set_fact: + network_name: "{{ 'ansible-test-podman-%0x' % ((2**32) | random) }}" + + - name: Make sure network doesn't exist + containers.podman.podman_network: + name: "{{ network_name }}" + state: absent + + - name: Get missing network info + containers.podman.podman_network_info: + name: "{{ network_name }}" + register: info + ignore_errors: true + + - name: Check results + assert: + that: + - info is failed + + - name: Create network + containers.podman.podman_network: + name: "{{ network_name }}" + register: net + + - name: Get existing network info + containers.podman.podman_network_info: + name: "{{ network_name }}" + register: info1 + + - name: Check info + assert: + that: + - info1 | length > 1 + - info1.networks.0.name == network_name + - net is changed + + - name: Create network again + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + register: info2 + + - name: Check info + assert: + that: + - info2 is not changed + + - name: Create network with disable DNS + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + disable_dns: true + register: info3 + + - name: Check info + assert: + that: + - >- + info3 is changed and dns_plugin|bool or + info3 is not changed and not dns_plugin|bool + + - name: Create network with disable DNS again + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + disable_dns: true + register: info4 + + - name: Check info + assert: + that: + - info4 is not changed + + - name: Create network w/o disable DNS + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + register: info5 + + - name: Check info + assert: + that: + - >- + info5 is changed and dns_plugin|bool or + info5 is not changed and not dns_plugin|bool + + - name: Create network with custom gateway + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + gateway: 10.100.100.100 + subnet: 10.100.100.0/24 + register: info6 + + - name: Check info + assert: + that: + - info6 is changed + + - name: Create network with custom gateway again + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + gateway: 10.100.100.100 + subnet: 10.100.100.0/24 + register: info7 + + - name: Check info + assert: + that: + - info7 is not changed + + - name: Create internal network + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + internal: true + register: info9 + + - name: Check info + assert: + that: + - info9 is changed + + - name: Create internal network again + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + internal: true + register: info10 + + - name: Check info + assert: + that: + - info10 is not changed + + - name: Create a regular external network + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + register: info11 + + - name: Check info + assert: + that: + - info11 is changed + + - name: Create network with subnet + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + subnet: 10.200.200.0/24 + register: info12 + + - name: Check info + assert: + that: + - info12 is changed + + - name: Create network with subnet again + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + subnet: 10.200.200.0/24 + register: info13 + + - name: Check info + assert: + that: + - info13 is not changed + + - name: Create network with ipv6 subnet + containers.podman.podman_network: + name: "{{ network_name }}" + state: present + subnet: 2001:cafe::/64 + ipv6: true + + - name: Make sure network doesn't exist + containers.podman.podman_network: + name: "{{ network_name }}" + state: absent + + - name: Get existing network info + containers.podman.podman_network_info: + name: "{{ network_name }}" + register: info100 + ignore_errors: true + + - name: Check results + assert: + that: + - info100 is failed + + always: + + - name: Cleanup + command: podman network rm -f {{ network_name }} + ignore_errors: true diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_network_info/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_network_info/tasks/main.yml new file mode 100644 index 00000000..81bcaa30 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_network_info/tasks/main.yml @@ -0,0 +1,60 @@ +- name: Test podman_network_info + become: true + block: + + - name: Print podman version + command: podman version + + - name: Generate random value for network name + set_fact: + network_name: "{{ 'ansible-test-podman-%0x' % ((2**32) | random) }}" + + - name: Make sure network doesn't exist + command: podman network rm {{ network_name }} + ignore_errors: true + + - name: Get missing network info + containers.podman.podman_network_info: + name: "{{ network_name }}" + register: nonexist + ignore_errors: true + + - name: Check results + assert: + that: + - "'networks' not in nonexist" + - nonexist is failed + + - name: Make sure network exists + command: podman network create {{ network_name }} + + - name: Get existing network info + containers.podman.podman_network_info: + name: "{{ network_name }}" + register: existing_network + + - name: Dump podman network inspect result + debug: var=existing_network + + - name: Comparison with 'podman network inspect' + command: podman network inspect "{{ network_name }}" + register: podman_inspect + + - name: Convert podman inspect output to JSON + set_fact: + podman_inspect_result: "{{ podman_inspect.stdout | from_json }}" + + - name: Cleanup + command: podman network rm {{ network_name }} + + - name: Make checks + assert: + that: + - "'networks' in existing_network" + - existing_network.networks + - "existing_network.networks == podman_inspect_result" + always: + + - name: Cleanup + command: podman network rm {{ network_name }} + ignore_errors: true diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/main.yml new file mode 100644 index 00000000..2ede24bd --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/main.yml @@ -0,0 +1,613 @@ +- name: Test podman pod + block: + + - name: Discover podman version + shell: podman version | grep "^Version:" | awk {'print $2'} + register: podman_v + + - name: Set podman version to 1 + set_fact: + podman_version: 1 + when: podman_v.stdout is version('2.0.0', 'lt') + + - name: Set podman version to 2 + set_fact: + podman_version: 2 + when: podman_v.stdout is version('2.0.0', '>=') + + - name: Discover cgroups version + shell: podman info | grep cgroupVersion | awk {'print $2'} + register: cgroups + + - name: Set cgroups version + set_fact: + cgroups_version: "{{ cgroups.stdout }}" + + - name: Delete all pods leftovers from tests + containers.podman.podman_pod: + name: "{{ item }}" + state: absent + loop: + - "pod1" + - "pod2" + + - name: Create pod + containers.podman.podman_pod: + name: pod1 + state: created + register: pod1_info + + - name: Check info + assert: + that: + - >- + (pod1_info.pod['State']['status'] is defined and + pod1_info.pod['State']['status'] == 'Created') or + (pod1_info.pod['State']['status'] is not defined and + pod1_info.pod['State'] == 'Created') + + - name: Start pod + containers.podman.podman_pod: + name: pod1 + state: started + register: pod2_info + + - name: Check info + assert: + that: + - >- + (pod2_info.pod['State']['status'] is defined and + pod2_info.pod['State']['status'] == 'Running') or + (pod2_info.pod['State']['status'] is not defined and + pod2_info.pod['State'] == 'Running') + + - name: Pause pod + containers.podman.podman_pod: + name: pod1 + state: paused + register: pod3_info + when: cgroups_version == 'v2' + + - name: Check info + assert: + that: + - >- + (pod3_info.pod['State']['status'] is defined and + pod3_info.pod['State']['status'] == 'Paused') or + (pod3_info.pod['State']['status'] is not defined and + pod3_info.pod['State'] == 'Paused') + when: cgroups_version == 'v2' + + - name: Unpause pod + containers.podman.podman_pod: + name: pod1 + state: unpaused + register: pod4_info + when: cgroups_version == 'v2' + + - name: Check info + assert: + that: + - >- + (pod4_info.pod['State']['status'] is defined and + pod4_info.pod['State']['status'] == 'Running') or + (pod4_info.pod['State']['status'] is not defined and + pod4_info.pod['State'] == 'Running') + when: cgroups_version == 'v2' + + - name: Stop pod + containers.podman.podman_pod: + name: pod1 + state: stopped + register: pod5_info + + - name: Check info + assert: + that: + - >- + (pod5_info.pod['State']['status'] is defined and + pod5_info.pod['State']['status'] != 'Running') or + (pod5_info.pod['State']['status'] is not defined and + pod5_info.pod['State'] != 'Running') + + - name: Kill pod + containers.podman.podman_pod: + name: pod1 + state: killed + register: pod6_info + + - name: Check info + assert: + that: + - >- + (pod6_info.pod['State']['status'] is defined and + pod6_info.pod['State']['status'] == 'Exited') or + (pod6_info.pod['State']['status'] is not defined and + pod6_info.pod['State'] == 'Exited') + + - name: Start pod + containers.podman.podman_pod: + name: pod1 + state: started + register: pod7_info + + - name: Check info + assert: + that: + - >- + (pod7_info.pod['State']['status'] is defined and + pod7_info.pod['State']['status'] == 'Running') or + (pod7_info.pod['State']['status'] is not defined and + pod7_info.pod['State'] == 'Running') + + - name: Start pod again for idempotency + containers.podman.podman_pod: + name: pod1 + state: started + register: pod8_info + + - name: Check info + assert: + that: + - pod8_info is not changed + + - name: Start pod with ports + containers.podman.podman_pod: + name: pod1 + state: started + ports: + - "4444:4444/tcp" + - "1212:5555" + - "8888:19191/udp" + - "1900:1900/udp" + - "127.0.0.1:7671:7676/udp" + - "127.0.0.1:12122:8876/udp" + - "127.0.0.1:13122:8871/tcp" + - "127.0.0.1:43423:8872" + register: pod9_info + + - name: Check info + assert: + that: + - pod9_info is changed + + - name: Start pod with ports for idempotency + containers.podman.podman_pod: + name: pod1 + state: started + ports: + - "4444:4444/tcp" + - "1212:5555" + - "8888:19191/udp" + - "1900:1900/udp" + - "127.0.0.1:7671:7676/udp" + - "127.0.0.1:12122:8876/udp" + - "127.0.0.1:13122:8871/tcp" + - "127.0.0.1:43423:8872" + register: pod10_info + + - name: Check info + assert: + that: + - pod10_info is not changed + + - name: Start pod again for idempotency + containers.podman.podman_pod: + name: pod1 + state: started + register: pod11_info + + - name: Check info + assert: + that: + - pod11_info is changed + + - name: Start pod with share + containers.podman.podman_pod: + name: pod1 + state: started + share: uts + register: pod12_info + + - name: Check info + assert: + that: + - pod12_info is changed + + - name: Start pod with share for idempotency + containers.podman.podman_pod: + name: pod1 + state: started + share: uts + register: pod13_info + + - name: Check info + assert: + that: + - pod13_info is not changed + + - name: Start pod without shares + containers.podman.podman_pod: + name: pod1 + state: started + share: "" + register: pod14_info + + - name: Check info + assert: + that: + - pod14_info is changed + + - name: Start pod with default shares + containers.podman.podman_pod: + name: pod1 + state: started + register: pod15_info + + - name: Check info + assert: + that: + - pod15_info is changed + + - name: Start pod with labels + containers.podman.podman_pod: + name: pod1 + state: started + label: + key: cval + otherkey: kddkdk + somekey: someval + register: pod16_info + + - name: Check info + assert: + that: + - pod16_info is changed + + - name: Start pod with labels again for idempotency + containers.podman.podman_pod: + name: pod1 + state: started + label: + key: cval + otherkey: kddkdk + somekey: someval + register: pod17_info + + - name: Check info + assert: + that: + - pod17_info is not changed + + - name: Start pod with different labels + containers.podman.podman_pod: + name: pod1 + state: started + label: + key: cval + otherkey: 23434dfsd + somekey: someval + register: pod18_info + + - name: Check info + assert: + that: + - pod18_info is changed + + - name: Start pod without labels + containers.podman.podman_pod: + name: pod1 + state: started + register: pod19_info + + - name: Check info + assert: + that: + - pod19_info is changed + + - name: Start pod with dns and hosts settings + containers.podman.podman_pod: + name: pod1 + state: started + add_host: + - "google:8.8.8.8" + - "site1:127.0.0.1" + dns: + - 1.1.1.1 + dns_opt: + - "option timeout:3" + dns_search: + - "redhat.com" + - "ibm.com" + hostname: happypod + register: pod20_info + + - name: Check info + assert: + that: + - pod20_info is changed + + - name: Start pod with dns and hosts settings again + containers.podman.podman_pod: + name: pod1 + state: started + add_host: + - "google:8.8.8.8" + - "site1:127.0.0.1" + dns: + - 1.1.1.1 + dns_opt: + - "option timeout:3" + dns_search: + - "redhat.com" + - "ibm.com" + hostname: happypod + register: pod21_info + + - name: Check info + assert: + that: + - pod21_info is not changed + + - name: Start pod with changed dns + containers.podman.podman_pod: + name: pod1 + state: started + add_host: + - "google:8.8.8.8" + - "site1:127.0.0.1" + dns: + - 1.1.1.1 + - 2.2.2.2 + dns_opt: + - "option timeout:3" + dns_search: + - "redhat.com" + - "ibm.com" + hostname: happypod + register: pod22_info + + - name: Check info + assert: + that: + - pod22_info is changed + + - name: Start pod with changed add host + containers.podman.podman_pod: + name: pod1 + state: started + add_host: + - "google:8.8.8.8" + - "site1:127.0.0.2" + dns: + - 1.1.1.1 + - 2.2.2.2 + dns_opt: + - "option timeout:3" + dns_search: + - "redhat.com" + - "ibm.com" + hostname: happypod + register: pod23_info + + - name: Check info + assert: + that: + - pod23_info is changed + + - name: Start pod with changed dns option + containers.podman.podman_pod: + name: pod1 + state: started + add_host: + - "google:8.8.8.8" + - "site1:127.0.0.2" + dns: + - 1.1.1.1 + - 2.2.2.2 + dns_opt: + - "option timeout:2" + dns_search: + - "redhat.com" + - "ibm.com" + hostname: happypod + register: pod24_info + + - name: Check info + assert: + that: + - pod24_info is changed + + - name: Start pod with changed dns search + containers.podman.podman_pod: + name: pod1 + state: started + add_host: + - "google:8.8.8.8" + - "site1:127.0.0.2" + dns: + - 1.1.1.1 + - 2.2.2.2 + dns_opt: + - "option timeout:2" + dns_search: + - "redhat.com" + hostname: happypod + register: pod25_info + + - name: Check info + assert: + that: + - pod25_info is changed + + - name: Start pod with changed hostname + containers.podman.podman_pod: + name: pod1 + state: started + add_host: + - "google:8.8.8.8" + - "site1:127.0.0.2" + dns: + - 1.1.1.1 + - 2.2.2.2 + dns_opt: + - "option timeout:2" + dns_search: + - "redhat.com" + hostname: bestpod + register: pod26_info + + - name: Check info + assert: + that: + - pod26_info is changed + + - name: Start pod with removed dns search + containers.podman.podman_pod: + name: pod1 + state: started + add_host: + - "google:8.8.8.8" + - "site1:127.0.0.2" + dns: + - 1.1.1.1 + - 2.2.2.2 + dns_opt: + - "option timeout:2" + hostname: bestpod + register: pod27_info + + - name: Check info + assert: + that: + - pod27_info is changed + + - name: Start pod with removed dns option + containers.podman.podman_pod: + name: pod1 + state: started + add_host: + - "google:8.8.8.8" + - "site1:127.0.0.2" + dns: + - 1.1.1.1 + - 2.2.2.2 + hostname: bestpod + register: pod28_info + + - name: Check info + assert: + that: + - pod28_info is changed + + - name: Start pod with removed dns + containers.podman.podman_pod: + name: pod1 + state: started + add_host: + - "google:8.8.8.8" + - "site1:127.0.0.2" + hostname: bestpod + register: pod29_info + + - name: Check info + assert: + that: + - pod29_info is changed + + - name: Start pod with removed add host + containers.podman.podman_pod: + name: pod1 + state: started + hostname: bestpod + register: pod30_info + + - name: Check info + assert: + that: + - pod30_info is changed + + - name: Start pod without infra + containers.podman.podman_pod: + name: pod1 + state: created + infra: false + register: pod31_info + + - name: Check info + assert: + that: + - pod31_info is changed + + - name: Start pod without infra again + containers.podman.podman_pod: + name: pod1 + state: created + infra: false + register: pod32_info + + - name: Check info + assert: + that: + - pod32_info is not changed + + - name: Start pod with infra + containers.podman.podman_pod: + name: pod1 + state: started + register: pod33_info + + - name: Check info + assert: + that: + - pod33_info is changed + + - name: Start pod with different infra image + containers.podman.podman_pod: + name: pod1 + state: created + infra_image: alpine:3.9 + register: pod34_info + + - name: Check info + assert: + that: + - pod34_info is changed + + - name: Start pod with different infra image again + containers.podman.podman_pod: + name: pod1 + state: created + infra_image: alpine:3.9 + register: pod35_info + + - name: Check info + assert: + that: + - pod35_info is not changed + + always: + + - name: Delete all pods leftovers from tests + containers.podman.podman_pod: + name: "{{ item }}" + state: absent + loop: + - "pod1" + - "pod2" + +- name: Test idempotency for root pods + include_tasks: root-pod.yml + vars: + ansible_python_interpreter: "/usr/bin/python" + args: + apply: + become: true + +- name: Test idempotency for root pods and networks + include_tasks: net-pod.yml + vars: + ansible_python_interpreter: "/usr/bin/python" + args: + apply: + become: true diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/net-pod.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/net-pod.yml new file mode 100644 index 00000000..b1b15def --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/net-pod.yml @@ -0,0 +1,48 @@ +- name: Test podman rootful pod + block: + + - name: Remove pods + containers.podman.podman_pod: + name: "rootnetpod" + state: absent + + - name: Create network testnet + command: podman network create testnet --subnet 10.91.91.0/24 + + - name: Create network anothernet + command: podman network create anothernet --subnet 10.71.71.0/24 + + - name: List current networks + command: podman network ls + + - name: Set test data + set_fact: + testdata: + - first_net: host + next_net: bridge + - first_net: bridge + next_net: host + - first_net: anothernet + next_net: testnet + - first_net: testnet + next_net: testnet,anothernet + - first_net: testnet,anothernet + next_net: anothernet + - first_net: testnet,anothernet + next_net: bridge + - first_net: testnet,anothernet + next_net: host + - first_net: host + next_net: anothernet + - first_net: bridge + next_net: anothernet,testnet + + - include_tasks: network-tests.yml + loop: "{{ testdata }}" + + always: + + - name: Delete all pods leftovers from tests + containers.podman.podman_pod: + name: "rootnetpod" + state: absent diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/network-tests.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/network-tests.yml new file mode 100644 index 00000000..8df7016b --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/network-tests.yml @@ -0,0 +1,39 @@ +--- +- name: Remove pod nettest + become: true + containers.podman.podman_pod: + name: nettest + state: absent + +- name: Run pod with {{ item.first_net }} + become: true + containers.podman.podman_pod: + name: nettest + state: started + network: "{{ item.first_net }}" + +- name: Run pod again with {{ item.first_net }} + become: true + containers.podman.podman_pod: + name: nettest + state: started + network: "{{ item.first_net }}" + register: info + +- name: Check info that not changed + assert: + that: + - info is not changed + +- name: Run pod changed from {{ item.first_net }} to {{ item.next_net }} + become: true + containers.podman.podman_pod: + name: nettest + state: started + network: "{{ item.next_net }}" + register: info1 + +- name: Check info + assert: + that: + - info1 is changed diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/root-pod.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/root-pod.yml new file mode 100644 index 00000000..d0027417 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod/tasks/root-pod.yml @@ -0,0 +1,120 @@ +- name: Test podman rootful pod + block: + + - name: Discover cgroups version + shell: podman info | grep cgroupVersion | awk {'print $2'} + register: cgroups + + - name: Set cgroups version + set_fact: + cgroups_version: "{{ cgroups.stdout }}" + + - name: Delete all pods leftovers from tests + containers.podman.podman_pod: + name: "rootpod" + state: absent + + - name: Create and start pod + containers.podman.podman_pod: + name: "rootpod" + state: started + + - name: Create and start pod + containers.podman.podman_pod: + name: "rootpod" + state: started + register: pod1_info + + - name: Check info + assert: + that: + - pod1_info is not changed + + - name: Add DNS to pod + containers.podman.podman_pod: + name: "rootpod" + state: started + dns: + - 1.1.1.1 + register: pod2_info + + - name: Check info + assert: + that: + - pod2_info is changed + + - name: Remove DNS from pod + containers.podman.podman_pod: + name: "rootpod" + state: started + register: pod3_info + + - name: Check info + assert: + that: + - pod3_info is changed + + - name: Create network newnet + command: podman network create newnet --subnet 10.90.90.0/24 + + - name: Create network net2 + command: podman network create net2 --subnet 10.70.70.0/24 + + - name: Start pod with networks + containers.podman.podman_pod: + name: "rootpod" + state: started + network: net2,newnet + register: pod4_info + + - name: Check info + assert: + that: + - pod4_info is changed + + - name: Start pod with networks again + containers.podman.podman_pod: + name: "rootpod" + state: started + network: newnet,net2 + register: pod5_info + + - name: Check info + assert: + that: + - pod5_info is not changed + + - name: Start pod with one network + containers.podman.podman_pod: + name: "rootpod" + state: started + network: net2 + register: pod6_info + + - name: Check info + assert: + that: + - pod6_info is changed + + - name: Start pod without networks + containers.podman.podman_pod: + name: "rootpod" + state: started + register: pod7_info + + - name: Check info + assert: + that: + - pod7_info is changed + + always: + + - name: Delete all pods leftovers from tests + containers.podman.podman_pod: + name: "rootpod" + state: absent + + - name: Delete all existing pods + shell: | + podman pod rm -fa; + ignore_errors: true diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod_info/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod_info/tasks/main.yml new file mode 100644 index 00000000..ccf20bf2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_pod_info/tasks/main.yml @@ -0,0 +1,103 @@ +- name: Test podman_pod_info + block: + + - name: Print podman version + command: podman info --debug + + - name: Discover podman version + shell: podman version | grep "^Version:" | awk {'print $2'} + register: podman_version + + - name: Generate random value for pod name + set_fact: + pod_name: "{{ 'ansible-test-podman-%0x' % ((2**32) | random) }}" + + - name: Make sure pod doesn't exist + containers.podman.podman_pod: + name: "{{ pod_name }}" + state: absent + + - name: Get missing pod info + containers.podman.podman_pod_info: + name: "{{ pod_name }}" + register: nonexist + + - name: Check info for missing pod + assert: + that: + - nonexist.pods == [] + + - name: Get all missing pods info + containers.podman.podman_pod_info: + register: nonexist2 + + - name: Check info for missing pod + assert: + that: + - nonexist2.pods == [] + + - name: Create test pod + containers.podman.podman_pod: + name: "{{ pod_name }}" + + - name: Get all pods info + containers.podman.podman_pod_info: + register: info + + - name: Check info for pod + assert: + that: + - info.pods | length == 1 + - >- + (info.pods[0]['Config']['name'] is defined and + info.pods[0]['Config']['name'] == pod_name) or + (info.pods[0]['Name'] is defined and + info.pods[0]['Name'] == pod_name) + + - name: Get specific pod info + containers.podman.podman_pod_info: + name: "{{ pod_name }}" + register: info2 + + - name: Check info for pod + assert: + that: + - info2.pods | length == 1 + - >- + (info2.pods[0]['Config']['name'] is defined and + info2.pods[0]['Config']['name'] == pod_name) or + (info2.pods[0]['Name'] is defined and + info2.pods[0]['Name'] == pod_name) + + - name: Create another test pod + containers.podman.podman_pod: + name: "{{ pod_name }}_1" + + - name: Get specific another pod info + containers.podman.podman_pod_info: + name: "{{ pod_name }}" + register: info3 + + - name: Check info for missing pod + assert: + that: + - info3.pods | length == 1 + + - name: Get pods info + containers.podman.podman_pod_info: + register: info4 + + - name: Check info for pods + assert: + that: + - info4.pods | length == 2 + + always: + + - name: Make sure pod doesn't exist + containers.podman.podman_pod: + name: "{{ item }}" + state: absent + loop: + - "{{ pod_name }}" + - "{{ pod_name }}_1" diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_volume/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_volume/tasks/main.yml new file mode 100644 index 00000000..669b34c5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_volume/tasks/main.yml @@ -0,0 +1,267 @@ +- name: Test podman_volume + block: + + - name: Print podman version + command: podman version + + - name: Generate random value for volume name + set_fact: + volume_name: "{{ 'ansible-test-podman-%0x' % ((2**32) | random) }}" + + - name: Make sure volume doesn't exist + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: absent + + - name: Get missing volume info + containers.podman.podman_volume_info: + name: "{{ volume_name }}" + register: info + ignore_errors: true + + - name: Check results + assert: + that: + - info is failed + + - name: Create volume + containers.podman.podman_volume: + name: "{{ volume_name }}" + register: vol + + - name: Get existing volume info + containers.podman.podman_volume_info: + name: "{{ volume_name }}" + register: info1 + + - name: Check info + assert: + that: + - info1 | length > 1 + - info1.volumes.0.Name == volume_name + - vol is changed + + - name: Create volume again + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + register: info2 + + - name: Check info + assert: + that: + - info2 is not changed + + - name: Create volume with labels + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + label: + key: val + nokey: noval + register: info3 + + - name: Check info + assert: + that: + - info3 is changed + + - name: Create volume with labels again + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + label: + key: val + nokey: noval + register: info4 + + - name: Check info + assert: + that: + - info4 is not changed + + - name: Create volume w/o labels + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + register: info5 + + - name: Check info + assert: + that: + - info5 is changed + + - name: Create volume with options + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + options: + - "device=/dev/something" + - "type=ext4" + register: info6 + + - name: Check info + assert: + that: + - info6 is changed + + - name: Create volume with options again + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + options: + - "device=/dev/something" + - "type=ext4" + register: info7 + + - name: Check info + assert: + that: + - info7 is not changed + + - name: Create volume w/o options + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + register: info8 + + - name: Check info + assert: + that: + - info8 is changed + + - name: Make sure volume doesn't exist + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: absent + register: delete + + - name: Get existing volume info + containers.podman.podman_volume_info: + name: "{{ volume_name }}" + register: info10 + ignore_errors: true + + - name: Check results + assert: + that: + - info10 is failed + - delete.volume == {} + + - name: Create volume with UID option + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + options: + - "o=uid=1001" + register: info11 + + - name: Check info + assert: + that: + - info11 is changed + + - name: Create volume with UID option - again + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + options: + - "o=uid=1001" + register: info12 + + - name: Check info + assert: + that: + - info12 is not changed + + - name: Create volume with UID option - default + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + register: info13 + + - name: Check info + assert: + that: + - info13 is changed + + - name: Create volume with GID option + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + options: + - "o=gid=1001" + register: info14 + + - name: Check info + assert: + that: + - info14 is changed + + - name: Create volume with GID option - again + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + options: + - "o=gid=1001" + register: info15 + + - name: Check info + assert: + that: + - info15 is not changed + + - name: Create volume with GID option - default + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + register: info16 + + - name: Check info + assert: + that: + - info16 is changed + + - name: Create volume with GID and UID option + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + options: + - "o=gid=1002,uid=1002" + register: info17 + + - name: Check info + assert: + that: + - info17 is changed + + - name: Create volume with GID and UID option - again + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + options: + - "o=uid=1002" + - "o=gid=1002" + register: info18 + + - name: Check info + assert: + that: + - info18 is not changed + + - name: Create volume with GID and UID option - default + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + register: info19 + + - name: Check info + assert: + that: + - info19 is changed + always: + + - name: Make sure volume doesn't exist + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: absent diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_volume_info/tasks/main.yml b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_volume_info/tasks/main.yml new file mode 100644 index 00000000..9bd046d7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/integration/targets/podman_volume_info/tasks/main.yml @@ -0,0 +1,65 @@ +- name: Test podman_volume_info + block: + + - name: Print podman version + command: podman version + + - name: Generate random value for volume name + set_fact: + volume_name: "{{ 'ansible-test-podman-%0x' % ((2**32) | random) }}" + + - name: Make sure volume doesn't exist + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: absent + + - name: Get missing volume info + containers.podman.podman_volume_info: + name: "{{ volume_name }}" + register: nonexist + ignore_errors: true + + - name: Check results + assert: + that: + - "'volumes' not in nonexist" + - nonexist is failed + + - name: Make sure volume exists + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: present + + - name: Get existing volume info + containers.podman.podman_volume_info: + name: "{{ volume_name }}" + register: existing_volume + + - name: Dump podman volume inspect result + debug: var=existing_volume + + - name: Comparison with 'podman volume inspect' + command: podman volume inspect "{{ volume_name }}" + register: podman_inspect + + - name: Convert podman inspect output to JSON + set_fact: + podman_inspect_result: "{{ podman_inspect.stdout | from_json }}" + + - name: Cleanup + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: absent + + - name: Make checks + assert: + that: + - "'volumes' in existing_volume" + - existing_volume.volumes + - "existing_volume.volumes == podman_inspect_result" + always: + + - name: Cleanup + containers.podman.podman_volume: + name: "{{ volume_name }}" + state: absent diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/ignore-2.10.txt b/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/ignore-2.10.txt new file mode 100644 index 00000000..1dc834b7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/ignore-2.10.txt @@ -0,0 +1,5 @@ +plugins/modules/podman_image.py validate-modules:mutually_exclusive-unknown +plugins/modules/podman_container.py validate-modules:parameter-list-no-elements +tests/integration/targets/connection_buildah/runme.sh shellcheck:SC2086 +tests/integration/targets/connection_podman/runme.sh shellcheck:SC2086 +plugins/module_utils/podman/common.py shebang!skip diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/ignore-2.11.txt b/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/ignore-2.11.txt new file mode 100644 index 00000000..1dc834b7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/ignore-2.11.txt @@ -0,0 +1,5 @@ +plugins/modules/podman_image.py validate-modules:mutually_exclusive-unknown +plugins/modules/podman_container.py validate-modules:parameter-list-no-elements +tests/integration/targets/connection_buildah/runme.sh shellcheck:SC2086 +tests/integration/targets/connection_podman/runme.sh shellcheck:SC2086 +plugins/module_utils/podman/common.py shebang!skip diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/ignore-2.9.txt b/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/ignore-2.9.txt new file mode 100644 index 00000000..9521178f --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/ignore-2.9.txt @@ -0,0 +1,3 @@ +tests/integration/targets/connection_buildah/runme.sh shellcheck:SC2086 +tests/integration/targets/connection_podman/runme.sh shellcheck:SC2086 +plugins/module_utils/podman/common.py shebang!skip diff --git a/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/requirements.txt b/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/requirements.txt new file mode 100644 index 00000000..fd9f609d --- /dev/null +++ b/collections-debian-merged/ansible_collections/containers/podman/tests/sanity/requirements.txt @@ -0,0 +1,8 @@ +packaging # needed for update-bundled and changelog +sphinx ; python_version >= '3.5' # docs build requires python 3+ +sphinx-notfound-page ; python_version >= '3.5' # docs build requires python 3+ +straight.plugin ; python_version >= '3.5' # needed for hacking/build-ansible.py which will host changelog generation and requires python 3+ +voluptuous +yamllint +pylint +virtualenv |