diff options
Diffstat (limited to 'ansible_collections/community/google')
41 files changed, 0 insertions, 8695 deletions
diff --git a/ansible_collections/community/google/.github/workflows/ansible-test.yml b/ansible_collections/community/google/.github/workflows/ansible-test.yml deleted file mode 100644 index 652b774ac..000000000 --- a/ansible_collections/community/google/.github/workflows/ansible-test.yml +++ /dev/null @@ -1,110 +0,0 @@ -# README FIRST -# 1. replace "NAMESPACE" and "COLLECTION_NAME" with the correct name in the env section (e.g. with 'community' and 'mycollection') -# 2. If you don't have unit tests remove that section -# 3. If your collection depends on other collections ensure they are installed, see "Install collection dependencies" -# If you need help please ask in #ansible-devel on Freenode IRC - -name: CI -on: - # Run CI against all pushes (direct commits, also merged PRs), Pull Requests - push: - pull_request: - schedule: - - cron: '0 6 * * *' -env: - NAMESPACE: community - COLLECTION_NAME: google - -jobs: - -### -# Sanity tests (REQUIRED) -# -# https://docs.ansible.com/ansible/latest/dev_guide/testing_sanity.html - - sanity: - name: Sanity (Ⓐ${{ matrix.ansible }}) - strategy: - matrix: - ansible: - # It's important that Sanity is tested against all stable-X.Y branches - # Testing against `devel` may fail as new tests are added. - # - stable-2.9 # Only if your collection supports Ansible 2.9 - - stable-2.10 - - devel - runs-on: ubuntu-latest - steps: - - # ansible-test requires the collection to be in a directory in the form - # .../ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}}/ - - - name: Check out code - uses: actions/checkout@v2 - with: - path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - - - name: Set up Python - uses: actions/setup-python@v2 - with: - # it is just required to run that once as "ansible-test sanity" in the docker image - # will run on all python versions it supports. - python-version: 3.8 - - # Install the head of the given branch (devel, stable-2.10) - - name: Install ansible-base (${{ matrix.ansible }}) - run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check - - # run ansible-test sanity inside of Docker. - # The docker container has all the pinned dependencies that are required - # and all python versions ansible supports. - - name: Run sanity tests - run: ansible-test sanity --docker -v --color - working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - -### -# Unit tests (OPTIONAL) -# -# https://docs.ansible.com/ansible/latest/dev_guide/testing_units.html - - units: - runs-on: ubuntu-latest - name: Units (Ⓐ${{ matrix.ansible }}) - strategy: - # As soon as the first unit test fails, cancel the others to free up the CI queue - fail-fast: true - matrix: - ansible: - # - stable-2.9 # Only if your collection supports Ansible 2.9 - - stable-2.10 - - devel - - steps: - - name: Check out code - uses: actions/checkout@v2 - with: - path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - - - name: Set up Python - uses: actions/setup-python@v2 - with: - # it is just required to run that once as "ansible-test units" in the docker image - # will run on all python versions it supports. - python-version: 3.8 - - - name: Install ansible-base (${{ matrix.ansible }}) - run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check - - # Run the unit tests - - name: Run unit test - run: ansible-test units -v --color --docker --coverage - working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - - # ansible-test support producing code coverage date - - name: Generate coverage report - run: ansible-test coverage xml -v --requirements --group-by command --group-by version - working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - - # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME - - uses: codecov/codecov-action@v1 - with: - fail_ci_if_error: false diff --git a/ansible_collections/community/google/CHANGELOG.rst b/ansible_collections/community/google/CHANGELOG.rst deleted file mode 100644 index 8e33c3d75..000000000 --- a/ansible_collections/community/google/CHANGELOG.rst +++ /dev/null @@ -1,23 +0,0 @@ -============================== -community.google Release Notes -============================== - -.. contents:: Topics - - -v1.0.0 -====== - -Release Summary ---------------- - -This is the first stable release version of community.google. - -v0.1.0 -====== - -Release Summary ---------------- - -This is the first pre-release version of community.google after migrating from community.general. - diff --git a/ansible_collections/community/google/FILES.json b/ansible_collections/community/google/FILES.json deleted file mode 100644 index ff7fa022a..000000000 --- a/ansible_collections/community/google/FILES.json +++ /dev/null @@ -1,418 +0,0 @@ -{ - "files": [ - { - "name": ".", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "LICENSE", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986", - "format": 1 - }, - { - "name": "README.md", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "42d8f0ee470ee5760bc195e06fb35ba0d4cf1d2f0663ee1f866d997cb0159bdc", - "format": 1 - }, - { - "name": "scripts", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "scripts/inventory", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "scripts/inventory/gce.ini", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "925cfa8728fa00c71d851d13203fc6c9bfe87f96c0528029a0e909a602a0004c", - "format": 1 - }, - { - "name": "scripts/inventory/gce.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "e2fd208cca342e3e91d6711bd781bc2658715bf4201fa7d190be8d5ad35dc423", - "format": 1 - }, - { - "name": "tests", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "tests/unit", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "tests/unit/requirements.txt", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "b8bc332492969db761f788580155f30f1f57382a5f952cf1a9c920b627843d6f", - "format": 1 - }, - { - "name": "tests/unit/plugins", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "tests/unit/plugins/module_utils", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "tests/unit/plugins/module_utils/test_utils.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "19432d0cf6054eff46a50aa1cd9300a3e6bfe1ea118a9906096312daf7ee58aa", - "format": 1 - }, - { - "name": "tests/unit/plugins/module_utils/__init__.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "format": 1 - }, - { - "name": "tests/unit/plugins/module_utils/test_auth.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "7f0279cfc003046e5f8e05851e12a8ef2b55d3770dcf9d585376afc5e6477b67", - "format": 1 - }, - { - "name": "tests/unit/plugins/modules", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "tests/unit/plugins/modules/test_gce_tag.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "f66f884d5d9a5f4ae7b34ece88d47071e2598a29915b5950e8c957986c80ed10", - "format": 1 - }, - { - "name": "tests/unit/plugins/modules/__init__.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "format": 1 - }, - { - "name": "tests/unit/compat", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "tests/unit/compat/mock.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "0af958450cf6de3fbafe94b1111eae8ba5a8dbe1d785ffbb9df81f26e4946d99", - "format": 1 - }, - { - "name": "tests/unit/compat/unittest.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "5401a046e5ce71fa19b6d905abd0f9bdf816c0c635f7bdda6730b3ef06e67096", - "format": 1 - }, - { - "name": "tests/unit/compat/__init__.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "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": "513d336e4fec3987f8bb1946f889e91126eae6e661e64359ecec85e69c00f23c", - "format": 1 - }, - { - "name": "tests/sanity/ignore-2.9.txt", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "9914221531e94c75983a9624dcaf8914fc459ba8d0e261ce1462135f300b5617", - "format": 1 - }, - { - "name": "tests/sanity/ignore-2.10.txt", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "513d336e4fec3987f8bb1946f889e91126eae6e661e64359ecec85e69c00f23c", - "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/gcp.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "a85bdf5808a57d1860fd128e8bf93be1ec8009022d0a0e8fdc1af3732c2e8a9e", - "format": 1 - }, - { - "name": "plugins/module_utils/gce.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "282fc5e4c59582e8ea24ecb406ec7e5f14953a8d305002fbacda680811628d32", - "format": 1 - }, - { - "name": "plugins/doc_fragments", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "plugins/doc_fragments/_gcp.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "31581014875f54e52e1ece32efe3b581b175c7bec2d939571d892daf2302219d", - "format": 1 - }, - { - "name": "plugins/modules", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "plugins/modules/gc_storage.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "6390536b4af26888d58eea69390ed7590c637e83aa7fe278da7a776267517416", - "format": 1 - }, - { - "name": "plugins/modules/gce_net.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "7346ffb330fd4a4aca1937a1bdc9647e117c02e2dd701b72f1e46859107e0fb5", - "format": 1 - }, - { - "name": "plugins/modules/gcpubsub.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "04636fac7225cdd6a5ca34ea471ad19749c14afc17ec6642c58b66f688d9205a", - "format": 1 - }, - { - "name": "plugins/modules/gce_img.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "ac082df31a9c7bc156c0698c4175e5f593bad90f634f27b60653542de122e12d", - "format": 1 - }, - { - "name": "plugins/modules/gce_tag.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "fe08d5ac7faee9f7a8033b19ab4dccc5c76c46a4524c83a935dd89856bcd4c01", - "format": 1 - }, - { - "name": "plugins/modules/gce_instance_template.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "536e39871eba316c2264690c0ff26a2732b2be8f31215a03bd65d36dc7638480", - "format": 1 - }, - { - "name": "plugins/modules/gce_snapshot.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "db83ddf7998f915e702851bfa122817a0d99f2e60c257f1899f639b2186faa49", - "format": 1 - }, - { - "name": "plugins/modules/gce_pd.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "0d3cb555bf6b2095751eb4d5a984c9eab8bcbe6e6073c33749d15a80e976b3eb", - "format": 1 - }, - { - "name": "plugins/modules/gce_eip.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "cc2585ec223fdbac121d7ba6e711e5bdbb574102d63779d17b429b7630fa3771", - "format": 1 - }, - { - "name": "plugins/modules/gce_mig.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "4dd037f1fd3a58af68217a4d213cde47dd835730e09fe840ab2b0aa4a6df6a2e", - "format": 1 - }, - { - "name": "plugins/modules/gce_labels.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "419e6fc92f59717da395a4d70c778d18475940431517071e8c1ed051d3694bc4", - "format": 1 - }, - { - "name": "plugins/modules/gcpubsub_info.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "9ef79767bdeee07d33aefe342ab4298f5d38c091ed5c58f6872d550d3b0f1f92", - "format": 1 - }, - { - "name": "plugins/modules/gce_lb.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "54de796b84bf1308d370b1c015b0e968d48e0f821e7ae382a63b0f61ff4d87f2", - "format": 1 - }, - { - "name": "plugins/lookup", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "plugins/lookup/gcp_storage_file.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "969de4dedeb2b6be2f3fe6c6072bdc4481f4b2e427e72eac03eb6c5359634a2b", - "format": 1 - }, - { - "name": "changelogs", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "changelogs/fragments", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "changelogs/fragments/.keep", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "format": 1 - }, - { - "name": "changelogs/changelog.yaml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "48a6a9e80e1629dbccaaa278f83493de9b19ca26a4334e7b114adc86f014332f", - "format": 1 - }, - { - "name": "changelogs/config.yaml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "d18c3fb08d51d89b581031b5079624a86306457a85ba48938c27152e38881fe9", - "format": 1 - }, - { - "name": "meta", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "meta/runtime.yml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "3b0634ab394e8ced5d60b2532bfa8ed14cec4c7d4739c9e0277c760c5543c0e1", - "format": 1 - }, - { - "name": "CHANGELOG.rst", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "1f5a70b361b5f1a356efddb0abde59de7d80197b7302859dd7155c7ad4e43b64", - "format": 1 - }, - { - "name": ".github", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": ".github/workflows", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": ".github/workflows/ansible-test.yml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "57d072bb0337cf78a96fd4e2ee58f0e8d57e21da29bd9642adfbaed25556549c", - "format": 1 - } - ], - "format": 1 -}
\ No newline at end of file diff --git a/ansible_collections/community/google/LICENSE b/ansible_collections/community/google/LICENSE deleted file mode 100644 index f288702d2..000000000 --- a/ansible_collections/community/google/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - 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/ansible_collections/community/google/MANIFEST.json b/ansible_collections/community/google/MANIFEST.json deleted file mode 100644 index aab8d0781..000000000 --- a/ansible_collections/community/google/MANIFEST.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "collection_info": { - "namespace": "community", - "name": "google", - "version": "1.0.0", - "authors": [ - "Google" - ], - "readme": "README.md", - "tags": [ - "google", - "gce", - "gcp" - ], - "description": "Google Cloud Platform community collection", - "license": [], - "license_file": "LICENSE", - "dependencies": {}, - "repository": "https://github.com/ansible-collections/community.google", - "documentation": null, - "homepage": "https://github.com/ansible-collections/community.google", - "issues": "https://github.com/ansible-collections/community.google/issues" - }, - "file_manifest_file": { - "name": "FILES.json", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "6dfe9e97ce5637373663dd885af32c1deb5c10352c3440296526b3b4fe5d401c", - "format": 1 - }, - "format": 1 -}
\ No newline at end of file diff --git a/ansible_collections/community/google/README.md b/ansible_collections/community/google/README.md deleted file mode 100644 index 27122af06..000000000 --- a/ansible_collections/community/google/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# community.google Ansible collection -[![CI](https://github.com/ansible-collections/community.google/workflows/CI/badge.svg?event=push)](https://github.com/ansible-collections/community.google/actions) [![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.google)](https://codecov.io/gh/ansible-collections/community.google) - -## External requirements - -- boto -- libcloud -- google-auth - -## Included content - -See: https://docs.ansible.com/ansible/latest/collections/community/google/ - -## Using this collection - -See [Ansible Using collections](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html) for more details. - -## Release notes - -See the [changelog](https://github.com/ansible-collections/community.google/tree/main/CHANGELOG.rst). - -## More information - -<!-- List out where the user can find additional information, such as working group meeting times, slack/IRC channels, or documentation for the product this collection automates. At a minimum, link to: --> - -- [Ansible Collection overview](https://github.com/ansible-collections/overview) -- [Ansible User guide](https://docs.ansible.com/ansible/latest/user_guide/index.html) -- [Ansible Developer guide](https://docs.ansible.com/ansible/latest/dev_guide/index.html) -- [Ansible Collections Checklist](https://github.com/ansible-collections/overview/blob/master/collection_requirements.rst) -- [Ansible Community code of conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html) -- [The Bullhorn (the Ansible Contributor newsletter)](https://us19.campaign-archive.com/home/?u=56d874e027110e35dea0e03c1&id=d6635f5420) -- [Changes impacting Contributors](https://github.com/ansible-collections/overview/issues/45) - -## Licensing - -GNU General Public License v3.0 or later. - -See [LICENSE](https://www.gnu.org/licenses/gpl-3.0.txt) to see the full text. diff --git a/ansible_collections/community/google/changelogs/changelog.yaml b/ansible_collections/community/google/changelogs/changelog.yaml deleted file mode 100644 index 13fbdc44d..000000000 --- a/ansible_collections/community/google/changelogs/changelog.yaml +++ /dev/null @@ -1,17 +0,0 @@ -ancestor: null -releases: - 0.1.0: - changes: - release_summary: 'This is the first pre-release version of community.google - after migrating from community.general. - - ' - fragments: - - 0.1.0.yml - release_date: '2020-11-30' - 1.0.0: - changes: - release_summary: This is the first stable release version of community.google. - fragments: - - 1.0.0.yaml - release_date: '2020-12-22' diff --git a/ansible_collections/community/google/changelogs/config.yaml b/ansible_collections/community/google/changelogs/config.yaml deleted file mode 100644 index 038e5eb48..000000000 --- a/ansible_collections/community/google/changelogs/config.yaml +++ /dev/null @@ -1,29 +0,0 @@ -changelog_filename_template: ../CHANGELOG.rst -changelog_filename_version_depth: 0 -changes_file: changelog.yaml -changes_format: combined -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: community.google -trivial_section_name: trivial diff --git a/ansible_collections/community/google/changelogs/fragments/.keep b/ansible_collections/community/google/changelogs/fragments/.keep deleted file mode 100644 index e69de29bb..000000000 --- a/ansible_collections/community/google/changelogs/fragments/.keep +++ /dev/null diff --git a/ansible_collections/community/google/meta/runtime.yml b/ansible_collections/community/google/meta/runtime.yml deleted file mode 100644 index a9d40b8b5..000000000 --- a/ansible_collections/community/google/meta/runtime.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -requires_ansible: '>=2.9.10' -plugin_routing: - modules: - gcpubsub_facts: - redirect: community.google.gcpubsub_info - deprecation: - removal_version: 2.0.0 - warning_text: Use community.google.gcpubsub_info instead. diff --git a/ansible_collections/community/google/plugins/doc_fragments/_gcp.py b/ansible_collections/community/google/plugins/doc_fragments/_gcp.py deleted file mode 100644 index 068725436..000000000 --- a/ansible_collections/community/google/plugins/doc_fragments/_gcp.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Google Inc. -# 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 - - -class ModuleDocFragment(object): - # GCP doc fragment. - DOCUMENTATION = r''' -options: - project: - description: - - The Google Cloud Platform project to use. - type: str - auth_kind: - description: - - The type of credential used. - type: str - required: true - choices: [ application, machineaccount, serviceaccount ] - service_account_contents: - description: - - The contents of a Service Account JSON file, either in a dictionary or as a JSON string that represents it. - type: jsonarg - service_account_file: - description: - - The path of a Service Account JSON file if serviceaccount is selected as type. - type: path - service_account_email: - description: - - An optional service account email address if machineaccount is selected - and the user does not wish to use the default email. - type: str - scopes: - description: - - Array of scopes to be used. - type: list - elements: str - env_type: - description: - - Specifies which Ansible environment you're running this module within. - - This should not be set unless you know what you're doing. - - This only alters the User Agent string for any API requests. - type: str -notes: - - for authentication, you can set service_account_file using the - c(gcp_service_account_file) env variable. - - for authentication, you can set service_account_contents using the - c(GCP_SERVICE_ACCOUNT_CONTENTS) env variable. - - For authentication, you can set service_account_email using the - C(GCP_SERVICE_ACCOUNT_EMAIL) env variable. - - For authentication, you can set auth_kind using the C(GCP_AUTH_KIND) env - variable. - - For authentication, you can set scopes using the C(GCP_SCOPES) env variable. - - Environment variables values will only be used if the playbook values are - not set. - - The I(service_account_email) and I(service_account_file) options are - mutually exclusive. -''' diff --git a/ansible_collections/community/google/plugins/lookup/gcp_storage_file.py b/ansible_collections/community/google/plugins/lookup/gcp_storage_file.py deleted file mode 100644 index ae58c5c59..000000000 --- a/ansible_collections/community/google/plugins/lookup/gcp_storage_file.py +++ /dev/null @@ -1,156 +0,0 @@ -# (c) 2019, Eric Anderson <eric.sysmin@gmail.com> -# 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 = ''' -lookup: gcp_storage_file -description: - - This lookup returns the contents from a file residing on Google Cloud Storage -short_description: Return GC Storage content -author: Eric Anderson (!UNKNOWN) <eanderson@avinetworks.com> -requirements: - - python >= 2.6 - - requests >= 2.18.4 - - google-auth >= 1.3.0 -options: - src: - description: - - Source location of file (may be local machine or cloud depending on action). - required: false - bucket: - description: - - The name of the bucket. - required: false -extends_documentation_fragment: -- community.google._gcp - -''' - -EXAMPLES = ''' -- ansible.builtin.debug: - msg: | - the value of foo.txt is {{ lookup('community.google.gcp_storage_file', - bucket='gcp-bucket', src='mydir/foo.txt', project='project-name', - auth_kind='serviceaccount', service_account_file='/tmp/myserviceaccountfile.json') }} -''' - -RETURN = ''' -_raw: - description: - - base64 encoded file content - type: list - elements: str -''' - -import base64 -import json -import mimetypes -import os -from ansible.errors import AnsibleError -from ansible.plugins.lookup import LookupBase -from ansible.utils.display import Display - -try: - import requests - HAS_REQUESTS = True -except ImportError: - HAS_REQUESTS = False - -try: - from ansible_collections.google.cloud.plugins.module_utils.gcp_utils import navigate_hash, GcpSession - HAS_GOOGLE_CLOUD_COLLECTION = True -except ImportError: - HAS_GOOGLE_CLOUD_COLLECTION = False - - -display = Display() - - -class GcpMockModule(object): - def __init__(self, params): - self.params = params - - def fail_json(self, *args, **kwargs): - raise AnsibleError(kwargs['msg']) - - def raise_for_status(self, response): - try: - response.raise_for_status() - except getattr(requests.exceptions, 'RequestException'): - self.fail_json(msg="GCP returned error: %s" % response.json()) - - -class GcpFileLookup(): - def get_file_contents(self, module): - auth = GcpSession(module, 'storage') - data = auth.get(self.media_link(module)) - return base64.b64encode(data.content.rstrip()) - - def fetch_resource(self, module, link, allow_not_found=True): - auth = GcpSession(module, 'storage') - return self.return_if_object(module, auth.get(link), allow_not_found) - - def self_link(self, module): - return "https://www.googleapis.com/storage/v1/b/{bucket}/o/{src}".format(**module.params) - - def media_link(self, module): - return "https://www.googleapis.com/storage/v1/b/{bucket}/o/{src}?alt=media".format(**module.params) - - def return_if_object(self, module, response, allow_not_found=False): - # If not found, return nothing. - if allow_not_found and response.status_code == 404: - return None - # If no content, return nothing. - if response.status_code == 204: - return None - try: - module.raise_for_status(response) - result = response.json() - except getattr(json.decoder, 'JSONDecodeError', ValueError) as inst: - raise AnsibleError("Invalid JSON response with error: %s" % inst) - if navigate_hash(result, ['error', 'errors']): - raise AnsibleError(navigate_hash(result, ['error', 'errors'])) - return result - - def object_headers(self, module): - return { - "name": module.params['src'], - "Content-Type": mimetypes.guess_type(module.params['src'])[0], - "Content-Length": str(os.path.getsize(module.params['src'])), - } - - def run(self, terms, variables=None, **kwargs): - params = { - 'bucket': kwargs.get('bucket', None), - 'src': kwargs.get('src', None), - 'projects': kwargs.get('projects', None), - 'scopes': kwargs.get('scopes', None), - 'zones': kwargs.get('zones', None), - 'auth_kind': kwargs.get('auth_kind', None), - 'service_account_file': kwargs.get('service_account_file', None), - 'service_account_email': kwargs.get('service_account_email', None), - } - - if not params['scopes']: - params['scopes'] = ['https://www.googleapis.com/auth/devstorage.full_control'] - - fake_module = GcpMockModule(params) - - # Check if files exist. - remote_object = self.fetch_resource(fake_module, self.self_link(fake_module)) - if not remote_object: - raise AnsibleError("File does not exist in bucket") - - result = self.get_file_contents(fake_module) - return [result] - - -class LookupModule(LookupBase): - def run(self, terms, variables=None, **kwargs): - if not HAS_GOOGLE_CLOUD_COLLECTION: - raise AnsibleError("community.google.gcp_storage_file needs a supported version of the google.cloud collection installed") - if not HAS_REQUESTS: - raise AnsibleError("community.google.gcp_storage_file needs requests installed. Use `pip install requests` to install it") - return GcpFileLookup().run(terms, variables=variables, **kwargs) diff --git a/ansible_collections/community/google/plugins/module_utils/gce.py b/ansible_collections/community/google/plugins/module_utils/gce.py deleted file mode 100644 index 7698e3c5e..000000000 --- a/ansible_collections/community/google/plugins/module_utils/gce.py +++ /dev/null @@ -1,39 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# Copyright (c), Franck Cuny <franck.cuny@gmail.com>, 2014 -# -# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -try: - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - HAS_LIBCLOUD_BASE = True -except ImportError: - HAS_LIBCLOUD_BASE = False - -from ansible_collections.community.google.plugins.module_utils.gcp import gcp_connect -from ansible_collections.community.google.plugins.module_utils.gcp import unexpected_error_msg as gcp_error - -USER_AGENT_PRODUCT = "Ansible-gce" -USER_AGENT_VERSION = "v1" - - -def gce_connect(module, provider=None): - """Return a GCP connection for Google Compute Engine.""" - if not HAS_LIBCLOUD_BASE: - module.fail_json(msg='libcloud must be installed to use this module') - provider = provider or Provider.GCE - - return gcp_connect(module, provider, get_driver, USER_AGENT_PRODUCT, USER_AGENT_VERSION) - - -def unexpected_error_msg(error): - """Create an error string based on passed in error.""" - return gcp_error(error) diff --git a/ansible_collections/community/google/plugins/module_utils/gcp.py b/ansible_collections/community/google/plugins/module_utils/gcp.py deleted file mode 100644 index a034f3b6c..000000000 --- a/ansible_collections/community/google/plugins/module_utils/gcp.py +++ /dev/null @@ -1,799 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# Copyright (c), Franck Cuny <franck.cuny@gmail.com>, 2014 -# -# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import os -import time -import traceback -from distutils.version import LooseVersion - -# libcloud -try: - import libcloud - HAS_LIBCLOUD_BASE = True -except ImportError: - HAS_LIBCLOUD_BASE = False - -# google-auth -try: - import google.auth - from google.oauth2 import service_account - HAS_GOOGLE_AUTH = True -except ImportError: - HAS_GOOGLE_AUTH = False - -# google-python-api -try: - import google_auth_httplib2 - from httplib2 import Http - from googleapiclient.http import set_user_agent - from googleapiclient.errors import HttpError - from apiclient.discovery import build - HAS_GOOGLE_API_LIB = True -except ImportError: - HAS_GOOGLE_API_LIB = False - - -import ansible.module_utils.six.moves.urllib.parse as urlparse - -GCP_DEFAULT_SCOPES = ['https://www.googleapis.com/auth/cloud-platform'] - - -def _get_gcp_ansible_credentials(module): - """Helper to fetch creds from AnsibleModule object.""" - service_account_email = module.params.get('service_account_email', None) - # Note: pem_file is discouraged and will be deprecated - credentials_file = module.params.get('pem_file', None) or module.params.get( - 'credentials_file', None) - project_id = module.params.get('project_id', None) - - return (service_account_email, credentials_file, project_id) - - -def _get_gcp_environ_var(var_name, default_value): - """Wrapper around os.environ.get call.""" - return os.environ.get( - var_name, default_value) - - -def _get_gcp_environment_credentials(service_account_email, credentials_file, project_id): - """Helper to look in environment variables for credentials.""" - # If any of the values are not given as parameters, check the appropriate - # environment variables. - if not service_account_email: - service_account_email = _get_gcp_environ_var('GCE_EMAIL', None) - if not credentials_file: - credentials_file = _get_gcp_environ_var( - 'GCE_CREDENTIALS_FILE_PATH', None) or _get_gcp_environ_var( - 'GOOGLE_APPLICATION_CREDENTIALS', None) or _get_gcp_environ_var( - 'GCE_PEM_FILE_PATH', None) - if not project_id: - project_id = _get_gcp_environ_var('GCE_PROJECT', None) or _get_gcp_environ_var( - 'GOOGLE_CLOUD_PROJECT', None) - return (service_account_email, credentials_file, project_id) - - -def _get_gcp_credentials(module, require_valid_json=True, check_libcloud=False): - """ - Obtain GCP credentials by trying various methods. - - There are 3 ways to specify GCP credentials: - 1. Specify via Ansible module parameters (recommended). - 2. Specify via environment variables. Two sets of env vars are available: - a) GOOGLE_CLOUD_PROJECT, GOOGLE_CREDENTIALS_APPLICATION (preferred) - b) GCE_PROJECT, GCE_CREDENTIAL_FILE_PATH, GCE_EMAIL (legacy, not recommended; req'd if - using p12 key) - 3. Specify via libcloud secrets.py file (deprecated). - - There are 3 helper functions to assist in the above. - - Regardless of method, the user also has the option of specifying a JSON - file or a p12 file as the credentials file. JSON is strongly recommended and - p12 will be removed in the future. - - Additionally, flags may be set to require valid json and check the libcloud - version. - - AnsibleModule.fail_json is called only if the project_id cannot be found. - - :param module: initialized Ansible module object - :type module: `class AnsibleModule` - - :param require_valid_json: If true, require credentials to be valid JSON. Default is True. - :type require_valid_json: ``bool`` - - :params check_libcloud: If true, check the libcloud version available to see if - JSON creds are supported. - :type check_libcloud: ``bool`` - - :return: {'service_account_email': service_account_email, - 'credentials_file': credentials_file, - 'project_id': project_id} - :rtype: ``dict`` - """ - (service_account_email, - credentials_file, - project_id) = _get_gcp_ansible_credentials(module) - - # If any of the values are not given as parameters, check the appropriate - # environment variables. - (service_account_email, - credentials_file, - project_id) = _get_gcp_environment_credentials(service_account_email, - credentials_file, project_id) - - if credentials_file is None or project_id is None or service_account_email is None: - if check_libcloud is True: - if project_id is None: - # TODO(supertom): this message is legacy and integration tests - # depend on it. - module.fail_json(msg='Missing GCE connection parameters in libcloud ' - 'secrets file.') - else: - if project_id is None: - module.fail_json(msg=('GCP connection error: unable to determine project (%s) or ' - 'credentials file (%s)' % (project_id, credentials_file))) - # Set these fields to empty strings if they are None - # consumers of this will make the distinction between an empty string - # and None. - if credentials_file is None: - credentials_file = '' - if service_account_email is None: - service_account_email = '' - - # ensure the credentials file is found and is in the proper format. - if credentials_file: - _validate_credentials_file(module, credentials_file, - require_valid_json=require_valid_json, - check_libcloud=check_libcloud) - - return {'service_account_email': service_account_email, - 'credentials_file': credentials_file, - 'project_id': project_id} - - -def _validate_credentials_file(module, credentials_file, require_valid_json=True, check_libcloud=False): - """ - Check for valid credentials file. - - Optionally check for JSON format and if libcloud supports JSON. - - :param module: initialized Ansible module object - :type module: `class AnsibleModule` - - :param credentials_file: path to file on disk - :type credentials_file: ``str``. Complete path to file on disk. - - :param require_valid_json: This argument is ignored as of Ansible 2.7. - :type require_valid_json: ``bool`` - - :params check_libcloud: If true, check the libcloud version available to see if - JSON creds are supported. - :type check_libcloud: ``bool`` - - :returns: True - :rtype: ``bool`` - """ - try: - # Try to read credentials as JSON - with open(credentials_file) as credentials: - json.loads(credentials.read()) - # If the credentials are proper JSON and we do not have the minimum - # required libcloud version, bail out and return a descriptive - # error - if check_libcloud and LooseVersion(libcloud.__version__) < '0.17.0': - module.fail_json(msg='Using JSON credentials but libcloud minimum version not met. ' - 'Upgrade to libcloud>=0.17.0.') - return True - except IOError as e: - module.fail_json(msg='GCP Credentials File %s not found.' % - credentials_file, changed=False) - return False - except ValueError as e: - module.fail_json( - msg='Non-JSON credentials file provided. Please generate a new JSON key from the Google Cloud console', - changed=False) - - -def gcp_connect(module, provider, get_driver, user_agent_product, user_agent_version): - """Return a Google libcloud driver connection.""" - if not HAS_LIBCLOUD_BASE: - module.fail_json(msg='libcloud must be installed to use this module') - - creds = _get_gcp_credentials(module, - require_valid_json=False, - check_libcloud=True) - try: - gcp = get_driver(provider)(creds['service_account_email'], creds['credentials_file'], - datacenter=module.params.get('zone', None), - project=creds['project_id']) - gcp.connection.user_agent_append("%s/%s" % ( - user_agent_product, user_agent_version)) - except (RuntimeError, ValueError) as e: - module.fail_json(msg=str(e), changed=False) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - return gcp - - -def get_google_cloud_credentials(module, scopes=None): - """ - Get credentials object for use with Google Cloud client. - - Attempts to obtain credentials by calling _get_gcp_credentials. If those are - not present will attempt to connect via Application Default Credentials. - - To connect via libcloud, don't use this function, use gcp_connect instead. For - Google Python API Client, see get_google_api_auth for how to connect. - - For more information on Google's client library options for Python, see: - U(https://cloud.google.com/apis/docs/client-libraries-explained#google_api_client_libraries) - - Google Cloud example: - creds, params = get_google_cloud_credentials(module, scopes, user_agent_product, user_agent_version) - pubsub_client = pubsub.Client(project=params['project_id'], credentials=creds) - pubsub_client.user_agent = 'ansible-pubsub-0.1' - ... - - :param module: initialized Ansible module object - :type module: `class AnsibleModule` - - :param scopes: list of scopes - :type module: ``list`` of URIs - - :returns: A tuple containing (google authorized) credentials object and - params dict {'service_account_email': '...', 'credentials_file': '...', 'project_id': ...} - :rtype: ``tuple`` - """ - scopes = [] if scopes is None else scopes - - if not HAS_GOOGLE_AUTH: - module.fail_json(msg='Please install google-auth.') - - conn_params = _get_gcp_credentials(module, - require_valid_json=True, - check_libcloud=False) - try: - if conn_params['credentials_file']: - credentials = service_account.Credentials.from_service_account_file( - conn_params['credentials_file']) - if scopes: - credentials = credentials.with_scopes(scopes) - else: - (credentials, project_id) = google.auth.default( - scopes=scopes) - if project_id is not None: - conn_params['project_id'] = project_id - - return (credentials, conn_params) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - return (None, None) - - -def get_google_api_auth(module, scopes=None, user_agent_product='ansible-python-api', user_agent_version='NA'): - """ - Authentication for use with google-python-api-client. - - Function calls get_google_cloud_credentials, which attempts to assemble the credentials - from various locations. Next it attempts to authenticate with Google. - - This function returns an httplib2 (compatible) object that can be provided to the Google Python API client. - - For libcloud, don't use this function, use gcp_connect instead. For Google Cloud, See - get_google_cloud_credentials for how to connect. - - For more information on Google's client library options for Python, see: - U(https://cloud.google.com/apis/docs/client-libraries-explained#google_api_client_libraries) - - Google API example: - http_auth, conn_params = get_google_api_auth(module, scopes, user_agent_product, user_agent_version) - service = build('myservice', 'v1', http=http_auth) - ... - - :param module: initialized Ansible module object - :type module: `class AnsibleModule` - - :param scopes: list of scopes - :type scopes: ``list`` of URIs - - :param user_agent_product: User agent product. eg: 'ansible-python-api' - :type user_agent_product: ``str`` - - :param user_agent_version: Version string to append to product. eg: 'NA' or '0.1' - :type user_agent_version: ``str`` - - :returns: A tuple containing (google authorized) httplib2 request object and a - params dict {'service_account_email': '...', 'credentials_file': '...', 'project_id': ...} - :rtype: ``tuple`` - """ - scopes = [] if scopes is None else scopes - - if not HAS_GOOGLE_API_LIB: - module.fail_json(msg="Please install google-api-python-client library") - if not scopes: - scopes = GCP_DEFAULT_SCOPES - try: - (credentials, conn_params) = get_google_cloud_credentials(module, scopes) - http = set_user_agent(Http(), '%s-%s' % - (user_agent_product, user_agent_version)) - http_auth = google_auth_httplib2.AuthorizedHttp(credentials, http=http) - - return (http_auth, conn_params) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - return (None, None) - - -def get_google_api_client(module, service, user_agent_product, user_agent_version, - scopes=None, api_version='v1'): - """ - Get the discovery-based python client. Use when a cloud client is not available. - - client = get_google_api_client(module, 'compute', user_agent_product=USER_AGENT_PRODUCT, - user_agent_version=USER_AGENT_VERSION) - - :returns: A tuple containing the authorized client to the specified service and a - params dict {'service_account_email': '...', 'credentials_file': '...', 'project_id': ...} - :rtype: ``tuple`` - """ - if not scopes: - scopes = GCP_DEFAULT_SCOPES - - http_auth, conn_params = get_google_api_auth(module, scopes=scopes, - user_agent_product=user_agent_product, - user_agent_version=user_agent_version) - client = build(service, api_version, http=http_auth) - - return (client, conn_params) - - -def check_min_pkg_version(pkg_name, minimum_version): - """Minimum required version is >= installed version.""" - from pkg_resources import get_distribution - try: - installed_version = get_distribution(pkg_name).version - return LooseVersion(installed_version) >= minimum_version - except Exception as e: - return False - - -def unexpected_error_msg(error): - """Create an error string based on passed in error.""" - return 'Unexpected response: (%s). Detail: %s' % (str(error), traceback.format_exc()) - - -def get_valid_location(module, driver, location, location_type='zone'): - if location_type == 'zone': - l = driver.ex_get_zone(location) - else: - l = driver.ex_get_region(location) - if l is None: - link = 'https://cloud.google.com/compute/docs/regions-zones/regions-zones#available' - module.fail_json(msg=('%s %s is invalid. Please see the list of ' - 'available %s at %s' % ( - location_type, location, location_type, link)), - changed=False) - return l - - -def check_params(params, field_list): - """ - Helper to validate params. - - Use this in function definitions if they require specific fields - to be present. - - :param params: structure that contains the fields - :type params: ``dict`` - - :param field_list: list of dict representing the fields - [{'name': str, 'required': True/False', 'type': cls}] - :type field_list: ``list`` of ``dict`` - - :return True or raises ValueError - :rtype: ``bool`` or `class:ValueError` - """ - for d in field_list: - if not d['name'] in params: - if 'required' in d and d['required'] is True: - raise ValueError(("%s is required and must be of type: %s" % - (d['name'], str(d['type'])))) - else: - if not isinstance(params[d['name']], d['type']): - raise ValueError(("%s must be of type: %s. %s (%s) provided." % ( - d['name'], str(d['type']), params[d['name']], - type(params[d['name']])))) - if 'values' in d: - if params[d['name']] not in d['values']: - raise ValueError(("%s must be one of: %s" % ( - d['name'], ','.join(d['values'])))) - if isinstance(params[d['name']], int): - if 'min' in d: - if params[d['name']] < d['min']: - raise ValueError(("%s must be greater than or equal to: %s" % ( - d['name'], d['min']))) - if 'max' in d: - if params[d['name']] > d['max']: - raise ValueError("%s must be less than or equal to: %s" % ( - d['name'], d['max'])) - return True - - -class GCPUtils(object): - """ - Helper utilities for GCP. - """ - - @staticmethod - def underscore_to_camel(txt): - return txt.split('_')[0] + ''.join(x.capitalize() or '_' for x in txt.split('_')[1:]) - - @staticmethod - def remove_non_gcp_params(params): - """ - Remove params if found. - """ - params_to_remove = ['state'] - for p in params_to_remove: - if p in params: - del params[p] - - return params - - @staticmethod - def params_to_gcp_dict(params, resource_name=None): - """ - Recursively convert ansible params to GCP Params. - - Keys are converted from snake to camelCase - ex: default_service to defaultService - - Handles lists, dicts and strings - - special provision for the resource name - """ - if not isinstance(params, dict): - return params - gcp_dict = {} - params = GCPUtils.remove_non_gcp_params(params) - for k, v in params.items(): - gcp_key = GCPUtils.underscore_to_camel(k) - if isinstance(v, dict): - retval = GCPUtils.params_to_gcp_dict(v) - gcp_dict[gcp_key] = retval - elif isinstance(v, list): - gcp_dict[gcp_key] = [GCPUtils.params_to_gcp_dict(x) for x in v] - else: - if resource_name and k == resource_name: - gcp_dict['name'] = v - else: - gcp_dict[gcp_key] = v - return gcp_dict - - @staticmethod - def execute_api_client_req(req, client=None, raw=True, - operation_timeout=180, poll_interval=5, - raise_404=True): - """ - General python api client interaction function. - - For use with google-api-python-client, or clients created - with get_google_api_client function - Not for use with Google Cloud client libraries - - For long-running operations, we make an immediate query and then - sleep poll_interval before re-querying. After the request is done - we rebuild the request with a get method and return the result. - - """ - try: - resp = req.execute() - - if not resp: - return None - - if raw: - return resp - - if resp['kind'] == 'compute#operation': - resp = GCPUtils.execute_api_client_operation_req(req, resp, - client, - operation_timeout, - poll_interval) - - if 'items' in resp: - return resp['items'] - - return resp - except HttpError as h: - # Note: 404s can be generated (incorrectly) for dependent - # resources not existing. We let the caller determine if - # they want 404s raised for their invocation. - if h.resp.status == 404 and not raise_404: - return None - else: - raise - except Exception: - raise - - @staticmethod - def execute_api_client_operation_req(orig_req, op_resp, client, - operation_timeout=180, poll_interval=5): - """ - Poll an operation for a result. - """ - parsed_url = GCPUtils.parse_gcp_url(orig_req.uri) - project_id = parsed_url['project'] - resource_name = GCPUtils.get_gcp_resource_from_methodId( - orig_req.methodId) - resource = GCPUtils.build_resource_from_name(client, resource_name) - - start_time = time.time() - - complete = False - attempts = 1 - while not complete: - if start_time + operation_timeout >= time.time(): - op_req = client.globalOperations().get( - project=project_id, operation=op_resp['name']) - op_resp = op_req.execute() - if op_resp['status'] != 'DONE': - time.sleep(poll_interval) - attempts += 1 - else: - complete = True - if op_resp['operationType'] == 'delete': - # don't wait for the delete - return True - elif op_resp['operationType'] in ['insert', 'update', 'patch']: - # TODO(supertom): Isolate 'build-new-request' stuff. - resource_name_singular = GCPUtils.get_entity_name_from_resource_name( - resource_name) - if op_resp['operationType'] == 'insert' or 'entity_name' not in parsed_url: - parsed_url['entity_name'] = GCPUtils.parse_gcp_url(op_resp['targetLink'])[ - 'entity_name'] - args = {'project': project_id, - resource_name_singular: parsed_url['entity_name']} - new_req = resource.get(**args) - resp = new_req.execute() - return resp - else: - # assuming multiple entities, do a list call. - new_req = resource.list(project=project_id) - resp = new_req.execute() - return resp - else: - # operation didn't complete on time. - raise GCPOperationTimeoutError("Operation timed out: %s" % ( - op_resp['targetLink'])) - - @staticmethod - def build_resource_from_name(client, resource_name): - try: - method = getattr(client, resource_name) - return method() - except AttributeError: - raise NotImplementedError('%s is not an attribute of %s' % (resource_name, - client)) - - @staticmethod - def get_gcp_resource_from_methodId(methodId): - try: - parts = methodId.split('.') - if len(parts) != 3: - return None - else: - return parts[1] - except AttributeError: - return None - - @staticmethod - def get_entity_name_from_resource_name(resource_name): - if not resource_name: - return None - - try: - # Chop off global or region prefixes - if resource_name.startswith('global'): - resource_name = resource_name.replace('global', '') - elif resource_name.startswith('regional'): - resource_name = resource_name.replace('region', '') - - # ensure we have a lower case first letter - resource_name = resource_name[0].lower() + resource_name[1:] - - if resource_name[-3:] == 'ies': - return resource_name.replace( - resource_name[-3:], 'y') - if resource_name[-1] == 's': - return resource_name[:-1] - - return resource_name - - except AttributeError: - return None - - @staticmethod - def parse_gcp_url(url): - """ - Parse GCP urls and return dict of parts. - - Supported URL structures: - /SERVICE/VERSION/'projects'/PROJECT_ID/RESOURCE - /SERVICE/VERSION/'projects'/PROJECT_ID/RESOURCE/ENTITY_NAME - /SERVICE/VERSION/'projects'/PROJECT_ID/RESOURCE/ENTITY_NAME/METHOD_NAME - /SERVICE/VERSION/'projects'/PROJECT_ID/'global'/RESOURCE - /SERVICE/VERSION/'projects'/PROJECT_ID/'global'/RESOURCE/ENTITY_NAME - /SERVICE/VERSION/'projects'/PROJECT_ID/'global'/RESOURCE/ENTITY_NAME/METHOD_NAME - /SERVICE/VERSION/'projects'/PROJECT_ID/LOCATION_TYPE/LOCATION/RESOURCE - /SERVICE/VERSION/'projects'/PROJECT_ID/LOCATION_TYPE/LOCATION/RESOURCE/ENTITY_NAME - /SERVICE/VERSION/'projects'/PROJECT_ID/LOCATION_TYPE/LOCATION/RESOURCE/ENTITY_NAME/METHOD_NAME - - :param url: GCP-generated URL, such as a selflink or resource location. - :type url: ``str`` - - :return: dictionary of parts. Includes stanard components of urlparse, plus - GCP-specific 'service', 'api_version', 'project' and - 'resource_name' keys. Optionally, 'zone', 'region', 'entity_name' - and 'method_name', if applicable. - :rtype: ``dict`` - """ - - p = urlparse.urlparse(url) - if not p: - return None - else: - # we add extra items such as - # zone, region and resource_name - url_parts = {} - url_parts['scheme'] = p.scheme - url_parts['host'] = p.netloc - url_parts['path'] = p.path - if p.path.find('/') == 0: - url_parts['path'] = p.path[1:] - url_parts['params'] = p.params - url_parts['fragment'] = p.fragment - url_parts['query'] = p.query - url_parts['project'] = None - url_parts['service'] = None - url_parts['api_version'] = None - - path_parts = url_parts['path'].split('/') - url_parts['service'] = path_parts[0] - url_parts['api_version'] = path_parts[1] - if path_parts[2] == 'projects': - url_parts['project'] = path_parts[3] - else: - # invalid URL - raise GCPInvalidURLError('unable to parse: %s' % url) - - if 'global' in path_parts: - url_parts['global'] = True - idx = path_parts.index('global') - if len(path_parts) - idx == 4: - # we have a resource, entity and method_name - url_parts['resource_name'] = path_parts[idx + 1] - url_parts['entity_name'] = path_parts[idx + 2] - url_parts['method_name'] = path_parts[idx + 3] - - if len(path_parts) - idx == 3: - # we have a resource and entity - url_parts['resource_name'] = path_parts[idx + 1] - url_parts['entity_name'] = path_parts[idx + 2] - - if len(path_parts) - idx == 2: - url_parts['resource_name'] = path_parts[idx + 1] - - if len(path_parts) - idx < 2: - # invalid URL - raise GCPInvalidURLError('unable to parse: %s' % url) - - elif 'regions' in path_parts or 'zones' in path_parts: - idx = -1 - if 'regions' in path_parts: - idx = path_parts.index('regions') - url_parts['region'] = path_parts[idx + 1] - else: - idx = path_parts.index('zones') - url_parts['zone'] = path_parts[idx + 1] - - if len(path_parts) - idx == 5: - # we have a resource, entity and method_name - url_parts['resource_name'] = path_parts[idx + 2] - url_parts['entity_name'] = path_parts[idx + 3] - url_parts['method_name'] = path_parts[idx + 4] - - if len(path_parts) - idx == 4: - # we have a resource and entity - url_parts['resource_name'] = path_parts[idx + 2] - url_parts['entity_name'] = path_parts[idx + 3] - - if len(path_parts) - idx == 3: - url_parts['resource_name'] = path_parts[idx + 2] - - if len(path_parts) - idx < 3: - # invalid URL - raise GCPInvalidURLError('unable to parse: %s' % url) - - else: - # no location in URL. - idx = path_parts.index('projects') - if len(path_parts) - idx == 5: - # we have a resource, entity and method_name - url_parts['resource_name'] = path_parts[idx + 2] - url_parts['entity_name'] = path_parts[idx + 3] - url_parts['method_name'] = path_parts[idx + 4] - - if len(path_parts) - idx == 4: - # we have a resource and entity - url_parts['resource_name'] = path_parts[idx + 2] - url_parts['entity_name'] = path_parts[idx + 3] - - if len(path_parts) - idx == 3: - url_parts['resource_name'] = path_parts[idx + 2] - - if len(path_parts) - idx < 3: - # invalid URL - raise GCPInvalidURLError('unable to parse: %s' % url) - - return url_parts - - @staticmethod - def build_googleapi_url(project, api_version='v1', service='compute'): - return 'https://www.googleapis.com/%s/%s/projects/%s' % (service, api_version, project) - - @staticmethod - def filter_gcp_fields(params, excluded_fields=None): - new_params = {} - if not excluded_fields: - excluded_fields = ['creationTimestamp', 'id', 'kind', - 'selfLink', 'fingerprint', 'description'] - - if isinstance(params, list): - new_params = [GCPUtils.filter_gcp_fields( - x, excluded_fields) for x in params] - elif isinstance(params, dict): - for k in params.keys(): - if k not in excluded_fields: - new_params[k] = GCPUtils.filter_gcp_fields( - params[k], excluded_fields) - else: - new_params = params - - return new_params - - @staticmethod - def are_params_equal(p1, p2): - """ - Check if two params dicts are equal. - TODO(supertom): need a way to filter out URLs, or they need to be built - """ - filtered_p1 = GCPUtils.filter_gcp_fields(p1) - filtered_p2 = GCPUtils.filter_gcp_fields(p2) - if filtered_p1 != filtered_p2: - return False - return True - - -class GCPError(Exception): - pass - - -class GCPOperationTimeoutError(GCPError): - pass - - -class GCPInvalidURLError(GCPError): - pass diff --git a/ansible_collections/community/google/plugins/modules/gc_storage.py b/ansible_collections/community/google/plugins/modules/gc_storage.py deleted file mode 100644 index 8344c251e..000000000 --- a/ansible_collections/community/google/plugins/modules/gc_storage.py +++ /dev/null @@ -1,497 +0,0 @@ -#!/usr/bin/python -# -# Copyright: 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 = ''' ---- -module: gc_storage -short_description: This module manages objects/buckets in Google Cloud Storage. -description: - - This module allows users to manage their objects/buckets in Google Cloud Storage. It allows upload and download operations and can set some - canned permissions. It also allows retrieval of URLs for objects for use in playbooks, and retrieval of string contents of objects. This module - requires setting the default project in GCS prior to playbook usage. See U(https://developers.google.com/storage/docs/reference/v1/apiversion1) for - information about setting the default project. - -options: - bucket: - type: str - description: - - Bucket name. - required: true - object: - type: path - description: - - Keyname of the object inside the bucket. Can be also be used to create "virtual directories" (see examples). - src: - type: str - description: - - The source file path when performing a PUT operation. - dest: - type: path - description: - - The destination file path when downloading an object/key with a GET operation. - overwrite: - description: - - Forces an overwrite either locally on the filesystem or remotely with the object/key. Used with PUT and GET operations. - type: bool - default: 'yes' - aliases: [ 'force' ] - permission: - type: str - description: - - This option let's the user set the canned permissions on the object/bucket that are created. The permissions that can be set are 'private', - 'public-read', 'authenticated-read'. - default: private - choices: ['private', 'public-read', 'authenticated-read'] - headers: - type: dict - description: - - Headers to attach to object. - default: {} - expiration: - type: int - default: 600 - description: - - Time limit (in seconds) for the URL generated and returned by GCA when performing a mode=put or mode=get_url operation. This url is only - available when public-read is the acl for the object. - aliases: [expiry] - mode: - type: str - description: - - Switches the module behaviour between upload, download, get_url (return download url) , get_str (download object as string), create (bucket) and - delete (bucket). - required: true - choices: [ 'get', 'put', 'get_url', 'get_str', 'delete', 'create' ] - gs_secret_key: - type: str - description: - - GS secret key. If not set then the value of the GS_SECRET_ACCESS_KEY environment variable is used. - required: true - gs_access_key: - type: str - description: - - GS access key. If not set then the value of the GS_ACCESS_KEY_ID environment variable is used. - required: true - region: - type: str - description: - - The gs region to use. If not defined then the value 'US' will be used. See U(https://cloud.google.com/storage/docs/bucket-locations) - default: 'US' - versioning: - description: - - Whether versioning is enabled or disabled (note that once versioning is enabled, it can only be suspended) - type: bool - default: false - -requirements: - - "python >= 2.6" - - "boto >= 2.9" - -author: -- Benno Joy (@bennojoy) -- Lukas Beumer (@Nitaco) - -''' - -EXAMPLES = ''' -- name: Upload some content - community.google.gc_storage: - bucket: mybucket - object: key.txt - src: /usr/local/myfile.txt - mode: put - permission: public-read - -- name: Upload some headers - community.google.gc_storage: - bucket: mybucket - object: key.txt - src: /usr/local/myfile.txt - headers: '{"Content-Encoding": "gzip"}' - -- name: Download some content - community.google.gc_storage: - bucket: mybucket - object: key.txt - dest: /usr/local/myfile.txt - mode: get - -- name: Download an object as a string to use else where in your playbook - community.google.gc_storage: - bucket: mybucket - object: key.txt - mode: get_str - -- name: Create an empty bucket - community.google.gc_storage: - bucket: mybucket - mode: create - -- name: Create a bucket with key as directory - community.google.gc_storage: - bucket: mybucket - object: /my/directory/path - mode: create - -- name: Delete a bucket and all contents - community.google.gc_storage: - bucket: mybucket - mode: delete - -- name: Create a bucket with versioning enabled - community.google.gc_storage: - bucket: "mybucket" - versioning: yes - mode: create - -- name: Create a bucket located in the eu - community.google.gc_storage: - bucket: "mybucket" - region: "europe-west3" - mode: create - -''' - -import os - -try: - import boto - HAS_BOTO = True -except ImportError: - HAS_BOTO = False - -from ansible.module_utils.basic import AnsibleModule - - -def grant_check(module, gs, obj): - try: - acp = obj.get_acl() - if module.params.get('permission') == 'public-read': - grant = [x for x in acp.entries.entry_list if x.scope.type == 'AllUsers'] - if not grant: - obj.set_acl('public-read') - module.exit_json(changed=True, result="The objects permission as been set to public-read") - if module.params.get('permission') == 'authenticated-read': - grant = [x for x in acp.entries.entry_list if x.scope.type == 'AllAuthenticatedUsers'] - if not grant: - obj.set_acl('authenticated-read') - module.exit_json(changed=True, result="The objects permission as been set to authenticated-read") - except gs.provider.storage_response_error as e: - module.fail_json(msg=str(e)) - return True - - -def key_check(module, gs, bucket, obj): - try: - bucket = gs.lookup(bucket) - key_check = bucket.get_key(obj) - except gs.provider.storage_response_error as e: - module.fail_json(msg=str(e)) - if key_check: - grant_check(module, gs, key_check) - return True - else: - return False - - -def keysum(module, gs, bucket, obj): - bucket = gs.lookup(bucket) - key_check = bucket.get_key(obj) - if not key_check: - return None - md5_remote = key_check.etag[1:-1] - etag_multipart = '-' in md5_remote # Check for multipart, etag is not md5 - if etag_multipart is True: - module.fail_json(msg="Files uploaded with multipart of gs are not supported with checksum, unable to compute checksum.") - return md5_remote - - -def bucket_check(module, gs, bucket): - try: - result = gs.lookup(bucket) - except gs.provider.storage_response_error as e: - module.fail_json(msg=str(e)) - if result: - grant_check(module, gs, result) - return True - else: - return False - - -def create_bucket(module, gs, bucket): - try: - bucket = gs.create_bucket(bucket, transform_headers(module.params.get('headers')), module.params.get('region')) - bucket.set_acl(module.params.get('permission')) - bucket.configure_versioning(module.params.get('versioning')) - except gs.provider.storage_response_error as e: - module.fail_json(msg=str(e)) - if bucket: - return True - - -def delete_bucket(module, gs, bucket): - try: - bucket = gs.lookup(bucket) - bucket_contents = bucket.list() - for key in bucket_contents: - bucket.delete_key(key.name) - bucket.delete() - return True - except gs.provider.storage_response_error as e: - module.fail_json(msg=str(e)) - - -def delete_key(module, gs, bucket, obj): - try: - bucket = gs.lookup(bucket) - bucket.delete_key(obj) - module.exit_json(msg="Object deleted from bucket ", changed=True) - except gs.provider.storage_response_error as e: - module.fail_json(msg=str(e)) - - -def create_dirkey(module, gs, bucket, obj): - try: - bucket = gs.lookup(bucket) - key = bucket.new_key(obj) - key.set_contents_from_string('') - module.exit_json(msg="Virtual directory %s created in bucket %s" % (obj, bucket.name), changed=True) - except gs.provider.storage_response_error as e: - module.fail_json(msg=str(e)) - - -def path_check(path): - if os.path.exists(path): - return True - else: - return False - - -def transform_headers(headers): - """ - Boto url-encodes values unless we convert the value to `str`, so doing - this prevents 'max-age=100000' from being converted to "max-age%3D100000". - - :param headers: Headers to convert - :type headers: dict - :rtype: dict - - """ - - for key, value in headers.items(): - headers[key] = str(value) - return headers - - -def upload_gsfile(module, gs, bucket, obj, src, expiry): - try: - bucket = gs.lookup(bucket) - key = bucket.new_key(obj) - key.set_contents_from_filename( - filename=src, - headers=transform_headers(module.params.get('headers')) - ) - key.set_acl(module.params.get('permission')) - url = key.generate_url(expiry) - module.exit_json(msg="PUT operation complete", url=url, changed=True) - except gs.provider.storage_copy_error as e: - module.fail_json(msg=str(e)) - - -def download_gsfile(module, gs, bucket, obj, dest): - try: - bucket = gs.lookup(bucket) - key = bucket.lookup(obj) - key.get_contents_to_filename(dest) - module.exit_json(msg="GET operation complete", changed=True) - except gs.provider.storage_copy_error as e: - module.fail_json(msg=str(e)) - - -def download_gsstr(module, gs, bucket, obj): - try: - bucket = gs.lookup(bucket) - key = bucket.lookup(obj) - contents = key.get_contents_as_string() - module.exit_json(msg="GET operation complete", contents=contents, changed=True) - except gs.provider.storage_copy_error as e: - module.fail_json(msg=str(e)) - - -def get_download_url(module, gs, bucket, obj, expiry): - try: - bucket = gs.lookup(bucket) - key = bucket.lookup(obj) - url = key.generate_url(expiry) - module.exit_json(msg="Download url:", url=url, expiration=expiry, changed=True) - except gs.provider.storage_response_error as e: - module.fail_json(msg=str(e)) - - -def handle_get(module, gs, bucket, obj, overwrite, dest): - md5_remote = keysum(module, gs, bucket, obj) - md5_local = module.md5(dest) - if md5_local == md5_remote: - module.exit_json(changed=False) - if md5_local != md5_remote and not overwrite: - module.exit_json(msg="WARNING: Checksums do not match. Use overwrite parameter to force download.", failed=True) - else: - download_gsfile(module, gs, bucket, obj, dest) - - -def handle_put(module, gs, bucket, obj, overwrite, src, expiration): - # Lets check to see if bucket exists to get ground truth. - bucket_rc = bucket_check(module, gs, bucket) - key_rc = key_check(module, gs, bucket, obj) - - # Lets check key state. Does it exist and if it does, compute the etag md5sum. - if bucket_rc and key_rc: - md5_remote = keysum(module, gs, bucket, obj) - md5_local = module.md5(src) - if md5_local == md5_remote: - module.exit_json(msg="Local and remote object are identical", changed=False) - if md5_local != md5_remote and not overwrite: - module.exit_json(msg="WARNING: Checksums do not match. Use overwrite parameter to force upload.", failed=True) - else: - upload_gsfile(module, gs, bucket, obj, src, expiration) - - if not bucket_rc: - create_bucket(module, gs, bucket) - upload_gsfile(module, gs, bucket, obj, src, expiration) - - # If bucket exists but key doesn't, just upload. - if bucket_rc and not key_rc: - upload_gsfile(module, gs, bucket, obj, src, expiration) - - -def handle_delete(module, gs, bucket, obj): - if bucket and not obj: - if bucket_check(module, gs, bucket): - module.exit_json(msg="Bucket %s and all keys have been deleted." % bucket, changed=delete_bucket(module, gs, bucket)) - else: - module.exit_json(msg="Bucket does not exist.", changed=False) - if bucket and obj: - if bucket_check(module, gs, bucket): - if key_check(module, gs, bucket, obj): - module.exit_json(msg="Object has been deleted.", changed=delete_key(module, gs, bucket, obj)) - else: - module.exit_json(msg="Object does not exist.", changed=False) - else: - module.exit_json(msg="Bucket does not exist.", changed=False) - else: - module.fail_json(msg="Bucket or Bucket & object parameter is required.", failed=True) - - -def handle_create(module, gs, bucket, obj): - if bucket and not obj: - if bucket_check(module, gs, bucket): - module.exit_json(msg="Bucket already exists.", changed=False) - else: - module.exit_json(msg="Bucket created successfully", changed=create_bucket(module, gs, bucket)) - if bucket and obj: - if obj.endswith('/'): - dirobj = obj - else: - dirobj = obj + "/" - - if bucket_check(module, gs, bucket): - if key_check(module, gs, bucket, dirobj): - module.exit_json(msg="Bucket %s and key %s already exists." % (bucket, obj), changed=False) - else: - create_dirkey(module, gs, bucket, dirobj) - else: - create_bucket(module, gs, bucket) - create_dirkey(module, gs, bucket, dirobj) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - bucket=dict(required=True), - object=dict(default=None, type='path'), - src=dict(default=None), - dest=dict(default=None, type='path'), - expiration=dict(type='int', default=600, aliases=['expiry']), - mode=dict(choices=['get', 'put', 'delete', 'create', 'get_url', 'get_str'], required=True), - permission=dict(choices=['private', 'public-read', 'authenticated-read'], default='private'), - headers=dict(type='dict', default={}), - gs_secret_key=dict(no_log=True, required=True), - gs_access_key=dict(required=True), - overwrite=dict(default=True, type='bool', aliases=['force']), - region=dict(default='US', type='str'), - versioning=dict(default=False, type='bool') - ), - ) - - if not HAS_BOTO: - module.fail_json(msg='`boto` 2.9+ is required for this module. Try: pip install `boto` --upgrade') - - bucket = module.params.get('bucket') - obj = module.params.get('object') - src = module.params.get('src') - dest = module.params.get('dest') - mode = module.params.get('mode') - expiry = module.params.get('expiration') - gs_secret_key = module.params.get('gs_secret_key') - gs_access_key = module.params.get('gs_access_key') - overwrite = module.params.get('overwrite') - - if mode == 'put': - if not src or not object: - module.fail_json(msg="When using PUT, src, bucket, object are mandatory parameters") - if mode == 'get': - if not dest or not object: - module.fail_json(msg="When using GET, dest, bucket, object are mandatory parameters") - - try: - gs = boto.connect_gs(gs_access_key, gs_secret_key) - except boto.exception.NoAuthHandlerFound as e: - module.fail_json(msg=str(e)) - - if mode == 'get': - if not bucket_check(module, gs, bucket) or not key_check(module, gs, bucket, obj): - module.fail_json(msg="Target bucket/key cannot be found", failed=True) - if not path_check(dest): - download_gsfile(module, gs, bucket, obj, dest) - else: - handle_get(module, gs, bucket, obj, overwrite, dest) - - if mode == 'put': - if not path_check(src): - module.fail_json(msg="Local object for PUT does not exist", failed=True) - handle_put(module, gs, bucket, obj, overwrite, src, expiry) - - # Support for deleting an object if we have both params. - if mode == 'delete': - handle_delete(module, gs, bucket, obj) - - if mode == 'create': - handle_create(module, gs, bucket, obj) - - if mode == 'get_url': - if bucket and obj: - if bucket_check(module, gs, bucket) and key_check(module, gs, bucket, obj): - get_download_url(module, gs, bucket, obj, expiry) - else: - module.fail_json(msg="Key/Bucket does not exist", failed=True) - else: - module.fail_json(msg="Bucket and Object parameters must be set", failed=True) - - # --------------------------- Get the String contents of an Object ------------------------- - if mode == 'get_str': - if bucket and obj: - if bucket_check(module, gs, bucket) and key_check(module, gs, bucket, obj): - download_gsstr(module, gs, bucket, obj) - else: - module.fail_json(msg="Key/Bucket does not exist", failed=True) - else: - module.fail_json(msg="Bucket and Object parameters must be set", failed=True) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gce_eip.py b/ansible_collections/community/google/plugins/modules/gce_eip.py deleted file mode 100644 index a9ad45e4d..000000000 --- a/ansible_collections/community/google/plugins/modules/gce_eip.py +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/python -# Copyright 2017 Google Inc. -# 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: gce_eip -short_description: Create or Destroy Global or Regional External IP addresses. -description: - - Create (reserve) or Destroy (release) Regional or Global IP Addresses. See - U(https://cloud.google.com/compute/docs/configure-instance-ip-addresses#reserve_new_static) for more on reserving static addresses. -requirements: - - "python >= 2.6" - - "apache-libcloud >= 0.19.0" -notes: - - Global addresses can only be used with Global Forwarding Rules. -author: - - "Tom Melendez (@supertom) <tom@supertom.com>" -options: - name: - type: str - description: - - Name of Address. - required: true - region: - type: str - description: - - Region to create the address in. Set to 'global' to create a global address. - required: true - state: - type: str - description: The state the address should be in. C(present) or C(absent) are the only valid options. - default: present - required: false - choices: [present, absent] - project_id: - type: str - description: - - The Google Cloud Platform project ID to use. - pem_file: - type: path - description: - - The path to the PEM file associated with the service account email. - - This option is deprecated and may be removed in a future release. Use I(credentials_file) instead. - credentials_file: - type: path - description: - - The path to the JSON file associated with the service account email. - service_account_email: - type: str - description: - - service account email - service_account_permissions: - type: list - description: - - service account permissions -''' - -EXAMPLES = ''' -- name: Create a Global external IP address - community.google.gce_eip: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - name: my-global-ip - region: global - state: present - -- name: Create a Regional external IP address - community.google.gce_eip: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - name: my-global-ip - region: us-east1 - state: present -''' - -RETURN = ''' -address: - description: IP address being operated on - returned: always - type: str - sample: "35.186.222.233" -name: - description: name of the address being operated on - returned: always - type: str - sample: "my-address" -region: - description: Which region an address belongs. - returned: always - type: str - sample: "global" -''' - -USER_AGENT_VERSION = 'v1' -USER_AGENT_PRODUCT = 'Ansible-gce_eip' - -try: - from ast import literal_eval - HAS_PYTHON26 = True -except ImportError: - HAS_PYTHON26 = False - -try: - import libcloud - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - from libcloud.common.google import GoogleBaseError, QuotaExceededError, \ - ResourceExistsError, ResourceInUseError, ResourceNotFoundError - from libcloud.compute.drivers.gce import GCEAddress - _ = Provider.GCE - HAS_LIBCLOUD = True -except ImportError: - HAS_LIBCLOUD = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gcp import gcp_connect - - -def get_address(gce, name, region): - """ - Get an Address from GCE. - - :param gce: An initialized GCE driver object. - :type gce: :class: `GCENodeDriver` - - :param name: Name of the Address. - :type name: ``str`` - - :return: A GCEAddress object or None. - :rtype: :class: `GCEAddress` or None - """ - try: - return gce.ex_get_address(name=name, region=region) - - except ResourceNotFoundError: - return None - - -def create_address(gce, params): - """ - Create a new Address. - - :param gce: An initialized GCE driver object. - :type gce: :class: `GCENodeDriver` - - :param params: Dictionary of parameters needed by the module. - :type params: ``dict`` - - :return: Tuple with changed status and address. - :rtype: tuple in the format of (bool, str) - """ - changed = False - return_data = [] - - address = gce.ex_create_address( - name=params['name'], region=params['region']) - - if address: - changed = True - return_data = address.address - - return (changed, return_data) - - -def delete_address(address): - """ - Delete an Address. - - :param gce: An initialized GCE driver object. - :type gce: :class: `GCENodeDriver` - - :param params: Dictionary of parameters needed by the module. - :type params: ``dict`` - - :return: Tuple with changed status and address. - :rtype: tuple in the format of (bool, str) - """ - changed = False - return_data = [] - if address.destroy(): - changed = True - return_data = address.address - return (changed, return_data) - - -def main(): - module = AnsibleModule(argument_spec=dict( - name=dict(required=True), - state=dict(choices=['absent', 'present'], default='present'), - region=dict(required=True), - service_account_email=dict(), - service_account_permissions=dict(type='list'), - pem_file=dict(type='path'), - credentials_file=dict(type='path'), - project_id=dict(), ), ) - - if not HAS_PYTHON26: - module.fail_json( - msg="GCE module requires python's 'ast' module, python v2.6+") - if not HAS_LIBCLOUD: - module.fail_json( - msg='libcloud with GCE support (+0.19) required for this module.') - - gce = gcp_connect(module, Provider.GCE, get_driver, - USER_AGENT_PRODUCT, USER_AGENT_VERSION) - - params = {} - params['state'] = module.params.get('state') - params['name'] = module.params.get('name') - params['region'] = module.params.get('region') - - changed = False - json_output = {'state': params['state']} - address = get_address(gce, params['name'], region=params['region']) - - if params['state'] == 'absent': - if not address: - # Doesn't exist in GCE, and state==absent. - changed = False - module.fail_json( - msg="Cannot delete unknown address: %s" % - (params['name'])) - else: - # Delete - (changed, json_output['address']) = delete_address(address) - else: - if not address: - # Create - (changed, json_output['address']) = create_address(gce, - params) - else: - changed = False - json_output['address'] = address.address - - json_output['changed'] = changed - json_output.update(params) - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gce_img.py b/ansible_collections/community/google/plugins/modules/gce_img.py deleted file mode 100644 index 9e53e1e23..000000000 --- a/ansible_collections/community/google/plugins/modules/gce_img.py +++ /dev/null @@ -1,211 +0,0 @@ -#!/usr/bin/python -# Copyright 2015 Google Inc. All Rights Reserved. -# 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 - - -"""An Ansible module to utilize GCE image resources.""" - -DOCUMENTATION = ''' ---- -module: gce_img -short_description: utilize GCE image resources -description: - - This module can create and delete GCE private images from gzipped - compressed tarball containing raw disk data or from existing detached - disks in any zone. U(https://cloud.google.com/compute/docs/images) -options: - name: - type: str - description: - - the name of the image to create or delete - required: true - description: - type: str - description: - - an optional description - family: - type: str - description: - - an optional family name - source: - type: str - description: - - the source disk or the Google Cloud Storage URI to create the image from - state: - type: str - description: - - desired state of the image - default: "present" - choices: ["present", "absent"] - zone: - type: str - description: - - the zone of the disk specified by source - default: "us-central1-a" - timeout: - type: int - description: - - timeout for the operation - default: 180 - service_account_email: - type: str - description: - - service account email - pem_file: - type: path - description: - - path to the pem file associated with the service account email - project_id: - type: str - description: - - your GCE project ID -requirements: - - "python >= 2.6" - - "apache-libcloud" -author: "Tom Melendez (@supertom)" -''' - -EXAMPLES = ''' -- name: Create an image named test-image from the disk 'test-disk' in zone us-central1-a - community.google.gce_img: - name: test-image - source: test-disk - zone: us-central1-a - state: present - -- name: Create an image named test-image from a tarball in Google Cloud Storage - community.google.gce_img: - name: test-image - source: https://storage.googleapis.com/bucket/path/to/image.tgz - -- name: Alternatively use the gs scheme - community.google.gce_img: - name: test-image - source: gs://bucket/path/to/image.tgz - -- name: Delete an image named test-image - community.google.gce_img: - name: test-image - state: absent -''' - - -try: - import libcloud - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - from libcloud.common.google import GoogleBaseError - from libcloud.common.google import ResourceExistsError - from libcloud.common.google import ResourceNotFoundError - _ = Provider.GCE - has_libcloud = True -except ImportError: - has_libcloud = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gce import gce_connect - - -GCS_URI = 'https://storage.googleapis.com/' - - -def create_image(gce, name, module): - """Create an image with the specified name.""" - source = module.params.get('source') - zone = module.params.get('zone') - desc = module.params.get('description') - timeout = module.params.get('timeout') - family = module.params.get('family') - - if not source: - module.fail_json(msg='Must supply a source', changed=False) - - if source.startswith(GCS_URI): - # source is a Google Cloud Storage URI - volume = source - elif source.startswith('gs://'): - # libcloud only accepts https URI. - volume = source.replace('gs://', GCS_URI) - else: - try: - volume = gce.ex_get_volume(source, zone) - except ResourceNotFoundError: - module.fail_json(msg='Disk %s not found in zone %s' % (source, zone), - changed=False) - except GoogleBaseError as e: - module.fail_json(msg=str(e), changed=False) - - gce_extra_args = {} - if family is not None: - gce_extra_args['family'] = family - - old_timeout = gce.connection.timeout - try: - gce.connection.timeout = timeout - gce.ex_create_image(name, volume, desc, use_existing=False, **gce_extra_args) - return True - except ResourceExistsError: - return False - except GoogleBaseError as e: - module.fail_json(msg=str(e), changed=False) - finally: - gce.connection.timeout = old_timeout - - -def delete_image(gce, name, module): - """Delete a specific image resource by name.""" - try: - gce.ex_delete_image(name) - return True - except ResourceNotFoundError: - return False - except GoogleBaseError as e: - module.fail_json(msg=str(e), changed=False) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - name=dict(required=True), - family=dict(), - description=dict(), - source=dict(), - state=dict(default='present', choices=['present', 'absent']), - zone=dict(default='us-central1-a'), - service_account_email=dict(), - pem_file=dict(type='path'), - project_id=dict(), - timeout=dict(type='int', default=180) - ) - ) - - if not has_libcloud: - module.fail_json(msg='libcloud with GCE support is required.') - - gce = gce_connect(module) - - name = module.params.get('name') - state = module.params.get('state') - family = module.params.get('family') - changed = False - - if family is not None and hasattr(libcloud, '__version__') and libcloud.__version__ <= '0.20.1': - module.fail_json(msg="Apache Libcloud 1.0.0+ is required to use 'family' option", - changed=False) - - # user wants to create an image. - if state == 'present': - changed = create_image(gce, name, module) - - # user wants to delete the image. - if state == 'absent': - changed = delete_image(gce, name, module) - - module.exit_json(changed=changed, name=name) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gce_instance_template.py b/ansible_collections/community/google/plugins/modules/gce_instance_template.py deleted file mode 100644 index ec436b422..000000000 --- a/ansible_collections/community/google/plugins/modules/gce_instance_template.py +++ /dev/null @@ -1,605 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: 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 = ''' ---- -module: gce_instance_template -short_description: create or destroy instance templates of Compute Engine of GCP. -description: - - Creates or destroy Google instance templates - of Compute Engine of Google Cloud Platform. -options: - state: - type: str - description: - - The desired state for the instance template. - default: "present" - choices: ["present", "absent"] - name: - type: str - description: - - The name of the GCE instance template. - required: True - aliases: [base_name] - size: - type: str - description: - - The desired machine type for the instance template. - default: "f1-micro" - source: - type: str - description: - - A source disk to attach to the instance. - Cannot specify both I(image) and I(source). - image: - type: str - description: - - The image to use to create the instance. - Cannot specify both both I(image) and I(source). - image_family: - type: str - description: - - The image family to use to create the instance. - If I(image) has been used I(image_family) is ignored. - Cannot specify both I(image) and I(source). - default: debian-8 - disk_type: - type: str - description: - - Specify a C(pd-standard) disk or C(pd-ssd) for an SSD disk. - choices: - - pd-standard - - pd-ssd - default: pd-standard - disk_auto_delete: - description: - - Indicate that the boot disk should be - deleted when the Node is deleted. - default: true - type: bool - network: - type: str - description: - - The network to associate with the instance. - default: "default" - subnetwork: - type: str - description: - - The Subnetwork resource name for this instance. - can_ip_forward: - description: - - Set to C(yes) to allow instance to - send/receive non-matching src/dst packets. - type: bool - default: 'no' - external_ip: - type: str - description: - - The external IP address to use. - If C(ephemeral), a new non-static address will be - used. If C(None), then no external address will - be used. To use an existing static IP address - specify address name. - default: "ephemeral" - service_account_email: - type: str - description: - - service account email - service_account_permissions: - type: list - description: - - service account permissions (see - U(https://cloud.google.com/sdk/gcloud/reference/compute/instances/create), - --scopes section for detailed information) - - > - Available choices are: - C(bigquery), C(cloud-platform), C(compute-ro), C(compute-rw), - C(useraccounts-ro), C(useraccounts-rw), C(datastore), C(logging-write), - C(monitoring), C(sql-admin), C(storage-full), C(storage-ro), - C(storage-rw), C(taskqueue), C(userinfo-email). - automatic_restart: - description: - - Defines whether the instance should be - automatically restarted when it is - terminated by Compute Engine. - type: bool - preemptible: - description: - - Defines whether the instance is preemptible. - type: bool - tags: - type: list - description: - - a comma-separated list of tags to associate with the instance - metadata: - description: - - a hash/dictionary of custom data for the instance; - '{"key":"value", ...}' - description: - type: str - description: - - description of instance template - disks: - type: list - description: - - a list of persistent disks to attach to the instance; a string value - gives the name of the disk; alternatively, a dictionary value can - define 'name' and 'mode' ('READ_ONLY' or 'READ_WRITE'). The first entry - will be the boot disk (which must be READ_WRITE). - nic_gce_struct: - type: list - description: - - Support passing in the GCE-specific - formatted networkInterfaces[] structure. - disks_gce_struct: - type: list - description: - - Support passing in the GCE-specific - formatted formatted disks[] structure. Case sensitive. - see U(https://cloud.google.com/compute/docs/reference/latest/instanceTemplates#resource) for detailed information - project_id: - type: str - description: - - your GCE project ID - pem_file: - type: path - description: - - path to the pem file associated with the service account email - This option is deprecated. Use 'credentials_file'. - credentials_file: - type: path - description: - - path to the JSON file associated with the service account email - subnetwork_region: - type: str - description: - - Region that subnetwork resides in. (Required for subnetwork to successfully complete) -requirements: - - "python >= 2.6" - - "apache-libcloud >= 0.13.3, >= 0.17.0 if using JSON credentials, - >= 0.20.0 if using preemptible option" -notes: - - JSON credentials strongly preferred. -author: "Gwenael Pellen (@GwenaelPellenArkeup) <gwenael.pellen@arkeup.com>" -''' - -EXAMPLES = ''' -# Usage -- name: Create instance template named foo - community.google.gce_instance_template: - name: foo - size: n1-standard-1 - image_family: ubuntu-1604-lts - state: present - project_id: "your-project-name" - credentials_file: "/path/to/your-key.json" - service_account_email: "your-sa@your-project-name.iam.gserviceaccount.com" - -# Example Playbook -- name: Compute Engine Instance Template Examples - hosts: localhost - vars: - service_account_email: "your-sa@your-project-name.iam.gserviceaccount.com" - credentials_file: "/path/to/your-key.json" - project_id: "your-project-name" - tasks: - - name: Create instance template - community.google.gce_instance_template: - name: my-test-instance-template - size: n1-standard-1 - image_family: ubuntu-1604-lts - state: present - project_id: "{{ project_id }}" - credentials_file: "{{ credentials_file }}" - service_account_email: "{{ service_account_email }}" - - name: Delete instance template - community.google.gce_instance_template: - name: my-test-instance-template - size: n1-standard-1 - image_family: ubuntu-1604-lts - state: absent - project_id: "{{ project_id }}" - credentials_file: "{{ credentials_file }}" - service_account_email: "{{ service_account_email }}" - -# Example playbook using disks_gce_struct -- name: Compute Engine Instance Template Examples - hosts: localhost - vars: - service_account_email: "your-sa@your-project-name.iam.gserviceaccount.com" - credentials_file: "/path/to/your-key.json" - project_id: "your-project-name" - tasks: - - name: Create instance template - community.google.gce_instance_template: - name: foo - size: n1-standard-1 - state: present - project_id: "{{ project_id }}" - credentials_file: "{{ credentials_file }}" - service_account_email: "{{ service_account_email }}" - disks_gce_struct: - - device_name: /dev/sda - boot: true - autoDelete: true - initializeParams: - diskSizeGb: 30 - diskType: pd-ssd - sourceImage: projects/debian-cloud/global/images/family/debian-8 - -''' - -RETURN = ''' -''' - -import traceback -try: - from ast import literal_eval - HAS_PYTHON26 = True -except ImportError: - HAS_PYTHON26 = False - -try: - import libcloud - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - from libcloud.common.google import GoogleBaseError, QuotaExceededError, \ - ResourceExistsError, ResourceInUseError, ResourceNotFoundError - from libcloud.compute.drivers.gce import GCEAddress - _ = Provider.GCE - HAS_LIBCLOUD = True -except ImportError: - HAS_LIBCLOUD = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gce import gce_connect -from ansible.module_utils._text import to_native - - -def get_info(inst): - """Retrieves instance template information - """ - return({ - 'name': inst.name, - 'extra': inst.extra, - }) - - -def create_instance_template(module, gce): - """Create an instance template - module : AnsibleModule object - gce: authenticated GCE libcloud driver - Returns: - instance template information - """ - # get info from module - name = module.params.get('name') - size = module.params.get('size') - source = module.params.get('source') - image = module.params.get('image') - image_family = module.params.get('image_family') - disk_type = module.params.get('disk_type') - disk_auto_delete = module.params.get('disk_auto_delete') - network = module.params.get('network') - subnetwork = module.params.get('subnetwork') - subnetwork_region = module.params.get('subnetwork_region') - can_ip_forward = module.params.get('can_ip_forward') - external_ip = module.params.get('external_ip') - service_account_permissions = module.params.get( - 'service_account_permissions') - service_account_email = module.params.get('service_account_email') - on_host_maintenance = module.params.get('on_host_maintenance') - automatic_restart = module.params.get('automatic_restart') - preemptible = module.params.get('preemptible') - tags = module.params.get('tags') - metadata = module.params.get('metadata') - description = module.params.get('description') - disks_gce_struct = module.params.get('disks_gce_struct') - changed = False - - # args of ex_create_instancetemplate - gce_args = dict( - name="instance", - size="f1-micro", - source=None, - image=None, - disk_type='pd-standard', - disk_auto_delete=True, - network='default', - subnetwork=None, - can_ip_forward=None, - external_ip='ephemeral', - service_accounts=None, - on_host_maintenance=None, - automatic_restart=None, - preemptible=None, - tags=None, - metadata=None, - description=None, - disks_gce_struct=None, - nic_gce_struct=None - ) - - gce_args['name'] = name - gce_args['size'] = size - - if source is not None: - gce_args['source'] = source - - if image: - gce_args['image'] = image - else: - if image_family: - image = gce.ex_get_image_from_family(image_family) - gce_args['image'] = image - else: - gce_args['image'] = "debian-8" - - gce_args['disk_type'] = disk_type - gce_args['disk_auto_delete'] = disk_auto_delete - - gce_network = gce.ex_get_network(network) - gce_args['network'] = gce_network - - if subnetwork is not None: - gce_args['subnetwork'] = gce.ex_get_subnetwork(subnetwork, region=subnetwork_region) - - if can_ip_forward is not None: - gce_args['can_ip_forward'] = can_ip_forward - - if external_ip == "ephemeral": - instance_external_ip = external_ip - elif external_ip == "none": - instance_external_ip = None - else: - try: - instance_external_ip = gce.ex_get_address(external_ip) - except GoogleBaseError as err: - # external_ip is name ? - instance_external_ip = external_ip - gce_args['external_ip'] = instance_external_ip - - ex_sa_perms = [] - bad_perms = [] - if service_account_permissions: - for perm in service_account_permissions: - if perm not in gce.SA_SCOPES_MAP: - bad_perms.append(perm) - if len(bad_perms) > 0: - module.fail_json(msg='bad permissions: %s' % str(bad_perms)) - if service_account_email is not None: - ex_sa_perms.append({'email': str(service_account_email)}) - else: - ex_sa_perms.append({'email': "default"}) - ex_sa_perms[0]['scopes'] = service_account_permissions - gce_args['service_accounts'] = ex_sa_perms - - if on_host_maintenance is not None: - gce_args['on_host_maintenance'] = on_host_maintenance - - if automatic_restart is not None: - gce_args['automatic_restart'] = automatic_restart - - if preemptible is not None: - gce_args['preemptible'] = preemptible - - if tags is not None: - gce_args['tags'] = tags - - if disks_gce_struct is not None: - gce_args['disks_gce_struct'] = disks_gce_struct - - # Try to convert the user's metadata value into the format expected - # by GCE. First try to ensure user has proper quoting of a - # dictionary-like syntax using 'literal_eval', then convert the python - # dict into a python list of 'key' / 'value' dicts. Should end up - # with: - # [ {'key': key1, 'value': value1}, {'key': key2, 'value': value2}, ...] - if metadata: - if isinstance(metadata, dict): - md = metadata - else: - try: - md = literal_eval(str(metadata)) - if not isinstance(md, dict): - raise ValueError('metadata must be a dict') - except ValueError as e: - module.fail_json(msg='bad metadata: %s' % str(e)) - except SyntaxError as e: - module.fail_json(msg='bad metadata syntax') - - if hasattr(libcloud, '__version__') and libcloud.__version__ < '0.15': - items = [] - for k, v in md.items(): - items.append({"key": k, "value": v}) - metadata = {'items': items} - else: - metadata = md - gce_args['metadata'] = metadata - - if description is not None: - gce_args['description'] = description - - instance = None - try: - instance = gce.ex_get_instancetemplate(name) - except ResourceNotFoundError: - try: - instance = gce.ex_create_instancetemplate(**gce_args) - changed = True - except GoogleBaseError as err: - module.fail_json( - msg='Unexpected error attempting to create instance {0}, error: {1}' - .format( - instance, - err.value - ) - ) - - if instance: - json_data = get_info(instance) - else: - module.fail_json(msg="no instance template!") - - return (changed, json_data, name) - - -def delete_instance_template(module, gce): - """ Delete instance template. - module : AnsibleModule object - gce: authenticated GCE libcloud driver - Returns: - instance template information - """ - name = module.params.get('name') - current_state = "absent" - changed = False - - # get instance template - instance = None - try: - instance = gce.ex_get_instancetemplate(name) - current_state = "present" - except GoogleBaseError as e: - json_data = dict(msg='instance template not exists: %s' % to_native(e), - exception=traceback.format_exc()) - - if current_state == "present": - rc = instance.destroy() - if rc: - changed = True - else: - module.fail_json( - msg='instance template destroy failed' - ) - - json_data = {} - return (changed, json_data, name) - - -def module_controller(module, gce): - ''' Control module state parameter. - module : AnsibleModule object - gce: authenticated GCE libcloud driver - Returns: - nothing - Exit: - AnsibleModule object exit with json data. - ''' - json_output = dict() - state = module.params.get("state") - if state == "present": - (changed, output, name) = create_instance_template(module, gce) - json_output['changed'] = changed - json_output['msg'] = output - elif state == "absent": - (changed, output, name) = delete_instance_template(module, gce) - json_output['changed'] = changed - json_output['msg'] = output - - module.exit_json(**json_output) - - -def check_if_system_state_would_be_changed(module, gce): - ''' check_if_system_state_would_be_changed ! - module : AnsibleModule object - gce: authenticated GCE libcloud driver - Returns: - system_state changed - ''' - changed = False - current_state = "absent" - - state = module.params.get("state") - name = module.params.get("name") - - try: - gce.ex_get_instancetemplate(name) - current_state = "present" - except GoogleBaseError as e: - module.fail_json(msg='GCE get instancetemplate problem: %s' % to_native(e), - exception=traceback.format_exc()) - - if current_state != state: - changed = True - - if current_state == "absent": - if changed: - output = 'instance template {0} will be created'.format(name) - else: - output = 'nothing to do for instance template {0} '.format(name) - if current_state == "present": - if changed: - output = 'instance template {0} will be destroyed'.format(name) - else: - output = 'nothing to do for instance template {0} '.format(name) - - return (changed, output) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - state=dict(choices=['present', 'absent'], default='present'), - name=dict(required=True, aliases=['base_name']), - size=dict(default='f1-micro'), - source=dict(), - image=dict(), - image_family=dict(default='debian-8'), - disk_type=dict(choices=['pd-standard', 'pd-ssd'], default='pd-standard', type='str'), - disk_auto_delete=dict(type='bool', default=True), - network=dict(default='default'), - subnetwork=dict(), - can_ip_forward=dict(type='bool', default=False), - external_ip=dict(default='ephemeral'), - service_account_email=dict(), - service_account_permissions=dict(type='list'), - automatic_restart=dict(type='bool', default=None), - preemptible=dict(type='bool', default=None), - tags=dict(type='list'), - metadata=dict(), - description=dict(), - disks=dict(type='list'), - nic_gce_struct=dict(type='list'), - project_id=dict(), - pem_file=dict(type='path'), - credentials_file=dict(type='path'), - subnetwork_region=dict(), - disks_gce_struct=dict(type='list') - ), - mutually_exclusive=[['source', 'image']], - required_one_of=[['image', 'image_family']], - supports_check_mode=True - ) - - if not HAS_PYTHON26: - module.fail_json( - msg="GCE module requires python's 'ast' module, python v2.6+") - if not HAS_LIBCLOUD: - module.fail_json( - msg='libcloud with GCE support (0.17.0+) required for this module') - - try: - gce = gce_connect(module) - except GoogleBaseError as e: - module.fail_json(msg='GCE Connection failed %s' % to_native(e), exception=traceback.format_exc()) - - if module.check_mode: - (changed, output) = check_if_system_state_would_be_changed(module, gce) - module.exit_json( - changed=changed, - msg=output - ) - else: - module_controller(module, gce) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gce_labels.py b/ansible_collections/community/google/plugins/modules/gce_labels.py deleted file mode 100644 index 3eed2df2f..000000000 --- a/ansible_collections/community/google/plugins/modules/gce_labels.py +++ /dev/null @@ -1,350 +0,0 @@ -#!/usr/bin/python -# Copyright 2017 Google Inc. -# 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: gce_labels -short_description: Create, Update or Destroy GCE Labels. -description: - - Create, Update or Destroy GCE Labels on instances, disks, snapshots, etc. - When specifying the GCE resource, users may specify the full URL for - the resource (its 'self_link'), or the individual parameters of the - resource (type, location, name). Examples for the two options can be - seen in the documentation. - See U(https://cloud.google.com/compute/docs/label-or-tag-resources) for - more information about GCE Labels. Labels are gradually being added to - more GCE resources, so this module will need to be updated as new - resources are added to the GCE (v1) API. -requirements: - - 'python >= 2.6' - - 'google-api-python-client >= 1.6.2' - - 'google-auth >= 1.0.0' - - 'google-auth-httplib2 >= 0.0.2' -notes: - - Labels support resources such as instances, disks, images, etc. See - U(https://cloud.google.com/compute/docs/labeling-resources) for the list - of resources available in the GCE v1 API (not alpha or beta). -author: - - 'Eric Johnson (@erjohnso) <erjohnso@google.com>' -options: - labels: - type: dict - description: - - A list of labels (key/value pairs) to add or remove for the resource. - required: false - resource_url: - type: str - description: - - The 'self_link' for the resource (instance, disk, snapshot, etc) - required: false - resource_type: - type: str - description: - - The type of resource (instances, disks, snapshots, images) - required: false - resource_location: - type: str - description: - - The location of resource (global, us-central1-f, etc.) - required: false - resource_name: - type: str - description: - - The name of resource. - required: false - state: - type: str - description: The state the labels should be in. C(present) or C(absent) are the only valid options. - default: present - required: false - choices: [present, absent] - project_id: - type: str - description: - - The Google Cloud Platform project ID to use. - pem_file: - type: str - description: - - The path to the PEM file associated with the service account email. - - This option is deprecated and may be removed in a future release. Use I(credentials_file) instead. - credentials_file: - type: str - description: - - The path to the JSON file associated with the service account email. - service_account_email: - type: str - description: - - service account email - service_account_permissions: - type: list - description: - - service account email -''' - -EXAMPLES = ''' -- name: Add labels on an existing instance (using resource_url) - community.google.gce_labels: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - labels: - webserver-frontend: homepage - environment: test - experiment-name: kennedy - resource_url: https://www.googleapis.com/compute/beta/projects/myproject/zones/us-central1-f/instances/example-instance - state: present -- name: Add labels on an image (using resource params) - community.google.gce_labels: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - labels: - webserver-frontend: homepage - environment: test - experiment-name: kennedy - resource_type: images - resource_location: global - resource_name: my-custom-image - state: present -- name: Remove specified labels from the GCE instance - community.google.gce_labels: - service_account_email: "{{ service_account_email }}" - credentials_file: "{{ credentials_file }}" - project_id: "{{ project_id }}" - labels: - environment: prod - experiment-name: kennedy - resource_url: https://www.googleapis.com/compute/beta/projects/myproject/zones/us-central1-f/instances/example-instance - state: absent -''' - -RETURN = ''' -labels: - description: List of labels that exist on the resource. - returned: Always. - type: dict - sample: [ { 'webserver-frontend': 'homepage', 'environment': 'test', 'environment-name': 'kennedy' } ] -resource_url: - description: The 'self_link' of the GCE resource. - returned: Always. - type: str - sample: 'https://www.googleapis.com/compute/beta/projects/myproject/zones/us-central1-f/instances/example-instance' -resource_type: - description: The type of the GCE resource. - returned: Always. - type: str - sample: instances -resource_location: - description: The location of the GCE resource. - returned: Always. - type: str - sample: us-central1-f -resource_name: - description: The name of the GCE resource. - returned: Always. - type: str - sample: my-happy-little-instance -state: - description: state of the labels - returned: Always. - type: str - sample: present -''' - -try: - from ast import literal_eval - HAS_PYTHON26 = True -except ImportError: - HAS_PYTHON26 = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gcp import check_params, get_google_api_client, GCPUtils - - -UA_PRODUCT = 'ansible-gce_labels' -UA_VERSION = '0.0.1' -GCE_API_VERSION = 'v1' - -# TODO(all): As Labels are added to more GCE resources, this list will need to -# be updated (along with some code changes below). The list can *only* include -# resources from the 'v1' GCE API and will *not* work with 'beta' or 'alpha'. -KNOWN_RESOURCES = ['instances', 'disks', 'snapshots', 'images'] - - -def _fetch_resource(client, module): - params = module.params - if params['resource_url']: - if not params['resource_url'].startswith('https://www.googleapis.com/compute'): - module.fail_json( - msg='Invalid self_link url: %s' % params['resource_url']) - else: - parts = params['resource_url'].split('/')[8:] - if len(parts) == 2: - resource_type, resource_name = parts - resource_location = 'global' - else: - resource_location, resource_type, resource_name = parts - else: - if not params['resource_type'] or not params['resource_location'] \ - or not params['resource_name']: - module.fail_json(msg='Missing required resource params.') - resource_type = params['resource_type'].lower() - resource_name = params['resource_name'].lower() - resource_location = params['resource_location'].lower() - - if resource_type not in KNOWN_RESOURCES: - module.fail_json(msg='Unsupported resource_type: %s' % resource_type) - - # TODO(all): See the comment above for KNOWN_RESOURCES. As labels are - # added to the v1 GCE API for more resources, some minor code work will - # need to be added here. - if resource_type == 'instances': - resource = client.instances().get(project=params['project_id'], - zone=resource_location, - instance=resource_name).execute() - elif resource_type == 'disks': - resource = client.disks().get(project=params['project_id'], - zone=resource_location, - disk=resource_name).execute() - elif resource_type == 'snapshots': - resource = client.snapshots().get(project=params['project_id'], - snapshot=resource_name).execute() - elif resource_type == 'images': - resource = client.images().get(project=params['project_id'], - image=resource_name).execute() - else: - module.fail_json(msg='Unsupported resource type: %s' % resource_type) - - return resource.get('labelFingerprint', ''), { - 'resource_name': resource.get('name'), - 'resource_url': resource.get('selfLink'), - 'resource_type': resource_type, - 'resource_location': resource_location, - 'labels': resource.get('labels', {}) - } - - -def _set_labels(client, new_labels, module, ri, fingerprint): - params = module.params - result = err = None - labels = { - 'labels': new_labels, - 'labelFingerprint': fingerprint - } - - # TODO(all): See the comment above for KNOWN_RESOURCES. As labels are - # added to the v1 GCE API for more resources, some minor code work will - # need to be added here. - if ri['resource_type'] == 'instances': - req = client.instances().setLabels(project=params['project_id'], - instance=ri['resource_name'], - zone=ri['resource_location'], - body=labels) - elif ri['resource_type'] == 'disks': - req = client.disks().setLabels(project=params['project_id'], - zone=ri['resource_location'], - resource=ri['resource_name'], - body=labels) - elif ri['resource_type'] == 'snapshots': - req = client.snapshots().setLabels(project=params['project_id'], - resource=ri['resource_name'], - body=labels) - elif ri['resource_type'] == 'images': - req = client.images().setLabels(project=params['project_id'], - resource=ri['resource_name'], - body=labels) - else: - module.fail_json(msg='Unsupported resource type: %s' % ri['resource_type']) - - # TODO(erjohnso): Once Labels goes GA, we'll be able to use the GCPUtils - # method to poll for the async request/operation to complete before - # returning. However, during 'beta', we are in an odd state where - # API requests must be sent to the 'compute/beta' API, but the python - # client library only allows for *Operations.get() requests to be - # sent to 'compute/v1' API. The response operation is in the 'beta' - # API-scope, but the client library cannot find the operation (404). - # result = GCPUtils.execute_api_client_req(req, client=client, raw=False) - # return result, err - result = req.execute() - return True, err - - -def main(): - module = AnsibleModule( - argument_spec=dict( - state=dict(choices=['absent', 'present'], default='present'), - service_account_email=dict(), - service_account_permissions=dict(type='list'), - pem_file=dict(), - credentials_file=dict(), - labels=dict(required=False, type='dict', default={}), - resource_url=dict(required=False, type='str'), - resource_name=dict(required=False, type='str'), - resource_location=dict(required=False, type='str'), - resource_type=dict(required=False, type='str'), - project_id=dict() - ), - required_together=[ - ['resource_name', 'resource_location', 'resource_type'] - ], - mutually_exclusive=[ - ['resource_url', 'resource_name'], - ['resource_url', 'resource_location'], - ['resource_url', 'resource_type'] - ] - ) - - if not HAS_PYTHON26: - module.fail_json( - msg="GCE module requires python's 'ast' module, python v2.6+") - - client, cparams = get_google_api_client(module, 'compute', - user_agent_product=UA_PRODUCT, - user_agent_version=UA_VERSION, - api_version=GCE_API_VERSION) - - # Get current resource info including labelFingerprint - fingerprint, resource_info = _fetch_resource(client, module) - new_labels = resource_info['labels'].copy() - - update_needed = False - if module.params['state'] == 'absent': - for k, v in module.params['labels'].items(): - if k in new_labels: - if new_labels[k] == v: - update_needed = True - new_labels.pop(k, None) - else: - module.fail_json(msg="Could not remove unmatched label pair '%s':'%s'" % (k, v)) - else: - for k, v in module.params['labels'].items(): - if k not in new_labels: - update_needed = True - new_labels[k] = v - - changed = False - json_output = {'state': module.params['state']} - if update_needed: - changed, err = _set_labels(client, new_labels, module, resource_info, - fingerprint) - json_output['changed'] = changed - - # TODO(erjohnso): probably want to re-fetch the resource to return the - # new labelFingerprint, check that desired labels match updated labels. - # BUT! Will need to wait for setLabels() to hit v1 API so we can use the - # GCPUtils feature to poll for the operation to be complete. For now, - # we'll just update the output with what we have from the original - # state of the resource. - json_output.update(resource_info) - json_output.update(module.params) - - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gce_lb.py b/ansible_collections/community/google/plugins/modules/gce_lb.py deleted file mode 100644 index ff29b56de..000000000 --- a/ansible_collections/community/google/plugins/modules/gce_lb.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/python -# Copyright 2013 Google Inc. -# 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: gce_lb -short_description: create/destroy GCE load-balancer resources -description: - - This module can create and destroy Google Compute Engine C(loadbalancer) - and C(httphealthcheck) resources. The primary LB resource is the - C(load_balancer) resource and the health check parameters are all - prefixed with I(httphealthcheck). - The full documentation for Google Compute Engine load balancing is at - U(https://developers.google.com/compute/docs/load-balancing/). However, - the ansible module simplifies the configuration by following the - libcloud model. - Full install/configuration instructions for the gce* modules can - be found in the comments of ansible/test/gce_tests.py. -options: - httphealthcheck_name: - type: str - description: - - the name identifier for the HTTP health check - httphealthcheck_port: - type: int - description: - - the TCP port to use for HTTP health checking - default: 80 - httphealthcheck_path: - type: str - description: - - the url path to use for HTTP health checking - default: "/" - httphealthcheck_interval: - type: int - description: - - the duration in seconds between each health check request - default: 5 - httphealthcheck_timeout: - type: int - description: - - the timeout in seconds before a request is considered a failed check - default: 5 - httphealthcheck_unhealthy_count: - type: int - description: - - number of consecutive failed checks before marking a node unhealthy - default: 2 - httphealthcheck_healthy_count: - type: int - description: - - number of consecutive successful checks before marking a node healthy - default: 2 - httphealthcheck_host: - type: str - description: - - host header to pass through on HTTP check requests - name: - type: str - description: - - name of the load-balancer resource - protocol: - type: str - description: - - the protocol used for the load-balancer packet forwarding, tcp or udp - - "the available choices are: C(tcp) or C(udp)." - default: "tcp" - region: - type: str - description: - - the GCE region where the load-balancer is defined - external_ip: - type: str - description: - - the external static IPv4 (or auto-assigned) address for the LB - port_range: - type: str - description: - - the port (range) to forward, e.g. 80 or 8000-8888 defaults to all ports - members: - type: list - description: - - a list of zone/nodename pairs, e.g ['us-central1-a/www-a', ...] - state: - type: str - description: - - desired state of the LB - - "the available choices are: C(active), C(present), C(absent), C(deleted)." - default: "present" - service_account_email: - type: str - description: - - service account email - pem_file: - type: path - description: - - path to the pem file associated with the service account email - This option is deprecated. Use 'credentials_file'. - credentials_file: - type: path - description: - - path to the JSON file associated with the service account email - project_id: - type: str - description: - - your GCE project ID - -requirements: - - "python >= 2.6" - - "apache-libcloud >= 0.13.3, >= 0.17.0 if using JSON credentials" -author: "Eric Johnson (@erjohnso) <erjohnso@google.com>" -''' - -EXAMPLES = ''' -- name: Simple example of creating a new LB, adding members, and a health check - local_action: - module: gce_lb - name: testlb - region: us-central1 - members: ["us-central1-a/www-a", "us-central1-b/www-b"] - httphealthcheck_name: hc - httphealthcheck_port: 80 - httphealthcheck_path: "/up" -''' - -try: - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - from libcloud.loadbalancer.types import Provider as Provider_lb - from libcloud.loadbalancer.providers import get_driver as get_driver_lb - from libcloud.common.google import GoogleBaseError, QuotaExceededError, ResourceExistsError, ResourceNotFoundError - - _ = Provider.GCE - HAS_LIBCLOUD = True -except ImportError: - HAS_LIBCLOUD = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gce import USER_AGENT_PRODUCT, USER_AGENT_VERSION, gce_connect, unexpected_error_msg - - -def main(): - module = AnsibleModule( - argument_spec=dict( - httphealthcheck_name=dict(), - httphealthcheck_port=dict(default=80, type='int'), - httphealthcheck_path=dict(default='/'), - httphealthcheck_interval=dict(default=5, type='int'), - httphealthcheck_timeout=dict(default=5, type='int'), - httphealthcheck_unhealthy_count=dict(default=2, type='int'), - httphealthcheck_healthy_count=dict(default=2, type='int'), - httphealthcheck_host=dict(), - name=dict(), - protocol=dict(default='tcp'), - region=dict(), - external_ip=dict(), - port_range=dict(), - members=dict(type='list'), - state=dict(default='present'), - service_account_email=dict(), - pem_file=dict(type='path'), - credentials_file=dict(type='path'), - project_id=dict(), - ) - ) - - if not HAS_LIBCLOUD: - module.fail_json(msg='libcloud with GCE support (0.13.3+) required for this module.') - - gce = gce_connect(module) - - httphealthcheck_name = module.params.get('httphealthcheck_name') - httphealthcheck_port = module.params.get('httphealthcheck_port') - httphealthcheck_path = module.params.get('httphealthcheck_path') - httphealthcheck_interval = module.params.get('httphealthcheck_interval') - httphealthcheck_timeout = module.params.get('httphealthcheck_timeout') - httphealthcheck_unhealthy_count = module.params.get('httphealthcheck_unhealthy_count') - httphealthcheck_healthy_count = module.params.get('httphealthcheck_healthy_count') - httphealthcheck_host = module.params.get('httphealthcheck_host') - name = module.params.get('name') - protocol = module.params.get('protocol') - region = module.params.get('region') - external_ip = module.params.get('external_ip') - port_range = module.params.get('port_range') - members = module.params.get('members') - state = module.params.get('state') - - try: - gcelb = get_driver_lb(Provider_lb.GCE)(gce_driver=gce) - gcelb.connection.user_agent_append("%s/%s" % ( - USER_AGENT_PRODUCT, USER_AGENT_VERSION)) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - changed = False - json_output = {'name': name, 'state': state} - - if not name and not httphealthcheck_name: - module.fail_json(msg='Nothing to do, please specify a "name" ' + 'or "httphealthcheck_name" parameter', changed=False) - - if state in ['active', 'present']: - # first, create the httphealthcheck if requested - hc = None - if httphealthcheck_name: - json_output['httphealthcheck_name'] = httphealthcheck_name - try: - hc = gcelb.ex_create_healthcheck(httphealthcheck_name, - host=httphealthcheck_host, path=httphealthcheck_path, - port=httphealthcheck_port, - interval=httphealthcheck_interval, - timeout=httphealthcheck_timeout, - unhealthy_threshold=httphealthcheck_unhealthy_count, - healthy_threshold=httphealthcheck_healthy_count) - changed = True - except ResourceExistsError: - hc = gce.ex_get_healthcheck(httphealthcheck_name) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - if hc is not None: - json_output['httphealthcheck_host'] = hc.extra['host'] - json_output['httphealthcheck_path'] = hc.path - json_output['httphealthcheck_port'] = hc.port - json_output['httphealthcheck_interval'] = hc.interval - json_output['httphealthcheck_timeout'] = hc.timeout - json_output['httphealthcheck_unhealthy_count'] = hc.unhealthy_threshold - json_output['httphealthcheck_healthy_count'] = hc.healthy_threshold - - # create the forwarding rule (and target pool under the hood) - lb = None - if name: - if not region: - module.fail_json(msg='Missing required region name', - changed=False) - nodes = [] - output_nodes = [] - json_output['name'] = name - # members is a python list of 'zone/inst' strings - if members: - for node in members: - try: - zone, node_name = node.split('/') - nodes.append(gce.ex_get_node(node_name, zone)) - output_nodes.append(node) - except Exception: - # skip nodes that are badly formatted or don't exist - pass - try: - if hc is not None: - lb = gcelb.create_balancer(name, port_range, protocol, - None, nodes, ex_region=region, ex_healthchecks=[hc], - ex_address=external_ip) - else: - lb = gcelb.create_balancer(name, port_range, protocol, - None, nodes, ex_region=region, ex_address=external_ip) - changed = True - except ResourceExistsError: - lb = gcelb.get_balancer(name) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - if lb is not None: - json_output['members'] = output_nodes - json_output['protocol'] = protocol - json_output['region'] = region - json_output['external_ip'] = lb.ip - json_output['port_range'] = lb.port - hc_names = [] - if 'healthchecks' in lb.extra: - for hc in lb.extra['healthchecks']: - hc_names.append(hc.name) - json_output['httphealthchecks'] = hc_names - - if state in ['absent', 'deleted']: - # first, delete the load balancer (forwarding rule and target pool) - # if specified. - if name: - json_output['name'] = name - try: - lb = gcelb.get_balancer(name) - gcelb.destroy_balancer(lb) - changed = True - except ResourceNotFoundError: - pass - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - # destroy the health check if specified - if httphealthcheck_name: - json_output['httphealthcheck_name'] = httphealthcheck_name - try: - hc = gce.ex_get_healthcheck(httphealthcheck_name) - gce.ex_destroy_healthcheck(hc) - changed = True - except ResourceNotFoundError: - pass - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - json_output['changed'] = changed - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gce_mig.py b/ansible_collections/community/google/plugins/modules/gce_mig.py deleted file mode 100644 index fd47167f9..000000000 --- a/ansible_collections/community/google/plugins/modules/gce_mig.py +++ /dev/null @@ -1,904 +0,0 @@ -#!/usr/bin/python -# Copyright 2016 Google Inc. -# 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: gce_mig -short_description: Create, Update or Destroy a Managed Instance Group (MIG). -description: - - Create, Update or Destroy a Managed Instance Group (MIG). See - U(https://cloud.google.com/compute/docs/instance-groups) for an overview. - Full install/configuration instructions for the gce* modules can - be found in the comments of ansible/test/gce_tests.py. -requirements: - - "python >= 2.6" - - "apache-libcloud >= 1.2.0" -notes: - - Resizing and Recreating VM are also supported. - - An existing instance template is required in order to create a - Managed Instance Group. -author: - - "Tom Melendez (@supertom) <tom@supertom.com>" -options: - name: - type: str - description: - - Name of the Managed Instance Group. - required: true - template: - type: str - description: - - Instance Template to be used in creating the VMs. See - U(https://cloud.google.com/compute/docs/instance-templates) to learn more - about Instance Templates. Required for creating MIGs. - size: - type: int - description: - - Size of Managed Instance Group. If MIG already exists, it will be - resized to the number provided here. Required for creating MIGs. - service_account_email: - type: str - description: - - service account email - service_account_permissions: - type: list - description: - - service account permissions - pem_file: - type: path - description: - - path to the pem file associated with the service account email - This option is deprecated. Use 'credentials_file'. - credentials_file: - type: path - description: - - Path to the JSON file associated with the service account email - project_id: - type: str - description: - - GCE project ID - state: - type: str - description: - - desired state of the resource - default: "present" - choices: ["absent", "present"] - zone: - type: str - description: - - The GCE zone to use for this Managed Instance Group. - required: true - autoscaling: - type: dict - description: - - A dictionary of configuration for the autoscaler. 'enabled (bool)', 'name (str)' - and policy.max_instances (int) are required fields if autoscaling is used. See - U(https://cloud.google.com/compute/docs/reference/beta/autoscalers) for more information - on Autoscaling. - named_ports: - type: list - description: - - Define named ports that backend services can forward data to. Format is a a list of - name:port dictionaries. - recreate_instances: - type: bool - default: no - description: - - Recreate MIG instances. -''' - -EXAMPLES = ''' -# Following playbook creates, rebuilds instances, resizes and then deletes a MIG. -# Notes: -# - Two valid Instance Templates must exist in your GCE project in order to run -# this playbook. Change the fields to match the templates used in your -# project. -# - The use of the 'pause' module is not required, it is just for convenience. -- name: Managed Instance Group Example - hosts: localhost - gather_facts: False - tasks: - - name: Create MIG - community.google.gce_mig: - name: ansible-mig-example - zone: us-central1-c - state: present - size: 1 - template: my-instance-template-1 - named_ports: - - name: http - port: 80 - - name: foobar - port: 82 - - - name: Pause for 30 seconds - ansible.builtin.pause: - seconds: 30 - - - name: Recreate MIG Instances with Instance Template change. - community.google.gce_mig: - name: ansible-mig-example - zone: us-central1-c - state: present - template: my-instance-template-2-small - recreate_instances: yes - - - name: Pause for 30 seconds - ansible.builtin.pause: - seconds: 30 - - - name: Resize MIG - community.google.gce_mig: - name: ansible-mig-example - zone: us-central1-c - state: present - size: 3 - - - name: Update MIG with Autoscaler - community.google.gce_mig: - name: ansible-mig-example - zone: us-central1-c - state: present - size: 3 - template: my-instance-template-2-small - recreate_instances: yes - autoscaling: - enabled: yes - name: my-autoscaler - policy: - min_instances: 2 - max_instances: 5 - cool_down_period: 37 - cpu_utilization: - target: .39 - load_balancing_utilization: - target: 0.4 - - - name: Pause for 30 seconds - ansible.builtin.pause: - seconds: 30 - - - name: Delete MIG - community.google.gce_mig: - name: ansible-mig-example - zone: us-central1-c - state: absent - autoscaling: - enabled: no - name: my-autoscaler -''' -RETURN = ''' -zone: - description: Zone in which to launch MIG. - returned: always - type: str - sample: "us-central1-b" - -template: - description: Instance Template to use for VMs. Must exist prior to using with MIG. - returned: changed - type: str - sample: "my-instance-template" - -name: - description: Name of the Managed Instance Group. - returned: changed - type: str - sample: "my-managed-instance-group" - -named_ports: - description: list of named ports acted upon - returned: when named_ports are initially set or updated - type: list - sample: [{ "name": "http", "port": 80 }, { "name": "foo", "port": 82 }] - -size: - description: Number of VMs in Managed Instance Group. - returned: changed - type: int - sample: 4 - -created_instances: - description: Names of instances created. - returned: When instances are created. - type: list - sample: ["ansible-mig-new-0k4y", "ansible-mig-new-0zk5", "ansible-mig-new-kp68"] - -deleted_instances: - description: Names of instances deleted. - returned: When instances are deleted. - type: list - sample: ["ansible-mig-new-0k4y", "ansible-mig-new-0zk5", "ansible-mig-new-kp68"] - -resize_created_instances: - description: Names of instances created during resizing. - returned: When a resize results in the creation of instances. - type: list - sample: ["ansible-mig-new-0k4y", "ansible-mig-new-0zk5", "ansible-mig-new-kp68"] - -resize_deleted_instances: - description: Names of instances deleted during resizing. - returned: When a resize results in the deletion of instances. - type: list - sample: ["ansible-mig-new-0k4y", "ansible-mig-new-0zk5", "ansible-mig-new-kp68"] - -recreated_instances: - description: Names of instances recreated. - returned: When instances are recreated. - type: list - sample: ["ansible-mig-new-0k4y", "ansible-mig-new-0zk5", "ansible-mig-new-kp68"] - -created_autoscaler: - description: True if Autoscaler was attempted and created. False otherwise. - returned: When the creation of an Autoscaler was attempted. - type: bool - sample: true - -updated_autoscaler: - description: True if an Autoscaler update was attempted and succeeded. - False returned if update failed. - returned: When the update of an Autoscaler was attempted. - type: bool - sample: true - -deleted_autoscaler: - description: True if an Autoscaler delete attempted and succeeded. - False returned if delete failed. - returned: When the delete of an Autoscaler was attempted. - type: bool - sample: true - -set_named_ports: - description: True if the named_ports have been set - returned: named_ports have been set - type: bool - sample: true - -updated_named_ports: - description: True if the named_ports have been updated - returned: named_ports have been updated - type: bool - sample: true -''' - -try: - from ast import literal_eval - HAS_PYTHON26 = True -except ImportError: - HAS_PYTHON26 = False - -try: - import libcloud - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - from libcloud.common.google import GoogleBaseError, QuotaExceededError, \ - ResourceExistsError, ResourceInUseError, ResourceNotFoundError - from libcloud.compute.drivers.gce import GCEAddress - _ = Provider.GCE - HAS_LIBCLOUD = True -except ImportError: - HAS_LIBCLOUD = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gce import gce_connect - - -def _check_params(params, field_list): - """ - Helper to validate params. - - Use this in function definitions if they require specific fields - to be present. - - :param params: structure that contains the fields - :type params: ``dict`` - - :param field_list: list of dict representing the fields - [{'name': str, 'required': True/False', 'type': cls}] - :type field_list: ``list`` of ``dict`` - - :return True, exits otherwise - :rtype: ``bool`` - """ - for d in field_list: - if not d['name'] in params: - if d['required'] is True: - return (False, "%s is required and must be of type: %s" % - (d['name'], str(d['type']))) - else: - if not isinstance(params[d['name']], d['type']): - return (False, - "%s must be of type: %s" % (d['name'], str(d['type']))) - - return (True, '') - - -def _validate_autoscaling_params(params): - """ - Validate that the minimum configuration is present for autoscaling. - - :param params: Ansible dictionary containing autoscaling configuration - It is expected that autoscaling config will be found at the - key 'autoscaling'. - :type params: ``dict`` - - :return: Tuple containing a boolean and a string. True if autoscaler - is valid, False otherwise, plus str for message. - :rtype: ``(``bool``, ``str``)`` - """ - if not params['autoscaling']: - # It's optional, so if not set at all, it's valid. - return (True, '') - if not isinstance(params['autoscaling'], dict): - return (False, - 'autoscaling: configuration expected to be a dictionary.') - - # check first-level required fields - as_req_fields = [ - {'name': 'name', 'required': True, 'type': str}, - {'name': 'enabled', 'required': True, 'type': bool}, - {'name': 'policy', 'required': True, 'type': dict} - ] # yapf: disable - - (as_req_valid, as_req_msg) = _check_params(params['autoscaling'], - as_req_fields) - if not as_req_valid: - return (False, as_req_msg) - - # check policy configuration - as_policy_fields = [ - {'name': 'max_instances', 'required': True, 'type': int}, - {'name': 'min_instances', 'required': False, 'type': int}, - {'name': 'cool_down_period', 'required': False, 'type': int} - ] # yapf: disable - - (as_policy_valid, as_policy_msg) = _check_params( - params['autoscaling']['policy'], as_policy_fields) - if not as_policy_valid: - return (False, as_policy_msg) - - # TODO(supertom): check utilization fields - - return (True, '') - - -def _validate_named_port_params(params): - """ - Validate the named ports parameters - - :param params: Ansible dictionary containing named_ports configuration - It is expected that autoscaling config will be found at the - key 'named_ports'. That key should contain a list of - {name : port} dictionaries. - :type params: ``dict`` - - :return: Tuple containing a boolean and a string. True if params - are valid, False otherwise, plus str for message. - :rtype: ``(``bool``, ``str``)`` - """ - if not params['named_ports']: - # It's optional, so if not set at all, it's valid. - return (True, '') - if not isinstance(params['named_ports'], list): - return (False, 'named_ports: expected list of name:port dictionaries.') - req_fields = [ - {'name': 'name', 'required': True, 'type': str}, - {'name': 'port', 'required': True, 'type': int} - ] # yapf: disable - - for np in params['named_ports']: - (valid_named_ports, np_msg) = _check_params(np, req_fields) - if not valid_named_ports: - return (False, np_msg) - - return (True, '') - - -def _get_instance_list(mig, field='name', filter_list=None): - """ - Helper to grab field from instances response. - - :param mig: Managed Instance Group Object from libcloud. - :type mig: :class: `GCEInstanceGroupManager` - - :param field: Field name in list_managed_instances response. Defaults - to 'name'. - :type field: ``str`` - - :param filter_list: list of 'currentAction' strings to filter on. Only - items that match a currentAction in this list will - be returned. Default is "['NONE']". - :type filter_list: ``list`` of ``str`` - - :return: List of strings from list_managed_instances response. - :rtype: ``list`` - """ - filter_list = ['NONE'] if filter_list is None else filter_list - - return [x[field] for x in mig.list_managed_instances() - if x['currentAction'] in filter_list] - - -def _gen_gce_as_policy(as_params): - """ - Take Autoscaler params and generate GCE-compatible policy. - - :param as_params: Dictionary in Ansible-playbook format - containing policy arguments. - :type as_params: ``dict`` - - :return: GCE-compatible policy dictionary - :rtype: ``dict`` - """ - asp_data = {} - asp_data['maxNumReplicas'] = as_params['max_instances'] - if 'min_instances' in as_params: - asp_data['minNumReplicas'] = as_params['min_instances'] - if 'cool_down_period' in as_params: - asp_data['coolDownPeriodSec'] = as_params['cool_down_period'] - if 'cpu_utilization' in as_params and 'target' in as_params[ - 'cpu_utilization']: - asp_data['cpuUtilization'] = {'utilizationTarget': - as_params['cpu_utilization']['target']} - if 'load_balancing_utilization' in as_params and 'target' in as_params[ - 'load_balancing_utilization']: - asp_data['loadBalancingUtilization'] = { - 'utilizationTarget': - as_params['load_balancing_utilization']['target'] - } - - return asp_data - - -def create_autoscaler(gce, mig, params): - """ - Create a new Autoscaler for a MIG. - - :param gce: An initialized GCE driver object. - :type gce: :class: `GCENodeDriver` - - :param mig: An initialized GCEInstanceGroupManager. - :type mig: :class: `GCEInstanceGroupManager` - - :param params: Dictionary of autoscaling parameters. - :type params: ``dict`` - - :return: Tuple with changed stats. - :rtype: tuple in the format of (bool, list) - """ - changed = False - as_policy = _gen_gce_as_policy(params['policy']) - autoscaler = gce.ex_create_autoscaler(name=params['name'], zone=mig.zone, - instance_group=mig, policy=as_policy) - if autoscaler: - changed = True - return changed - - -def update_autoscaler(gce, autoscaler, params): - """ - Update an Autoscaler. - - Takes an existing Autoscaler object, and updates it with - the supplied params before calling libcloud's update method. - - :param gce: An initialized GCE driver object. - :type gce: :class: `GCENodeDriver` - - :param autoscaler: An initialized GCEAutoscaler. - :type autoscaler: :class: `GCEAutoscaler` - - :param params: Dictionary of autoscaling parameters. - :type params: ``dict`` - - :return: True if changes, False otherwise. - :rtype: ``bool`` - """ - as_policy = _gen_gce_as_policy(params['policy']) - if autoscaler.policy != as_policy: - autoscaler.policy = as_policy - autoscaler = gce.ex_update_autoscaler(autoscaler) - if autoscaler: - return True - return False - - -def delete_autoscaler(autoscaler): - """ - Delete an Autoscaler. Does not affect MIG. - - :param mig: Managed Instance Group Object from Libcloud. - :type mig: :class: `GCEInstanceGroupManager` - - :return: Tuple with changed stats and a list of affected instances. - :rtype: tuple in the format of (bool, list) - """ - changed = False - if autoscaler.destroy(): - changed = True - return changed - - -def get_autoscaler(gce, name, zone): - """ - Get an Autoscaler from GCE. - - If the Autoscaler is not found, None is found. - - :param gce: An initialized GCE driver object. - :type gce: :class: `GCENodeDriver` - - :param name: Name of the Autoscaler. - :type name: ``str`` - - :param zone: Zone that the Autoscaler is located in. - :type zone: ``str`` - - :return: A GCEAutoscaler object or None. - :rtype: :class: `GCEAutoscaler` or None - """ - try: - # Does the Autoscaler already exist? - return gce.ex_get_autoscaler(name, zone) - - except ResourceNotFoundError: - return None - - -def create_mig(gce, params): - """ - Create a new Managed Instance Group. - - :param gce: An initialized GCE driver object. - :type gce: :class: `GCENodeDriver` - - :param params: Dictionary of parameters needed by the module. - :type params: ``dict`` - - :return: Tuple with changed stats and a list of affected instances. - :rtype: tuple in the format of (bool, list) - """ - - changed = False - return_data = [] - actions_filter = ['CREATING'] - - mig = gce.ex_create_instancegroupmanager( - name=params['name'], size=params['size'], template=params['template'], - zone=params['zone']) - - if mig: - changed = True - return_data = _get_instance_list(mig, filter_list=actions_filter) - - return (changed, return_data) - - -def delete_mig(mig): - """ - Delete a Managed Instance Group. All VMs in that MIG are also deleted." - - :param mig: Managed Instance Group Object from Libcloud. - :type mig: :class: `GCEInstanceGroupManager` - - :return: Tuple with changed stats and a list of affected instances. - :rtype: tuple in the format of (bool, list) - """ - changed = False - return_data = [] - actions_filter = ['NONE', 'CREATING', 'RECREATING', 'DELETING', - 'ABANDONING', 'RESTARTING', 'REFRESHING'] - instance_names = _get_instance_list(mig, filter_list=actions_filter) - if mig.destroy(): - changed = True - return_data = instance_names - - return (changed, return_data) - - -def recreate_instances_in_mig(mig): - """ - Recreate the instances for a Managed Instance Group. - - :param mig: Managed Instance Group Object from libcloud. - :type mig: :class: `GCEInstanceGroupManager` - - :return: Tuple with changed stats and a list of affected instances. - :rtype: tuple in the format of (bool, list) - """ - changed = False - return_data = [] - actions_filter = ['RECREATING'] - - if mig.recreate_instances(): - changed = True - return_data = _get_instance_list(mig, filter_list=actions_filter) - - return (changed, return_data) - - -def resize_mig(mig, size): - """ - Resize a Managed Instance Group. - - Based on the size provided, GCE will automatically create and delete - VMs as needed. - - :param mig: Managed Instance Group Object from libcloud. - :type mig: :class: `GCEInstanceGroupManager` - - :return: Tuple with changed stats and a list of affected instances. - :rtype: tuple in the format of (bool, list) - """ - changed = False - return_data = [] - actions_filter = ['CREATING', 'DELETING'] - - if mig.resize(size): - changed = True - return_data = _get_instance_list(mig, filter_list=actions_filter) - - return (changed, return_data) - - -def get_mig(gce, name, zone): - """ - Get a Managed Instance Group from GCE. - - If the MIG is not found, None is found. - - :param gce: An initialized GCE driver object. - :type gce: :class: `GCENodeDriver` - - :param name: Name of the Managed Instance Group. - :type name: ``str`` - - :param zone: Zone that the Managed Instance Group is located in. - :type zone: ``str`` - - :return: A GCEInstanceGroupManager object or None. - :rtype: :class: `GCEInstanceGroupManager` or None - """ - try: - # Does the MIG already exist? - return gce.ex_get_instancegroupmanager(name=name, zone=zone) - - except ResourceNotFoundError: - return None - - -def update_named_ports(mig, named_ports): - """ - Set the named ports on a Managed Instance Group. - - Sort the existing named ports and new. If different, update. - This also implicitly allows for the removal of named_por - - :param mig: Managed Instance Group Object from libcloud. - :type mig: :class: `GCEInstanceGroupManager` - - :param named_ports: list of dictionaries in the format of {'name': port} - :type named_ports: ``list`` of ``dict`` - - :return: True if successful - :rtype: ``bool`` - """ - changed = False - existing_ports = [] - new_ports = [] - if hasattr(mig.instance_group, 'named_ports'): - existing_ports = sorted(mig.instance_group.named_ports, - key=lambda x: x['name']) - if named_ports is not None: - new_ports = sorted(named_ports, key=lambda x: x['name']) - - if existing_ports != new_ports: - if mig.instance_group.set_named_ports(named_ports): - changed = True - - return changed - - -def main(): - module = AnsibleModule(argument_spec=dict( - name=dict(required=True), - template=dict(), - recreate_instances=dict(type='bool', default=False), - # Do not set a default size here. For Create and some update - # operations, it is required and should be explicitly set. - # Below, we set it to the existing value if it has not been set. - size=dict(type='int'), - state=dict(choices=['absent', 'present'], default='present'), - zone=dict(required=True), - autoscaling=dict(type='dict', default=None), - named_ports=dict(type='list', default=None), - service_account_email=dict(), - service_account_permissions=dict(type='list'), - pem_file=dict(type='path'), - credentials_file=dict(type='path'), - project_id=dict(), ), ) - - if not HAS_PYTHON26: - module.fail_json( - msg="GCE module requires python's 'ast' module, python v2.6+") - if not HAS_LIBCLOUD: - module.fail_json( - msg='libcloud with GCE Managed Instance Group support (1.2+) required for this module.') - - gce = gce_connect(module) - if not hasattr(gce, 'ex_create_instancegroupmanager'): - module.fail_json( - msg='libcloud with GCE Managed Instance Group support (1.2+) required for this module.', - changed=False) - - params = {} - params['state'] = module.params.get('state') - params['zone'] = module.params.get('zone') - params['name'] = module.params.get('name') - params['size'] = module.params.get('size') - params['template'] = module.params.get('template') - params['recreate_instances'] = module.params.get('recreate_instances') - params['autoscaling'] = module.params.get('autoscaling', None) - params['named_ports'] = module.params.get('named_ports', None) - - (valid_autoscaling, as_msg) = _validate_autoscaling_params(params) - if not valid_autoscaling: - module.fail_json(msg=as_msg, changed=False) - - if params['named_ports'] is not None and not hasattr( - gce, 'ex_instancegroup_set_named_ports'): - module.fail_json( - msg="Apache Libcloud 1.3.0+ is required to use 'named_ports' option", - changed=False) - - (valid_named_ports, np_msg) = _validate_named_port_params(params) - if not valid_named_ports: - module.fail_json(msg=np_msg, changed=False) - - changed = False - json_output = {'state': params['state'], 'zone': params['zone']} - mig = get_mig(gce, params['name'], params['zone']) - - if not mig: - if params['state'] == 'absent': - # Doesn't exist in GCE, and state==absent. - changed = False - module.fail_json( - msg="Cannot delete unknown managed instance group: %s" % - (params['name'])) - else: - # Create MIG - req_create_fields = [ - {'name': 'template', 'required': True, 'type': str}, - {'name': 'size', 'required': True, 'type': int} - ] # yapf: disable - - (valid_create_fields, valid_create_msg) = _check_params( - params, req_create_fields) - if not valid_create_fields: - module.fail_json(msg=valid_create_msg, changed=False) - - (changed, json_output['created_instances']) = create_mig(gce, - params) - if params['autoscaling'] and params['autoscaling'][ - 'enabled'] is True: - # Fetch newly-created MIG and create Autoscaler for it. - mig = get_mig(gce, params['name'], params['zone']) - if not mig: - module.fail_json( - msg='Unable to fetch created MIG %s to create \ - autoscaler in zone: %s' % ( - params['name'], params['zone']), changed=False) - - if not create_autoscaler(gce, mig, params['autoscaling']): - module.fail_json( - msg='Unable to fetch MIG %s to create autoscaler \ - in zone: %s' % (params['name'], params['zone']), - changed=False) - - json_output['created_autoscaler'] = True - # Add named ports if available - if params['named_ports']: - mig = get_mig(gce, params['name'], params['zone']) - if not mig: - module.fail_json( - msg='Unable to fetch created MIG %s to create \ - autoscaler in zone: %s' % ( - params['name'], params['zone']), changed=False) - json_output['set_named_ports'] = update_named_ports( - mig, params['named_ports']) - if json_output['set_named_ports']: - json_output['named_ports'] = params['named_ports'] - - elif params['state'] == 'absent': - # Delete MIG - - # First, check and remove the autoscaler, if present. - # Note: multiple autoscalers can be associated to a single MIG. We - # only handle the one that is named, but we might want to think about this. - if params['autoscaling']: - autoscaler = get_autoscaler(gce, params['autoscaling']['name'], - params['zone']) - if not autoscaler: - module.fail_json(msg='Unable to fetch autoscaler %s to delete \ - in zone: %s' % (params['autoscaling']['name'], params['zone']), - changed=False) - - changed = delete_autoscaler(autoscaler) - json_output['deleted_autoscaler'] = changed - - # Now, delete the MIG. - (changed, json_output['deleted_instances']) = delete_mig(mig) - - else: - # Update MIG - - # If we're going to update a MIG, we need a size and template values. - # If not specified, we use the values from the existing MIG. - if not params['size']: - params['size'] = mig.size - - if not params['template']: - params['template'] = mig.template.name - - if params['template'] != mig.template.name: - # Update Instance Template. - new_template = gce.ex_get_instancetemplate(params['template']) - mig.set_instancetemplate(new_template) - json_output['updated_instancetemplate'] = True - changed = True - if params['recreate_instances'] is True: - # Recreate Instances. - (changed, json_output['recreated_instances'] - ) = recreate_instances_in_mig(mig) - - if params['size'] != mig.size: - # Resize MIG. - keystr = 'created' if params['size'] > mig.size else 'deleted' - (changed, json_output['resize_%s_instances' % - (keystr)]) = resize_mig(mig, params['size']) - - # Update Autoscaler - if params['autoscaling']: - autoscaler = get_autoscaler(gce, params['autoscaling']['name'], - params['zone']) - if not autoscaler: - # Try to create autoscaler. - # Note: this isn't perfect, if the autoscaler name has changed - # we wouldn't know that here. - if not create_autoscaler(gce, mig, params['autoscaling']): - module.fail_json( - msg='Unable to create autoscaler %s for existing MIG %s\ - in zone: %s' % (params['autoscaling']['name'], - params['name'], params['zone']), - changed=False) - json_output['created_autoscaler'] = True - changed = True - else: - if params['autoscaling']['enabled'] is False: - # Delete autoscaler - changed = delete_autoscaler(autoscaler) - json_output['delete_autoscaler'] = changed - else: - # Update policy, etc. - changed = update_autoscaler(gce, autoscaler, - params['autoscaling']) - json_output['updated_autoscaler'] = changed - named_ports = params['named_ports'] or [] - json_output['updated_named_ports'] = update_named_ports(mig, - named_ports) - if json_output['updated_named_ports']: - json_output['named_ports'] = named_ports - - json_output['changed'] = changed - json_output.update(params) - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gce_net.py b/ansible_collections/community/google/plugins/modules/gce_net.py deleted file mode 100644 index 48395f2a9..000000000 --- a/ansible_collections/community/google/plugins/modules/gce_net.py +++ /dev/null @@ -1,511 +0,0 @@ -#!/usr/bin/python -# Copyright 2013 Google Inc. -# 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: gce_net -short_description: create/destroy GCE networks and firewall rules -description: - - This module can create and destroy Google Compute Engine networks and - firewall rules U(https://cloud.google.com/compute/docs/networking). - The I(name) parameter is reserved for referencing a network while the - I(fwname) parameter is used to reference firewall rules. - IPv4 Address ranges must be specified using the CIDR - U(http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) format. - Full install/configuration instructions for the gce* modules can - be found in the comments of ansible/test/gce_tests.py. -options: - allowed: - type: str - description: - - the protocol:ports to allow (I(tcp:80) or I(tcp:80,443) or I(tcp:80-800;udp:1-25)) - this parameter is mandatory when creating or updating a firewall rule - ipv4_range: - type: str - description: - - the IPv4 address range in CIDR notation for the network - this parameter is not mandatory when you specified existing network in name parameter, - but when you create new network, this parameter is mandatory - fwname: - type: str - description: - - name of the firewall rule - name: - type: str - description: - - name of the network - src_range: - type: list - description: - - the source IPv4 address range in CIDR notation - default: [] - src_tags: - type: list - description: - - the source instance tags for creating a firewall rule - default: [] - target_tags: - type: list - description: - - the target instance tags for creating a firewall rule - default: [] - state: - type: str - description: - - desired state of the network or firewall - - "Available choices are: C(active), C(present), C(absent), C(deleted)." - default: "present" - service_account_email: - type: str - description: - - service account email - pem_file: - type: path - description: - - path to the pem file associated with the service account email - This option is deprecated. Use C(credentials_file). - credentials_file: - type: path - description: - - path to the JSON file associated with the service account email - project_id: - type: str - description: - - your GCE project ID - mode: - type: str - description: - - network mode for Google Cloud - C(legacy) indicates a network with an IP address range; - C(auto) automatically generates subnetworks in different regions; - C(custom) uses networks to group subnets of user specified IP address ranges - https://cloud.google.com/compute/docs/networking#network_types - default: "legacy" - choices: ["legacy", "auto", "custom"] - subnet_name: - type: str - description: - - name of subnet to create - subnet_region: - type: str - description: - - region of subnet to create - subnet_desc: - type: str - description: - - description of subnet to create - -requirements: - - "python >= 2.6" - - "apache-libcloud >= 0.13.3, >= 0.17.0 if using JSON credentials" -author: "Eric Johnson (@erjohnso) <erjohnso@google.com>, Tom Melendez (@supertom) <supertom@google.com>" -''' - -EXAMPLES = ''' -# Create a 'legacy' Network -- name: Create Legacy Network - community.google.gce_net: - name: legacynet - ipv4_range: '10.24.17.0/24' - mode: legacy - state: present - -# Create an 'auto' Network -- name: Create Auto Network - community.google.gce_net: - name: autonet - mode: auto - state: present - -# Create a 'custom' Network -- name: Create Custom Network - community.google.gce_net: - name: customnet - mode: custom - subnet_name: "customsubnet" - subnet_region: us-east1 - ipv4_range: '10.240.16.0/24' - state: "present" - -# Create Firewall Rule with Source Tags -- name: Create Firewall Rule w/Source Tags - community.google.gce_net: - name: default - fwname: "my-firewall-rule" - allowed: tcp:80 - state: "present" - src_tags: "foo,bar" - -# Create Firewall Rule with Source Range -- name: Create Firewall Rule w/Source Range - community.google.gce_net: - name: default - fwname: "my-firewall-rule" - allowed: tcp:80 - state: "present" - src_range: ['10.1.1.1/32'] - -# Create Custom Subnetwork -- name: Create Custom Subnetwork - community.google.gce_net: - name: privatenet - mode: custom - subnet_name: subnet_example - subnet_region: us-central1 - ipv4_range: '10.0.0.0/16' -''' - -RETURN = ''' -allowed: - description: Rules (ports and protocols) specified by this firewall rule. - returned: When specified - type: str - sample: "tcp:80;icmp" - -fwname: - description: Name of the firewall rule. - returned: When specified - type: str - sample: "my-fwname" - -ipv4_range: - description: IPv4 range of the specified network or subnetwork. - returned: when specified or when a subnetwork is created - type: str - sample: "10.0.0.0/16" - -name: - description: Name of the network. - returned: always - type: str - sample: "my-network" - -src_range: - description: IP address blocks a firewall rule applies to. - returned: when specified - type: list - sample: [ '10.1.1.12/8' ] - -src_tags: - description: Instance Tags firewall rule applies to. - returned: when specified while creating a firewall rule - type: list - sample: [ 'foo', 'bar' ] - -state: - description: State of the item operated on. - returned: always - type: str - sample: "present" - -subnet_name: - description: Name of the subnetwork. - returned: when specified or when a subnetwork is created - type: str - sample: "my-subnetwork" - -subnet_region: - description: Region of the specified subnet. - returned: when specified or when a subnetwork is created - type: str - sample: "us-east1" - -target_tags: - description: Instance Tags with these tags receive traffic allowed by firewall rule. - returned: when specified while creating a firewall rule - type: list - sample: [ 'foo', 'bar' ] -''' -try: - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - from libcloud.common.google import GoogleBaseError, QuotaExceededError, ResourceExistsError, ResourceNotFoundError - _ = Provider.GCE - HAS_LIBCLOUD = True -except ImportError: - HAS_LIBCLOUD = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gce import gce_connect, unexpected_error_msg - - -def format_allowed_section(allowed): - """Format each section of the allowed list""" - if allowed.count(":") == 0: - protocol = allowed - ports = [] - elif allowed.count(":") == 1: - protocol, ports = allowed.split(":") - else: - return [] - if ports.count(","): - ports = ports.split(",") - elif ports: - ports = [ports] - return_val = {"IPProtocol": protocol} - if ports: - return_val["ports"] = ports - return return_val - - -def format_allowed(allowed): - """Format the 'allowed' value so that it is GCE compatible.""" - return_value = [] - if allowed.count(";") == 0: - return [format_allowed_section(allowed)] - else: - sections = allowed.split(";") - for section in sections: - return_value.append(format_allowed_section(section)) - return return_value - - -def sorted_allowed_list(allowed_list): - """Sort allowed_list (output of format_allowed) by protocol and port.""" - # sort by protocol - allowed_by_protocol = sorted(allowed_list, key=lambda x: x['IPProtocol']) - # sort the ports list - return sorted(allowed_by_protocol, key=lambda y: sorted(y.get('ports', []))) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - allowed=dict(), - ipv4_range=dict(), - fwname=dict(), - name=dict(), - src_range=dict(default=[], type='list'), - src_tags=dict(default=[], type='list'), - target_tags=dict(default=[], type='list'), - state=dict(default='present'), - service_account_email=dict(), - pem_file=dict(type='path'), - credentials_file=dict(type='path'), - project_id=dict(), - mode=dict(default='legacy', choices=['legacy', 'auto', 'custom']), - subnet_name=dict(), - subnet_region=dict(), - subnet_desc=dict(), - ) - ) - - if not HAS_LIBCLOUD: - module.fail_json(msg='libcloud with GCE support (0.17.0+) required for this module') - - gce = gce_connect(module) - - allowed = module.params.get('allowed') - ipv4_range = module.params.get('ipv4_range') - fwname = module.params.get('fwname') - name = module.params.get('name') - src_range = module.params.get('src_range') - src_tags = module.params.get('src_tags') - target_tags = module.params.get('target_tags') - state = module.params.get('state') - mode = module.params.get('mode') - subnet_name = module.params.get('subnet_name') - subnet_region = module.params.get('subnet_region') - subnet_desc = module.params.get('subnet_desc') - - changed = False - json_output = {'state': state} - - if state in ['active', 'present']: - network = None - subnet = None - try: - network = gce.ex_get_network(name) - json_output['name'] = name - if mode == 'legacy': - json_output['ipv4_range'] = network.cidr - if network and mode == 'custom' and subnet_name: - if not hasattr(gce, 'ex_get_subnetwork'): - module.fail_json(msg="Update libcloud to a more recent version (>1.0) that supports network 'mode' parameter", changed=False) - - subnet = gce.ex_get_subnetwork(subnet_name, region=subnet_region) - json_output['subnet_name'] = subnet_name - json_output['ipv4_range'] = subnet.cidr - except ResourceNotFoundError: - pass - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - # user wants to create a new network that doesn't yet exist - if name and not network: - if not ipv4_range and mode != 'auto': - module.fail_json(msg="Network '" + name + "' is not found. To create network in legacy or custom mode, 'ipv4_range' parameter is required", - changed=False) - args = [ipv4_range if mode == 'legacy' else None] - kwargs = {} - if mode != 'legacy': - kwargs['mode'] = mode - - try: - network = gce.ex_create_network(name, *args, **kwargs) - json_output['name'] = name - json_output['ipv4_range'] = ipv4_range - changed = True - except TypeError: - module.fail_json(msg="Update libcloud to a more recent version (>1.0) that supports network 'mode' parameter", changed=False) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - if (subnet_name or ipv4_range) and not subnet and mode == 'custom': - if not hasattr(gce, 'ex_create_subnetwork'): - module.fail_json(msg='Update libcloud to a more recent version (>1.0) that supports subnetwork creation', changed=changed) - if not subnet_name or not ipv4_range or not subnet_region: - module.fail_json(msg="subnet_name, ipv4_range, and subnet_region required for custom mode", changed=changed) - - try: - subnet = gce.ex_create_subnetwork(subnet_name, cidr=ipv4_range, network=name, region=subnet_region, description=subnet_desc) - json_output['subnet_name'] = subnet_name - json_output['ipv4_range'] = ipv4_range - changed = True - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=changed) - - if fwname: - # user creating a firewall rule - if not allowed and not src_range and not src_tags: - if changed and network: - module.fail_json( - msg="Network created, but missing required " + "firewall rule parameter(s)", changed=True) - module.fail_json( - msg="Missing required firewall rule parameter(s)", - changed=False) - - allowed_list = format_allowed(allowed) - - # Fetch existing rule and if it exists, compare attributes - # update if attributes changed. Create if doesn't exist. - try: - fw_changed = False - fw = gce.ex_get_firewall(fwname) - - # If old and new attributes are different, we update the firewall rule. - # This implicitly lets us clear out attributes as well. - # allowed_list is required and must not be None for firewall rules. - if allowed_list and (sorted_allowed_list(allowed_list) != sorted_allowed_list(fw.allowed)): - fw.allowed = allowed_list - fw_changed = True - - # source_ranges might not be set in the project; cast it to an empty list - fw.source_ranges = fw.source_ranges or [] - - # If these attributes are lists, we sort them first, then compare. - # Otherwise, we update if they differ. - if fw.source_ranges != src_range: - if isinstance(src_range, list): - if sorted(fw.source_ranges) != sorted(src_range): - fw.source_ranges = src_range - fw_changed = True - else: - fw.source_ranges = src_range - fw_changed = True - - # source_tags might not be set in the project; cast it to an empty list - fw.source_tags = fw.source_tags or [] - - if fw.source_tags != src_tags: - if isinstance(src_tags, list): - if sorted(fw.source_tags) != sorted(src_tags): - fw.source_tags = src_tags - fw_changed = True - else: - fw.source_tags = src_tags - fw_changed = True - - # target_tags might not be set in the project; cast it to an empty list - fw.target_tags = fw.target_tags or [] - - if fw.target_tags != target_tags: - if isinstance(target_tags, list): - if sorted(fw.target_tags) != sorted(target_tags): - fw.target_tags = target_tags - fw_changed = True - else: - fw.target_tags = target_tags - fw_changed = True - - if fw_changed is True: - try: - gce.ex_update_firewall(fw) - changed = True - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - # Firewall rule not found so we try to create it. - except ResourceNotFoundError: - try: - gce.ex_create_firewall(fwname, allowed_list, network=name, - source_ranges=src_range, source_tags=src_tags, target_tags=target_tags) - changed = True - - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - json_output['fwname'] = fwname - json_output['allowed'] = allowed - json_output['src_range'] = src_range - json_output['src_tags'] = src_tags - json_output['target_tags'] = target_tags - - if state in ['absent', 'deleted']: - if fwname: - json_output['fwname'] = fwname - fw = None - try: - fw = gce.ex_get_firewall(fwname) - except ResourceNotFoundError: - pass - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - if fw: - gce.ex_destroy_firewall(fw) - changed = True - elif subnet_name: - if not hasattr(gce, 'ex_get_subnetwork') or not hasattr(gce, 'ex_destroy_subnetwork'): - module.fail_json(msg='Update libcloud to a more recent version (>1.0) that supports subnetwork creation', changed=changed) - json_output['name'] = subnet_name - subnet = None - try: - subnet = gce.ex_get_subnetwork(subnet_name, region=subnet_region) - except ResourceNotFoundError: - pass - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - if subnet: - gce.ex_destroy_subnetwork(subnet) - changed = True - elif name: - json_output['name'] = name - network = None - try: - network = gce.ex_get_network(name) - - except ResourceNotFoundError: - pass - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - if network: - try: - gce.ex_destroy_network(network) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - changed = True - - json_output['changed'] = changed - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gce_pd.py b/ansible_collections/community/google/plugins/modules/gce_pd.py deleted file mode 100644 index 0e3093d8b..000000000 --- a/ansible_collections/community/google/plugins/modules/gce_pd.py +++ /dev/null @@ -1,293 +0,0 @@ -#!/usr/bin/python -# Copyright 2013 Google Inc. -# 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: gce_pd -short_description: utilize GCE persistent disk resources -description: - - This module can create and destroy unformatted GCE persistent disks - U(https://developers.google.com/compute/docs/disks#persistentdisks). - It also supports attaching and detaching disks from running instances. - Full install/configuration instructions for the gce* modules can - be found in the comments of ansible/test/gce_tests.py. -options: - detach_only: - description: - - do not destroy the disk, merely detach it from an instance - type: bool - instance_name: - type: str - description: - - instance name if you wish to attach or detach the disk - mode: - type: str - description: - - GCE mount mode of disk, READ_ONLY (default) or READ_WRITE - default: "READ_ONLY" - choices: ["READ_WRITE", "READ_ONLY"] - name: - type: str - description: - - name of the disk - required: true - size_gb: - type: str - description: - - whole integer size of disk (in GB) to create, default is 10 GB - default: "10" - image: - type: str - description: - - the source image to use for the disk - snapshot: - type: str - description: - - the source snapshot to use for the disk - state: - type: str - description: - - desired state of the persistent disk - - "Available choices are: C(active), C(present), C(absent), C(deleted)." - default: "present" - zone: - type: str - description: - - zone in which to create the disk - default: "us-central1-b" - service_account_email: - type: str - description: - - service account email - pem_file: - type: path - description: - - path to the pem file associated with the service account email - This option is deprecated. Use 'credentials_file'. - credentials_file: - type: path - description: - - path to the JSON file associated with the service account email - project_id: - type: str - description: - - your GCE project ID - disk_type: - type: str - description: - - Specify a C(pd-standard) disk or C(pd-ssd) for an SSD disk. - default: "pd-standard" - delete_on_termination: - description: - - If C(yes), deletes the volume when instance is terminated - type: bool - image_family: - type: str - description: - - The image family to use to create the instance. - If I(image) has been used I(image_family) is ignored. - Cannot specify both I(image) and I(source). - external_projects: - type: list - description: - - A list of other projects (accessible with the provisioning credentials) - to be searched for the image. - -requirements: - - "python >= 2.6" - - "apache-libcloud >= 0.13.3, >= 0.17.0 if using JSON credentials" -author: "Eric Johnson (@erjohnso) <erjohnso@google.com>" -''' - -EXAMPLES = ''' -- name: Simple attachment action to an existing instance - local_action: - module: gce_pd - instance_name: notlocalhost - size_gb: 5 - name: pd -''' - -try: - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - from libcloud.common.google import GoogleBaseError, QuotaExceededError, ResourceExistsError, ResourceNotFoundError, ResourceInUseError - _ = Provider.GCE - HAS_LIBCLOUD = True -except ImportError: - HAS_LIBCLOUD = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gce import gce_connect, unexpected_error_msg - - -def main(): - module = AnsibleModule( - argument_spec=dict( - delete_on_termination=dict(type='bool'), - detach_only=dict(type='bool'), - instance_name=dict(), - mode=dict(default='READ_ONLY', choices=['READ_WRITE', 'READ_ONLY']), - name=dict(required=True), - size_gb=dict(default=10), - disk_type=dict(default='pd-standard'), - image=dict(), - image_family=dict(), - external_projects=dict(type='list'), - snapshot=dict(), - state=dict(default='present'), - zone=dict(default='us-central1-b'), - service_account_email=dict(), - pem_file=dict(type='path'), - credentials_file=dict(type='path'), - project_id=dict(), - ) - ) - if not HAS_LIBCLOUD: - module.fail_json(msg='libcloud with GCE support (0.17.0+) is required for this module') - - gce = gce_connect(module) - - delete_on_termination = module.params.get('delete_on_termination') - detach_only = module.params.get('detach_only') - instance_name = module.params.get('instance_name') - mode = module.params.get('mode') - name = module.params.get('name') - size_gb = module.params.get('size_gb') - disk_type = module.params.get('disk_type') - image = module.params.get('image') - image_family = module.params.get('image_family') - external_projects = module.params.get('external_projects') - snapshot = module.params.get('snapshot') - state = module.params.get('state') - zone = module.params.get('zone') - - if delete_on_termination and not instance_name: - module.fail_json( - msg='Must specify an instance name when requesting delete on termination', - changed=False) - - if detach_only and not instance_name: - module.fail_json( - msg='Must specify an instance name when detaching a disk', - changed=False) - - disk = inst = None - changed = is_attached = False - - json_output = {'name': name, 'zone': zone, 'state': state, 'disk_type': disk_type} - if detach_only: - json_output['detach_only'] = True - json_output['detached_from_instance'] = instance_name - - if instance_name: - # user wants to attach/detach from an existing instance - try: - inst = gce.ex_get_node(instance_name, zone) - # is the disk attached? - for d in inst.extra['disks']: - if d['deviceName'] == name: - is_attached = True - json_output['attached_mode'] = d['mode'] - json_output['attached_to_instance'] = inst.name - except Exception: - pass - - # find disk if it already exists - try: - disk = gce.ex_get_volume(name) - json_output['size_gb'] = int(disk.size) - except ResourceNotFoundError: - pass - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - - # user wants a disk to exist. If "instance_name" is supplied the user - # also wants it attached - if state in ['active', 'present']: - - if not size_gb: - module.fail_json(msg="Must supply a size_gb", changed=False) - try: - size_gb = int(round(float(size_gb))) - if size_gb < 1: - raise Exception - except Exception: - module.fail_json(msg="Must supply a size_gb larger than 1 GB", - changed=False) - - if instance_name and inst is None: - module.fail_json(msg='Instance %s does not exist in zone %s' % ( - instance_name, zone), changed=False) - - if not disk: - if image is not None and snapshot is not None: - module.fail_json( - msg='Cannot give both image (%s) and snapshot (%s)' % ( - image, snapshot), changed=False) - lc_image = None - lc_snapshot = None - if image_family is not None: - lc_image = gce.ex_get_image_from_family(image_family, ex_project_list=external_projects) - elif image is not None: - lc_image = gce.ex_get_image(image, ex_project_list=external_projects) - elif snapshot is not None: - lc_snapshot = gce.ex_get_snapshot(snapshot) - try: - disk = gce.create_volume( - size_gb, name, location=zone, image=lc_image, - snapshot=lc_snapshot, ex_disk_type=disk_type) - except ResourceExistsError: - pass - except QuotaExceededError: - module.fail_json(msg='Requested disk size exceeds quota', - changed=False) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - json_output['size_gb'] = size_gb - if image is not None: - json_output['image'] = image - if snapshot is not None: - json_output['snapshot'] = snapshot - changed = True - if inst and not is_attached: - try: - gce.attach_volume(inst, disk, device=name, ex_mode=mode, - ex_auto_delete=delete_on_termination) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - json_output['attached_to_instance'] = inst.name - json_output['attached_mode'] = mode - if delete_on_termination: - json_output['delete_on_termination'] = True - changed = True - - # user wants to delete a disk (or perhaps just detach it). - if state in ['absent', 'deleted'] and disk: - - if inst and is_attached: - try: - gce.detach_volume(disk, ex_node=inst) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - changed = True - if not detach_only: - try: - gce.destroy_volume(disk) - except ResourceInUseError as e: - module.fail_json(msg=str(e.value), changed=False) - except Exception as e: - module.fail_json(msg=unexpected_error_msg(e), changed=False) - changed = True - - json_output['changed'] = changed - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gce_snapshot.py b/ansible_collections/community/google/plugins/modules/gce_snapshot.py deleted file mode 100644 index 6723f4648..000000000 --- a/ansible_collections/community/google/plugins/modules/gce_snapshot.py +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright: 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 = ''' ---- -module: gce_snapshot -short_description: Create or destroy snapshots for GCE storage volumes -description: - - Manages snapshots for GCE instances. This module manages snapshots for - the storage volumes of a GCE compute instance. If there are multiple - volumes, each snapshot will be prepended with the disk name -options: - instance_name: - type: str - description: - - The GCE instance to snapshot - required: True - snapshot_name: - type: str - description: - - The name of the snapshot to manage - required: True - disks: - type: list - description: - - A list of disks to create snapshots for. If none is provided, - all of the volumes will have snapshots created. - required: False - state: - type: str - description: - - Whether a snapshot should be C(present) or C(absent) - required: false - default: present - choices: [present, absent] - service_account_email: - type: str - description: - - GCP service account email for the project where the instance resides - credentials_file: - type: path - description: - - The path to the credentials file associated with the service account - project_id: - type: str - description: - - The GCP project ID to use -requirements: - - "python >= 2.6" - - "apache-libcloud >= 0.19.0" -author: Rob Wagner (@robwagner33) -''' - -EXAMPLES = ''' -- name: Create gce snapshot - community.google.gce_snapshot: - instance_name: example-instance - snapshot_name: example-snapshot - state: present - service_account_email: project_name@appspot.gserviceaccount.com - credentials_file: /path/to/credentials - project_id: project_name - delegate_to: localhost - -- name: Delete gce snapshot - community.google.gce_snapshot: - instance_name: example-instance - snapshot_name: example-snapshot - state: absent - service_account_email: project_name@appspot.gserviceaccount.com - credentials_file: /path/to/credentials - project_id: project_name - delegate_to: localhost - -# This example creates snapshots for only two of the available disks as -# disk0-example-snapshot and disk1-example-snapshot -- name: Create snapshots of specific disks - community.google.gce_snapshot: - instance_name: example-instance - snapshot_name: example-snapshot - state: present - disks: - - disk0 - - disk1 - service_account_email: project_name@appspot.gserviceaccount.com - credentials_file: /path/to/credentials - project_id: project_name - delegate_to: localhost -''' - -RETURN = ''' -snapshots_created: - description: List of newly created snapshots - returned: When snapshots are created - type: list - sample: "[disk0-example-snapshot, disk1-example-snapshot]" - -snapshots_deleted: - description: List of destroyed snapshots - returned: When snapshots are deleted - type: list - sample: "[disk0-example-snapshot, disk1-example-snapshot]" - -snapshots_existing: - description: List of snapshots that already existed (no-op) - returned: When snapshots were already present - type: list - sample: "[disk0-example-snapshot, disk1-example-snapshot]" - -snapshots_absent: - description: List of snapshots that were already absent (no-op) - returned: When snapshots were already absent - type: list - sample: "[disk0-example-snapshot, disk1-example-snapshot]" -''' - -try: - from libcloud.compute.types import Provider - _ = Provider.GCE - HAS_LIBCLOUD = True -except ImportError: - HAS_LIBCLOUD = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gce import gce_connect - - -def find_snapshot(volume, name): - ''' - Check if there is a snapshot already created with the given name for - the passed in volume. - - Args: - volume: A gce StorageVolume object to manage - name: The name of the snapshot to look for - - Returns: - The VolumeSnapshot object if one is found - ''' - found_snapshot = None - snapshots = volume.list_snapshots() - for snapshot in snapshots: - if name == snapshot.name: - found_snapshot = snapshot - return found_snapshot - - -def main(): - module = AnsibleModule( - argument_spec=dict( - instance_name=dict(required=True), - snapshot_name=dict(required=True), - state=dict(choices=['present', 'absent'], default='present'), - disks=dict(default=None, type='list'), - service_account_email=dict(type='str'), - credentials_file=dict(type='path'), - project_id=dict(type='str') - ) - ) - - if not HAS_LIBCLOUD: - module.fail_json(msg='libcloud with GCE support (0.19.0+) is required for this module') - - gce = gce_connect(module) - - instance_name = module.params.get('instance_name') - snapshot_name = module.params.get('snapshot_name') - disks = module.params.get('disks') - state = module.params.get('state') - - json_output = dict( - changed=False, - snapshots_created=[], - snapshots_deleted=[], - snapshots_existing=[], - snapshots_absent=[] - ) - - snapshot = None - - instance = gce.ex_get_node(instance_name, 'all') - instance_disks = instance.extra['disks'] - - for instance_disk in instance_disks: - disk_snapshot_name = snapshot_name - disk_info = gce._get_components_from_path(instance_disk['source']) - device_name = disk_info['name'] - device_zone = disk_info['zone'] - if disks is None or device_name in disks: - volume_obj = gce.ex_get_volume(device_name, device_zone) - - # If we have more than one disk to snapshot, prepend the disk name - if len(instance_disks) > 1: - disk_snapshot_name = device_name + "-" + disk_snapshot_name - - snapshot = find_snapshot(volume_obj, disk_snapshot_name) - - if snapshot and state == 'present': - json_output['snapshots_existing'].append(disk_snapshot_name) - - elif snapshot and state == 'absent': - snapshot.destroy() - json_output['changed'] = True - json_output['snapshots_deleted'].append(disk_snapshot_name) - - elif not snapshot and state == 'present': - volume_obj.snapshot(disk_snapshot_name) - json_output['changed'] = True - json_output['snapshots_created'].append(disk_snapshot_name) - - elif not snapshot and state == 'absent': - json_output['snapshots_absent'].append(disk_snapshot_name) - - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gce_tag.py b/ansible_collections/community/google/plugins/modules/gce_tag.py deleted file mode 100644 index 4af318632..000000000 --- a/ansible_collections/community/google/plugins/modules/gce_tag.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, 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 = ''' ---- -module: gce_tag -short_description: add or remove tag(s) to/from GCE instances -description: - - This module can add or remove tags U(https://cloud.google.com/compute/docs/label-or-tag-resources#tags) - to/from GCE instances. Use 'instance_pattern' to update multiple instances in a specify zone. -options: - instance_name: - type: str - description: - - The name of the GCE instance to add/remove tags. - - Required if C(instance_pattern) is not specified. - instance_pattern: - type: str - description: - - The pattern of GCE instance names to match for adding/removing tags. Full-Python regex is supported. - See U(https://docs.python.org/2/library/re.html) for details. - - If C(instance_name) is not specified, this field is required. - tags: - type: list - description: - - Comma-separated list of tags to add or remove. - required: yes - state: - type: str - description: - - Desired state of the tags. - choices: [ absent, present ] - default: present - zone: - type: str - description: - - The zone of the disk specified by source. - default: us-central1-a - service_account_email: - type: str - description: - - Service account email. - pem_file: - type: path - description: - - Path to the PEM file associated with the service account email. - project_id: - type: str - description: - - Your GCE project ID. -requirements: - - python >= 2.6 - - apache-libcloud >= 0.17.0 -notes: - - Either I(instance_name) or I(instance_pattern) is required. -author: - - Do Hoang Khiem (@dohoangkhiem) <(dohoangkhiem@gmail.com> - - Tom Melendez (@supertom) -''' - -EXAMPLES = ''' -- name: Add tags to instance - community.google.gce_tag: - instance_name: staging-server - tags: http-server,https-server,staging - zone: us-central1-a - state: present - -- name: Remove tags from instance in default zone (us-central1-a) - community.google.gce_tag: - instance_name: test-server - tags: foo,bar - state: absent - -- name: Add tags to instances in zone that match pattern - community.google.gce_tag: - instance_pattern: test-server-* - tags: foo,bar - zone: us-central1-a - state: present -''' - -import re -import traceback - -try: - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - from libcloud.common.google import GoogleBaseError, QuotaExceededError, \ - ResourceExistsError, ResourceNotFoundError, InvalidRequestError - - _ = Provider.GCE - HAS_LIBCLOUD = True -except ImportError: - HAS_LIBCLOUD = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gce import gce_connect - - -def _union_items(baselist, comparelist): - """Combine two lists, removing duplicates.""" - return list(set(baselist) | set(comparelist)) - - -def _intersect_items(baselist, comparelist): - """Return matching items in both lists.""" - return list(set(baselist) & set(comparelist)) - - -def _get_changed_items(baselist, comparelist): - """Return changed items as they relate to baselist.""" - return list(set(baselist) & set(set(baselist) ^ set(comparelist))) - - -def modify_tags(gce, module, node, tags, state='present'): - """Modify tags on an instance.""" - - existing_tags = node.extra['tags'] - tags = [x.lower() for x in tags] - tags_changed = [] - - if state == 'absent': - # tags changed are any that intersect - tags_changed = _intersect_items(existing_tags, tags) - if not tags_changed: - return False, None - # update instance with tags in existing tags that weren't specified - node_tags = _get_changed_items(existing_tags, tags) - else: - # tags changed are any that in the new list that weren't in existing - tags_changed = _get_changed_items(tags, existing_tags) - if not tags_changed: - return False, None - # update instance with the combined list - node_tags = _union_items(existing_tags, tags) - - try: - gce.ex_set_node_tags(node, node_tags) - return True, tags_changed - except (GoogleBaseError, InvalidRequestError) as e: - module.fail_json(msg=str(e), changed=False) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - instance_name=dict(type='str'), - instance_pattern=dict(type='str'), - tags=dict(type='list', required=True), - state=dict(type='str', default='present', choices=['absent', 'present']), - zone=dict(type='str', default='us-central1-a'), - service_account_email=dict(type='str'), - pem_file=dict(type='path'), - project_id=dict(type='str'), - ), - mutually_exclusive=[ - ['instance_name', 'instance_pattern'] - ], - required_one_of=[ - ['instance_name', 'instance_pattern'] - ], - ) - - instance_name = module.params.get('instance_name') - instance_pattern = module.params.get('instance_pattern') - state = module.params.get('state') - tags = module.params.get('tags') - zone = module.params.get('zone') - changed = False - - if not HAS_LIBCLOUD: - module.fail_json(msg='libcloud with GCE support (0.17.0+) required for this module') - - gce = gce_connect(module) - - # Create list of nodes to operate on - matching_nodes = [] - try: - if instance_pattern: - instances = gce.list_nodes(ex_zone=zone) - # no instances in zone - if not instances: - module.exit_json(changed=False, tags=tags, zone=zone, instances_updated=[]) - try: - # Python regex fully supported: https://docs.python.org/2/library/re.html - p = re.compile(instance_pattern) - matching_nodes = [i for i in instances if p.search(i.name) is not None] - except re.error as e: - module.fail_json(msg='Regex error for pattern %s: %s' % (instance_pattern, e), changed=False) - else: - matching_nodes = [gce.ex_get_node(instance_name, zone=zone)] - except ResourceNotFoundError: - module.fail_json(msg='Instance %s not found in zone %s' % (instance_name, zone), changed=False) - except GoogleBaseError as e: - module.fail_json(msg=str(e), changed=False, exception=traceback.format_exc()) - - # Tag nodes - instance_pattern_matches = [] - tags_changed = [] - for node in matching_nodes: - changed, tags_changed = modify_tags(gce, module, node, tags, state) - if changed: - instance_pattern_matches.append({'instance_name': node.name, 'tags_changed': tags_changed}) - if instance_pattern: - module.exit_json(changed=changed, instance_pattern=instance_pattern, tags=tags_changed, zone=zone, instances_updated=instance_pattern_matches) - else: - module.exit_json(changed=changed, instance_name=instance_name, tags=tags_changed, zone=zone) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gcpubsub.py b/ansible_collections/community/google/plugins/modules/gcpubsub.py deleted file mode 100644 index 2d9230c8f..000000000 --- a/ansible_collections/community/google/plugins/modules/gcpubsub.py +++ /dev/null @@ -1,349 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2016, Google Inc. -# 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: gcpubsub -short_description: Create and Delete Topics/Subscriptions, Publish and pull messages on PubSub -description: - - Create and Delete Topics/Subscriptions, Publish and pull messages on PubSub. - See U(https://cloud.google.com/pubsub/docs) for an overview. -requirements: - - google-auth >= 0.5.0 - - google-cloud-pubsub >= 0.22.0 -notes: - - Subscription pull happens before publish. You cannot publish and pull in the same task. -author: - - Tom Melendez (@supertom) <tom@supertom.com> -options: - topic: - type: str - description: - - GCP pubsub topic name. - - Only the name, not the full path, is required. - required: yes - subscription: - type: dict - description: - - Dictionary containing a subscription name associated with a topic (required), along with optional ack_deadline, push_endpoint and pull. - For pulling from a subscription, message_ack (bool), max_messages (int) and return_immediate are available as subfields. - See subfields name, push_endpoint and ack_deadline for more information. - suboptions: - name: - description: - - Subfield of subscription. Required if subscription is specified. See examples. - ack_deadline: - description: - - Subfield of subscription. Not required. Default deadline for subscriptions to ACK the message before it is resent. See examples. - pull: - description: - - Subfield of subscription. Not required. If specified, messages will be retrieved from topic via the - provided subscription name. max_messages (int; default None; max number of messages to pull), - message_ack (bool; default False; acknowledge the message) and return_immediately - (bool; default True, don't wait for messages to appear). If the messages are acknowledged, - changed is set to True, otherwise, changed is False. - push_endpoint: - description: - - Subfield of subscription. Not required. If specified, message will be sent to an endpoint. - See U(https://cloud.google.com/pubsub/docs/advanced#push_endpoints) for more information. - publish: - type: list - description: - - List of dictionaries describing messages and attributes to be published. Dictionary is in message(str):attributes(dict) format. - Only message is required. - state: - type: str - description: - - State of the topic or queue. - - Applies to the most granular resource. - - If subscription isspecified we remove it. - - If only topic is specified, that is what is removed. - - NOTE - A topic can be removed without first removing the subscription. - choices: [ absent, present ] - default: present - project_id: - type: str - description: - - your GCE project ID - credentials_file: - type: str - description: - - path to the JSON file associated with the service account email - service_account_email: - type: str - description: - - service account email -''' - -EXAMPLES = ''' -# (Message will be pushed; there is no check to see if the message was pushed before -- name: Create a topic and publish a message to it - community.google.gcpubsub: - topic: ansible-topic-example - state: present - -# Subscriptions associated with topic are not deleted. -- name: Delete Topic - community.google.gcpubsub: - topic: ansible-topic-example - state: absent - -# Setting absent will keep the messages from being sent -- name: Publish multiple messages, with attributes (key:value available with the message) - community.google.gcpubsub: - topic: '{{ topic_name }}' - state: present - publish: - - message: this is message 1 - attributes: - mykey1: myvalue - mykey2: myvalu2 - mykey3: myvalue3 - - message: this is message 2 - attributes: - server: prod - sla: "99.9999" - owner: fred - -- name: Create Subscription (pull) - community.google.gcpubsub: - topic: ansible-topic-example - subscription: - - name: mysub - state: present - -# pull is default, ack_deadline is not required -- name: Create Subscription with ack_deadline and push endpoint - community.google.gcpubsub: - topic: ansible-topic-example - subscription: - - name: mysub - ack_deadline: "60" - push_endpoint: http://pushendpoint.example.com - state: present - -# Setting push_endpoint to "None" converts subscription to pull. -- name: Subscription change from push to pull - community.google.gcpubsub: - topic: ansible-topic-example - subscription: - name: mysub - push_endpoint: "None" - -### Topic will not be deleted -- name: Delete subscription - community.google.gcpubsub: - topic: ansible-topic-example - subscription: - - name: mysub - state: absent - -# only pull keyword is required. -- name: Pull messages from subscription - community.google.gcpubsub: - topic: ansible-topic-example - subscription: - name: ansible-topic-example-sub - pull: - message_ack: yes - max_messages: "100" -''' - -RETURN = ''' -publish: - description: List of dictionaries describing messages and attributes to be published. Dictionary is in message(str):attributes(dict) format. - Only message is required. - returned: Only when specified - type: list - sample: "publish: ['message': 'my message', attributes: {'key1': 'value1'}]" - -pulled_messages: - description: list of dictionaries containing message info. Fields are ack_id, attributes, data, message_id. - returned: Only when subscription.pull is specified - type: list - sample: [{ "ack_id": "XkASTCcYREl...","attributes": {"key1": "val1",...}, "data": "this is message 1", "message_id": "49107464153705"},..] - -state: - description: The state of the topic or subscription. Value will be either 'absent' or 'present'. - returned: Always - type: str - sample: "present" - -subscription: - description: Name of subscription. - returned: When subscription fields are specified - type: str - sample: "mysubscription" - -topic: - description: Name of topic. - returned: Always - type: str - sample: "mytopic" -''' - -try: - from ast import literal_eval - HAS_PYTHON26 = True -except ImportError: - HAS_PYTHON26 = False - -try: - from google.cloud import pubsub - HAS_GOOGLE_CLOUD_PUBSUB = True -except ImportError as e: - HAS_GOOGLE_CLOUD_PUBSUB = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gcp import check_min_pkg_version, get_google_cloud_credentials - - -CLOUD_CLIENT = 'google-cloud-pubsub' -CLOUD_CLIENT_MINIMUM_VERSION = '0.22.0' -CLOUD_CLIENT_USER_AGENT = 'ansible-pubsub-0.1' - - -def publish_messages(message_list, topic): - with topic.batch() as batch: - for message in message_list: - msg = message['message'] - attrs = {} - if 'attributes' in message: - attrs = message['attributes'] - batch.publish(bytes(msg), **attrs) - return True - - -def pull_messages(pull_params, sub): - """ - :rtype: tuple (output, changed) - """ - changed = False - max_messages = pull_params.get('max_messages', None) - message_ack = pull_params.get('message_ack', 'no') - return_immediately = pull_params.get('return_immediately', False) - - output = [] - pulled = sub.pull(return_immediately=return_immediately, max_messages=max_messages) - - for ack_id, msg in pulled: - msg_dict = {'message_id': msg.message_id, - 'attributes': msg.attributes, - 'data': msg.data, - 'ack_id': ack_id} - output.append(msg_dict) - - if message_ack: - ack_ids = [m['ack_id'] for m in output] - if ack_ids: - sub.acknowledge(ack_ids) - changed = True - return (output, changed) - - -def main(): - - module = AnsibleModule( - argument_spec=dict( - topic=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present']), - publish=dict(type='list'), - subscription=dict(type='dict'), - service_account_email=dict(type='str'), - credentials_file=dict(type='str'), - project_id=dict(type='str'), - ), - ) - - if not HAS_PYTHON26: - module.fail_json( - msg="GCE module requires python's 'ast' module, python v2.6+") - - if not HAS_GOOGLE_CLOUD_PUBSUB: - module.fail_json(msg="Please install google-cloud-pubsub library.") - - if not check_min_pkg_version(CLOUD_CLIENT, CLOUD_CLIENT_MINIMUM_VERSION): - module.fail_json(msg="Please install %s client version %s" % (CLOUD_CLIENT, CLOUD_CLIENT_MINIMUM_VERSION)) - - mod_params = {} - mod_params['publish'] = module.params.get('publish') - mod_params['state'] = module.params.get('state') - mod_params['topic'] = module.params.get('topic') - mod_params['subscription'] = module.params.get('subscription') - - creds, params = get_google_cloud_credentials(module) - pubsub_client = pubsub.Client(project=params['project_id'], credentials=creds, use_gax=False) - pubsub_client.user_agent = CLOUD_CLIENT_USER_AGENT - - changed = False - json_output = {} - - t = None - if mod_params['topic']: - t = pubsub_client.topic(mod_params['topic']) - s = None - if mod_params['subscription']: - # Note: default ack deadline cannot be changed without deleting/recreating subscription - s = t.subscription(mod_params['subscription']['name'], - ack_deadline=mod_params['subscription'].get('ack_deadline', None), - push_endpoint=mod_params['subscription'].get('push_endpoint', None)) - - if mod_params['state'] == 'absent': - # Remove the most granular resource. If subscription is specified - # we remove it. If only topic is specified, that is what is removed. - # Note that a topic can be removed without first removing the subscription. - # TODO(supertom): Enhancement: Provide an option to only delete a topic - # if there are no subscriptions associated with it (which the API does not support). - if s is not None: - if s.exists(): - s.delete() - changed = True - else: - if t.exists(): - t.delete() - changed = True - elif mod_params['state'] == 'present': - if not t.exists(): - t.create() - changed = True - if s: - if not s.exists(): - s.create() - s.reload() - changed = True - else: - # Subscription operations - # TODO(supertom): if more 'update' operations arise, turn this into a function. - s.reload() - push_endpoint = mod_params['subscription'].get('push_endpoint', None) - if push_endpoint is not None: - if push_endpoint != s.push_endpoint: - if push_endpoint == 'None': - push_endpoint = None - s.modify_push_configuration(push_endpoint=push_endpoint) - s.reload() - changed = push_endpoint == s.push_endpoint - - if 'pull' in mod_params['subscription']: - if s.push_endpoint is not None: - module.fail_json(msg="Cannot pull messages, push_endpoint is configured.") - (json_output['pulled_messages'], changed) = pull_messages( - mod_params['subscription']['pull'], s) - - # publish messages to the topic - if mod_params['publish'] and len(mod_params['publish']) > 0: - changed = publish_messages(mod_params['publish'], t) - - json_output['changed'] = changed - json_output.update(mod_params) - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/plugins/modules/gcpubsub_info.py b/ansible_collections/community/google/plugins/modules/gcpubsub_info.py deleted file mode 100644 index 1feac1e03..000000000 --- a/ansible_collections/community/google/plugins/modules/gcpubsub_info.py +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/python -# Copyright 2016 Google Inc. -# 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: gcpubsub_info -short_description: List Topics/Subscriptions and Messages from Google PubSub. -description: - - List Topics/Subscriptions from Google PubSub. Use the gcpubsub module for - topic/subscription management. - See U(https://cloud.google.com/pubsub/docs) for an overview. - - This module was called C(gcpubsub_facts) before Ansible 2.9. The usage did not change. -requirements: - - "python >= 2.6" - - "google-auth >= 0.5.0" - - "google-cloud-pubsub >= 0.22.0" -notes: - - list state enables user to list topics or subscriptions in the project. See examples for details. -author: - - "Tom Melendez (@supertom) <tom@supertom.com>" -options: - topic: - type: str - description: - - GCP pubsub topic name. Only the name, not the full path, is required. - required: False - view: - type: str - description: - - Choices are 'topics' or 'subscriptions' - choices: [topics, subscriptions] - default: topics - state: - type: str - description: - - list is the only valid option. - required: False - choices: [list] - default: list - project_id: - type: str - description: - - your GCE project ID - credentials_file: - type: str - description: - - path to the JSON file associated with the service account email - service_account_email: - type: str - description: - - service account email -''' - -EXAMPLES = ''' -- name: List all Topics in a project - community.google.gcpubsub_info: - view: topics - state: list - -- name: List all Subscriptions in a project - community.google.gcpubsub_info: - view: subscriptions - state: list - -- name: List all Subscriptions for a Topic in a project - community.google.gcpubsub_info: - view: subscriptions - topic: my-topic - state: list -''' - -RETURN = ''' -subscriptions: - description: List of subscriptions. - returned: When view is set to subscriptions. - type: list - sample: ["mysubscription", "mysubscription2"] -topic: - description: Name of topic. Used to filter subscriptions. - returned: Always - type: str - sample: "mytopic" -topics: - description: List of topics. - returned: When view is set to topics. - type: list - sample: ["mytopic", "mytopic2"] -''' - -try: - from ast import literal_eval - HAS_PYTHON26 = True -except ImportError: - HAS_PYTHON26 = False - -try: - from google.cloud import pubsub - HAS_GOOGLE_CLOUD_PUBSUB = True -except ImportError as e: - HAS_GOOGLE_CLOUD_PUBSUB = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.google.plugins.module_utils.gcp import check_min_pkg_version, get_google_cloud_credentials - - -def list_func(data, member='name'): - """Used for state=list.""" - return [getattr(x, member) for x in data] - - -def main(): - module = AnsibleModule(argument_spec=dict( - view=dict(choices=['topics', 'subscriptions'], default='topics'), - topic=dict(required=False), - state=dict(choices=['list'], default='list'), - service_account_email=dict(), - credentials_file=dict(), - project_id=dict(), ),) - if module._name in ('gcpubsub_facts', 'community.google.gcpubsub_facts'): - module.deprecate("The 'gcpubsub_facts' module has been renamed to 'gcpubsub_info'", - version='3.0.0', collection_name='community.google') # was Ansible 2.13 - - if not HAS_PYTHON26: - module.fail_json( - msg="GCE module requires python's 'ast' module, python v2.6+") - - if not HAS_GOOGLE_CLOUD_PUBSUB: - module.fail_json(msg="Please install google-cloud-pubsub library.") - - CLIENT_MINIMUM_VERSION = '0.22.0' - if not check_min_pkg_version('google-cloud-pubsub', CLIENT_MINIMUM_VERSION): - module.fail_json(msg="Please install google-cloud-pubsub library version %s" % CLIENT_MINIMUM_VERSION) - - mod_params = {} - mod_params['state'] = module.params.get('state') - mod_params['topic'] = module.params.get('topic') - mod_params['view'] = module.params.get('view') - - creds, params = get_google_cloud_credentials(module) - pubsub_client = pubsub.Client(project=params['project_id'], credentials=creds, use_gax=False) - pubsub_client.user_agent = 'ansible-pubsub-0.1' - - json_output = {} - if mod_params['view'] == 'topics': - json_output['topics'] = list_func(pubsub_client.list_topics()) - elif mod_params['view'] == 'subscriptions': - if mod_params['topic']: - t = pubsub_client.topic(mod_params['topic']) - json_output['subscriptions'] = list_func(t.list_subscriptions()) - else: - json_output['subscriptions'] = list_func(pubsub_client.list_subscriptions()) - - json_output['changed'] = False - json_output.update(mod_params) - module.exit_json(**json_output) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/google/scripts/inventory/gce.ini b/ansible_collections/community/google/scripts/inventory/gce.ini deleted file mode 100644 index af27a9c4a..000000000 --- a/ansible_collections/community/google/scripts/inventory/gce.ini +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright 2013 Google Inc. -# -# This file is part of Ansible -# -# Ansible 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. -# -# Ansible 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 Ansible. If not, see <http://www.gnu.org/licenses/>. - -# The GCE inventory script has the following dependencies: -# 1. A valid Google Cloud Platform account with Google Compute Engine -# enabled. See https://cloud.google.com -# 2. An OAuth2 Service Account flow should be enabled. This will generate -# a private key file that the inventory script will use for API request -# authorization. See https://developers.google.com/accounts/docs/OAuth2 -# 3. Convert the private key from PKCS12 to PEM format -# $ openssl pkcs12 -in pkey.pkcs12 -passin pass:notasecret \ -# > -nodes -nocerts | openssl rsa -out pkey.pem -# 4. The libcloud (>=0.13.3) python libray. See http://libcloud.apache.org -# -# (See ansible/test/gce_tests.py comments for full install instructions) -# -# Author: Eric Johnson <erjohnso@google.com> -# Contributors: John Roach <johnroach1985@gmail.com> - -[gce] -# GCE Service Account configuration information can be stored in the -# libcloud 'secrets.py' file. Ideally, the 'secrets.py' file will already -# exist in your PYTHONPATH and be picked up automatically with an import -# statement in the inventory script. However, you can specify an absolute -# path to the secrets.py file with 'libcloud_secrets' parameter. -# This option will be deprecated in a future release. -libcloud_secrets = - -# If you are not going to use a 'secrets.py' file, you can set the necessary -# authorization parameters here. -# You can add multiple gce projects to by using a comma separated list. Make -# sure that the service account used has permissions on said projects. -gce_service_account_email_address = -gce_service_account_pem_file_path = -gce_project_id = -gce_zone = - -# Filter inventory based on state. Leave undefined to return instances regardless of state. -# example: Uncomment to only return inventory in the running or provisioning state -#instance_states = RUNNING,PROVISIONING - -# Filter inventory based on instance tags. Leave undefined to return instances regardless of tags. -# example: Uncomment to only return inventory with the http-server or https-server tag -#instance_tags = http-server,https-server - - -[inventory] -# The 'inventory_ip_type' parameter specifies whether 'ansible_ssh_host' should -# contain the instance internal or external address. Values may be either -# 'internal' or 'external'. If 'external' is specified but no external instance -# address exists, the internal address will be used. -# The INVENTORY_IP_TYPE environment variable will override this value. -inventory_ip_type = - -[cache] -# directory in which cache should be created -cache_path = ~/.ansible/tmp - -# The number of seconds a cache file is considered valid. After this many -# seconds, a new API call will be made, and the cache file will be updated. -# To disable the cache, set this value to 0 -cache_max_age = 300 diff --git a/ansible_collections/community/google/scripts/inventory/gce.py b/ansible_collections/community/google/scripts/inventory/gce.py deleted file mode 100644 index 05a93f483..000000000 --- a/ansible_collections/community/google/scripts/inventory/gce.py +++ /dev/null @@ -1,524 +0,0 @@ -#!/usr/bin/env python - -# Copyright: (c) 2013, Google Inc. -# 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 - -''' -GCE external inventory script -================================= - -Generates inventory that Ansible can understand by making API requests -Google Compute Engine via the libcloud library. Full install/configuration -instructions for the gce* modules can be found in the comments of -ansible/test/gce_tests.py. - -When run against a specific host, this script returns the following variables -based on the data obtained from the libcloud Node object: - - gce_uuid - - gce_id - - gce_image - - gce_machine_type - - gce_private_ip - - gce_public_ip - - gce_name - - gce_description - - gce_status - - gce_zone - - gce_tags - - gce_metadata - - gce_network - - gce_subnetwork - -When run in --list mode, instances are grouped by the following categories: - - zone: - zone group name examples are us-central1-b, europe-west1-a, etc. - - instance tags: - An entry is created for each tag. For example, if you have two instances - with a common tag called 'foo', they will both be grouped together under - the 'tag_foo' name. - - network name: - the name of the network is appended to 'network_' (e.g. the 'default' - network will result in a group named 'network_default') - - machine type - types follow a pattern like n1-standard-4, g1-small, etc. - - running status: - group name prefixed with 'status_' (e.g. status_running, status_stopped,..) - - image: - when using an ephemeral/scratch disk, this will be set to the image name - used when creating the instance (e.g. debian-7-wheezy-v20130816). when - your instance was created with a root persistent disk it will be set to - 'persistent_disk' since there is no current way to determine the image. - -Examples: - Execute uname on all instances in the us-central1-a zone - $ ansible -i gce.py us-central1-a -m shell -a "/bin/uname -a" - - Use the GCE inventory script to print out instance specific information - $ contrib/inventory/gce.py --host my_instance - -Author: Eric Johnson <erjohnso@google.com> -Contributors: Matt Hite <mhite@hotmail.com>, Tom Melendez <supertom@google.com>, - John Roach <johnroach1985@gmail.com> -Version: 0.0.4 -''' - -try: - import pkg_resources -except ImportError: - # Use pkg_resources to find the correct versions of libraries and set - # sys.path appropriately when there are multiversion installs. We don't - # fail here as there is code that better expresses the errors where the - # library is used. - pass - -USER_AGENT_PRODUCT = "Ansible-gce_inventory_plugin" -USER_AGENT_VERSION = "v2" - -import sys -import os -import argparse - -from time import time - -from ansible.module_utils.six.moves import configparser - -import logging -logging.getLogger('libcloud.common.google').addHandler(logging.NullHandler()) - -import json - -try: - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - _ = Provider.GCE -except Exception: - sys.exit("GCE inventory script requires libcloud >= 0.13") - - -class CloudInventoryCache(object): - def __init__(self, cache_name='ansible-cloud-cache', cache_path='/tmp', - cache_max_age=300): - cache_dir = os.path.expanduser(cache_path) - if not os.path.exists(cache_dir): - os.makedirs(cache_dir) - self.cache_path_cache = os.path.join(cache_dir, cache_name) - - self.cache_max_age = cache_max_age - - def is_valid(self, max_age=None): - ''' Determines if the cache files have expired, or if it is still valid ''' - - if max_age is None: - max_age = self.cache_max_age - - if os.path.isfile(self.cache_path_cache): - mod_time = os.path.getmtime(self.cache_path_cache) - current_time = time() - if (mod_time + max_age) > current_time: - return True - - return False - - def get_all_data_from_cache(self, filename=''): - ''' Reads the JSON inventory from the cache file. Returns Python dictionary. ''' - - data = '' - if not filename: - filename = self.cache_path_cache - with open(filename, 'r') as cache: - data = cache.read() - return json.loads(data) - - def write_to_cache(self, data, filename=''): - ''' Writes data to file as JSON. Returns True. ''' - if not filename: - filename = self.cache_path_cache - json_data = json.dumps(data) - with open(filename, 'w') as cache: - cache.write(json_data) - return True - - -class GceInventory(object): - def __init__(self): - # Cache object - self.cache = None - # dictionary containing inventory read from disk - self.inventory = {} - - # Read settings and parse CLI arguments - self.parse_cli_args() - self.config = self.get_config() - self.drivers = self.get_gce_drivers() - self.ip_type = self.get_inventory_options() - if self.ip_type: - self.ip_type = self.ip_type.lower() - - # Cache management - start_inventory_time = time() - cache_used = False - if self.args.refresh_cache or not self.cache.is_valid(): - self.do_api_calls_update_cache() - else: - self.load_inventory_from_cache() - cache_used = True - self.inventory['_meta']['stats'] = {'use_cache': True} - self.inventory['_meta']['stats'] = { - 'inventory_load_time': time() - start_inventory_time, - 'cache_used': cache_used - } - - # Just display data for specific host - if self.args.host: - print(self.json_format_dict( - self.inventory['_meta']['hostvars'][self.args.host], - pretty=self.args.pretty)) - else: - # Otherwise, assume user wants all instances grouped - zones = self.parse_env_zones() - print(self.json_format_dict(self.inventory, - pretty=self.args.pretty)) - sys.exit(0) - - def get_config(self): - """ - Reads the settings from the gce.ini file. - - Populates a ConfigParser object with defaults and - attempts to read an .ini-style configuration from the filename - specified in GCE_INI_PATH. If the environment variable is - not present, the filename defaults to gce.ini in the current - working directory. - """ - gce_ini_default_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "gce.ini") - gce_ini_path = os.environ.get('GCE_INI_PATH', gce_ini_default_path) - - # Create a ConfigParser. - # This provides empty defaults to each key, so that environment - # variable configuration (as opposed to INI configuration) is able - # to work. - config = configparser.ConfigParser(defaults={ - 'gce_service_account_email_address': '', - 'gce_service_account_pem_file_path': '', - 'gce_project_id': '', - 'gce_zone': '', - 'libcloud_secrets': '', - 'instance_tags': '', - 'inventory_ip_type': '', - 'cache_path': '~/.ansible/tmp', - 'cache_max_age': '300' - }) - if 'gce' not in config.sections(): - config.add_section('gce') - if 'inventory' not in config.sections(): - config.add_section('inventory') - if 'cache' not in config.sections(): - config.add_section('cache') - - config.read(gce_ini_path) - - ######### - # Section added for processing ini settings - ######### - - # Set the instance_states filter based on config file options - self.instance_states = [] - if config.has_option('gce', 'instance_states'): - states = config.get('gce', 'instance_states') - # Ignore if instance_states is an empty string. - if states: - self.instance_states = states.split(',') - - # Set the instance_tags filter, env var overrides config from file - # and cli param overrides all - if self.args.instance_tags: - self.instance_tags = self.args.instance_tags - else: - self.instance_tags = os.environ.get( - 'GCE_INSTANCE_TAGS', config.get('gce', 'instance_tags')) - if self.instance_tags: - self.instance_tags = self.instance_tags.split(',') - - # Caching - cache_path = config.get('cache', 'cache_path') - cache_max_age = config.getint('cache', 'cache_max_age') - # TOOD(supertom): support project-specific caches - cache_name = 'ansible-gce.cache' - self.cache = CloudInventoryCache(cache_path=cache_path, - cache_max_age=cache_max_age, - cache_name=cache_name) - return config - - def get_inventory_options(self): - """Determine inventory options. Environment variables always - take precedence over configuration files.""" - ip_type = self.config.get('inventory', 'inventory_ip_type') - # If the appropriate environment variables are set, they override - # other configuration - ip_type = os.environ.get('INVENTORY_IP_TYPE', ip_type) - return ip_type - - def get_gce_drivers(self): - """Determine the GCE authorization settings and return a list of - libcloud drivers. - """ - # Attempt to get GCE params from a configuration file, if one - # exists. - secrets_path = self.config.get('gce', 'libcloud_secrets') - secrets_found = False - - try: - import secrets - args = list(secrets.GCE_PARAMS) - kwargs = secrets.GCE_KEYWORD_PARAMS - secrets_found = True - except Exception: - pass - - if not secrets_found and secrets_path: - if not secrets_path.endswith('secrets.py'): - err = "Must specify libcloud secrets file as " - err += "/absolute/path/to/secrets.py" - sys.exit(err) - sys.path.append(os.path.dirname(secrets_path)) - try: - import secrets - args = list(getattr(secrets, 'GCE_PARAMS', [])) - kwargs = getattr(secrets, 'GCE_KEYWORD_PARAMS', {}) - secrets_found = True - except Exception: - pass - - if not secrets_found: - args = [ - self.config.get('gce', 'gce_service_account_email_address'), - self.config.get('gce', 'gce_service_account_pem_file_path') - ] - kwargs = {'project': self.config.get('gce', 'gce_project_id'), - 'datacenter': self.config.get('gce', 'gce_zone')} - - # If the appropriate environment variables are set, they override - # other configuration; process those into our args and kwargs. - args[0] = os.environ.get('GCE_EMAIL', args[0]) - args[1] = os.environ.get('GCE_PEM_FILE_PATH', args[1]) - args[1] = os.environ.get('GCE_CREDENTIALS_FILE_PATH', args[1]) - - kwargs['project'] = os.environ.get('GCE_PROJECT', kwargs['project']) - kwargs['datacenter'] = os.environ.get('GCE_ZONE', kwargs['datacenter']) - - gce_drivers = [] - projects = kwargs['project'].split(',') - for project in projects: - kwargs['project'] = project - gce = get_driver(Provider.GCE)(*args, **kwargs) - gce.connection.user_agent_append( - '%s/%s' % (USER_AGENT_PRODUCT, USER_AGENT_VERSION), - ) - gce_drivers.append(gce) - return gce_drivers - - def parse_env_zones(self): - '''returns a list of comma separated zones parsed from the GCE_ZONE environment variable. - If provided, this will be used to filter the results of the grouped_instances call''' - import csv - reader = csv.reader([os.environ.get('GCE_ZONE', "")], skipinitialspace=True) - zones = [r for r in reader] - return [z for z in zones[0]] - - def parse_cli_args(self): - ''' Command line argument processing ''' - - parser = argparse.ArgumentParser( - description='Produce an Ansible Inventory file based on GCE') - parser.add_argument('--list', action='store_true', default=True, - help='List instances (default: True)') - parser.add_argument('--host', action='store', - help='Get all information about an instance') - parser.add_argument('--instance-tags', action='store', - help='Only include instances with this tags, separated by comma') - parser.add_argument('--pretty', action='store_true', default=False, - help='Pretty format (default: False)') - parser.add_argument( - '--refresh-cache', action='store_true', default=False, - help='Force refresh of cache by making API requests (default: False - use cache files)') - self.args = parser.parse_args() - - def node_to_dict(self, inst): - md = {} - - if inst is None: - return {} - - if 'items' in inst.extra['metadata']: - for entry in inst.extra['metadata']['items']: - md[entry['key']] = entry['value'] - - net = inst.extra['networkInterfaces'][0]['network'].split('/')[-1] - subnet = None - if 'subnetwork' in inst.extra['networkInterfaces'][0]: - subnet = inst.extra['networkInterfaces'][0]['subnetwork'].split('/')[-1] - # default to exernal IP unless user has specified they prefer internal - if self.ip_type == 'internal': - ssh_host = inst.private_ips[0] - else: - ssh_host = inst.public_ips[0] if len(inst.public_ips) >= 1 else inst.private_ips[0] - - return { - 'gce_uuid': inst.uuid, - 'gce_id': inst.id, - 'gce_image': inst.image, - 'gce_machine_type': inst.size, - 'gce_private_ip': inst.private_ips[0], - 'gce_public_ip': inst.public_ips[0] if len(inst.public_ips) >= 1 else None, - 'gce_name': inst.name, - 'gce_description': inst.extra['description'], - 'gce_status': inst.extra['status'], - 'gce_zone': inst.extra['zone'].name, - 'gce_tags': inst.extra['tags'], - 'gce_metadata': md, - 'gce_network': net, - 'gce_subnetwork': subnet, - # Hosts don't have a public name, so we add an IP - 'ansible_ssh_host': ssh_host - } - - def load_inventory_from_cache(self): - ''' Loads inventory from JSON on disk. ''' - - try: - self.inventory = self.cache.get_all_data_from_cache() - hosts = self.inventory['_meta']['hostvars'] - except Exception as e: - print( - "Invalid inventory file %s. Please rebuild with -refresh-cache option." - % (self.cache.cache_path_cache)) - raise - - def do_api_calls_update_cache(self): - ''' Do API calls and save data in cache. ''' - zones = self.parse_env_zones() - data = self.group_instances(zones) - self.cache.write_to_cache(data) - self.inventory = data - - def list_nodes(self): - all_nodes = [] - params, more_results = {'maxResults': 500}, True - while more_results: - for driver in self.drivers: - driver.connection.gce_params = params - all_nodes.extend(driver.list_nodes()) - more_results = 'pageToken' in params - return all_nodes - - def group_instances(self, zones=None): - '''Group all instances''' - groups = {} - meta = {} - meta["hostvars"] = {} - - for node in self.list_nodes(): - - # This check filters on the desired instance states defined in the - # config file with the instance_states config option. - # - # If the instance_states list is _empty_ then _ALL_ states are returned. - # - # If the instance_states list is _populated_ then check the current - # state against the instance_states list - if self.instance_states and not node.extra['status'] in self.instance_states: - continue - - # This check filters on the desired instance tags defined in the - # config file with the instance_tags config option, env var GCE_INSTANCE_TAGS, - # or as the cli param --instance-tags. - # - # If the instance_tags list is _empty_ then _ALL_ instances are returned. - # - # If the instance_tags list is _populated_ then check the current - # instance tags against the instance_tags list. If the instance has - # at least one tag from the instance_tags list, it is returned. - if self.instance_tags and not set(self.instance_tags) & set(node.extra['tags']): - continue - - name = node.name - - meta["hostvars"][name] = self.node_to_dict(node) - - zone = node.extra['zone'].name - - # To avoid making multiple requests per zone - # we list all nodes and then filter the results - if zones and zone not in zones: - continue - - if zone in groups: - groups[zone].append(name) - else: - groups[zone] = [name] - - tags = node.extra['tags'] - for t in tags: - if t.startswith('group-'): - tag = t[6:] - else: - tag = 'tag_%s' % t - if tag in groups: - groups[tag].append(name) - else: - groups[tag] = [name] - - net = node.extra['networkInterfaces'][0]['network'].split('/')[-1] - net = 'network_%s' % net - if net in groups: - groups[net].append(name) - else: - groups[net] = [name] - - machine_type = node.size - if machine_type in groups: - groups[machine_type].append(name) - else: - groups[machine_type] = [name] - - image = node.image or 'persistent_disk' - if image in groups: - groups[image].append(name) - else: - groups[image] = [name] - - status = node.extra['status'] - stat = 'status_%s' % status.lower() - if stat in groups: - groups[stat].append(name) - else: - groups[stat] = [name] - - for private_ip in node.private_ips: - groups[private_ip] = [name] - - if len(node.public_ips) >= 1: - for public_ip in node.public_ips: - groups[public_ip] = [name] - - groups["_meta"] = meta - - return groups - - def json_format_dict(self, data, pretty=False): - ''' Converts a dict to a JSON object and dumps it as a formatted - string ''' - - if pretty: - return json.dumps(data, sort_keys=True, indent=2) - else: - return json.dumps(data) - - -# Run the script -if __name__ == '__main__': - GceInventory() diff --git a/ansible_collections/community/google/tests/sanity/ignore-2.10.txt b/ansible_collections/community/google/tests/sanity/ignore-2.10.txt deleted file mode 100644 index c233efa5e..000000000 --- a/ansible_collections/community/google/tests/sanity/ignore-2.10.txt +++ /dev/null @@ -1,22 +0,0 @@ -plugins/modules/gce_eip.py pylint:blacklisted-name -plugins/modules/gce_eip.py validate-modules:parameter-list-no-elements -plugins/modules/gce_img.py pylint:blacklisted-name -plugins/modules/gce_instance_template.py pylint:blacklisted-name -plugins/modules/gce_instance_template.py validate-modules:doc-missing-type -plugins/modules/gce_instance_template.py validate-modules:parameter-list-no-elements -plugins/modules/gce_labels.py validate-modules:parameter-list-no-elements -plugins/modules/gce_lb.py pylint:blacklisted-name -plugins/modules/gce_lb.py validate-modules:parameter-list-no-elements -plugins/modules/gce_mig.py pylint:blacklisted-name -plugins/modules/gce_mig.py validate-modules:parameter-list-no-elements -plugins/modules/gce_net.py pylint:blacklisted-name -plugins/modules/gce_net.py validate-modules:parameter-list-no-elements -plugins/modules/gce_pd.py pylint:blacklisted-name -plugins/modules/gce_pd.py validate-modules:parameter-list-no-elements -plugins/modules/gce_snapshot.py pylint:blacklisted-name -plugins/modules/gce_snapshot.py validate-modules:parameter-list-no-elements -plugins/modules/gce_tag.py pylint:blacklisted-name -plugins/modules/gce_tag.py validate-modules:parameter-list-no-elements -plugins/modules/gcpubsub.py validate-modules:parameter-list-no-elements -plugins/modules/gcpubsub_info.py validate-modules:parameter-state-invalid-choice -scripts/inventory/gce.py pylint:blacklisted-name diff --git a/ansible_collections/community/google/tests/sanity/ignore-2.11.txt b/ansible_collections/community/google/tests/sanity/ignore-2.11.txt deleted file mode 100644 index c233efa5e..000000000 --- a/ansible_collections/community/google/tests/sanity/ignore-2.11.txt +++ /dev/null @@ -1,22 +0,0 @@ -plugins/modules/gce_eip.py pylint:blacklisted-name -plugins/modules/gce_eip.py validate-modules:parameter-list-no-elements -plugins/modules/gce_img.py pylint:blacklisted-name -plugins/modules/gce_instance_template.py pylint:blacklisted-name -plugins/modules/gce_instance_template.py validate-modules:doc-missing-type -plugins/modules/gce_instance_template.py validate-modules:parameter-list-no-elements -plugins/modules/gce_labels.py validate-modules:parameter-list-no-elements -plugins/modules/gce_lb.py pylint:blacklisted-name -plugins/modules/gce_lb.py validate-modules:parameter-list-no-elements -plugins/modules/gce_mig.py pylint:blacklisted-name -plugins/modules/gce_mig.py validate-modules:parameter-list-no-elements -plugins/modules/gce_net.py pylint:blacklisted-name -plugins/modules/gce_net.py validate-modules:parameter-list-no-elements -plugins/modules/gce_pd.py pylint:blacklisted-name -plugins/modules/gce_pd.py validate-modules:parameter-list-no-elements -plugins/modules/gce_snapshot.py pylint:blacklisted-name -plugins/modules/gce_snapshot.py validate-modules:parameter-list-no-elements -plugins/modules/gce_tag.py pylint:blacklisted-name -plugins/modules/gce_tag.py validate-modules:parameter-list-no-elements -plugins/modules/gcpubsub.py validate-modules:parameter-list-no-elements -plugins/modules/gcpubsub_info.py validate-modules:parameter-state-invalid-choice -scripts/inventory/gce.py pylint:blacklisted-name diff --git a/ansible_collections/community/google/tests/sanity/ignore-2.9.txt b/ansible_collections/community/google/tests/sanity/ignore-2.9.txt deleted file mode 100644 index f2dffaa46..000000000 --- a/ansible_collections/community/google/tests/sanity/ignore-2.9.txt +++ /dev/null @@ -1,11 +0,0 @@ -plugins/modules/gce_eip.py pylint:blacklisted-name -plugins/modules/gce_img.py pylint:blacklisted-name -plugins/modules/gce_instance_template.py pylint:blacklisted-name -plugins/modules/gce_instance_template.py validate-modules:doc-missing-type -plugins/modules/gce_lb.py pylint:blacklisted-name -plugins/modules/gce_mig.py pylint:blacklisted-name -plugins/modules/gce_net.py pylint:blacklisted-name -plugins/modules/gce_pd.py pylint:blacklisted-name -plugins/modules/gce_snapshot.py pylint:blacklisted-name -plugins/modules/gce_tag.py pylint:blacklisted-name -scripts/inventory/gce.py pylint:blacklisted-name diff --git a/ansible_collections/community/google/tests/unit/compat/__init__.py b/ansible_collections/community/google/tests/unit/compat/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/ansible_collections/community/google/tests/unit/compat/__init__.py +++ /dev/null diff --git a/ansible_collections/community/google/tests/unit/compat/mock.py b/ansible_collections/community/google/tests/unit/compat/mock.py deleted file mode 100644 index 0972cd2e8..000000000 --- a/ansible_collections/community/google/tests/unit/compat/mock.py +++ /dev/null @@ -1,122 +0,0 @@ -# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com> -# -# This file is part of Ansible -# -# Ansible 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. -# -# Ansible 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 Ansible. If not, see <http://www.gnu.org/licenses/>. - -# Make coding more python3-ish -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -''' -Compat module for Python3.x's unittest.mock module -''' -import sys - -# Python 2.7 - -# Note: Could use the pypi mock library on python3.x as well as python2.x. It -# is the same as the python3 stdlib mock library - -try: - # Allow wildcard import because we really do want to import all of mock's - # symbols into this compat shim - # pylint: disable=wildcard-import,unused-wildcard-import - from unittest.mock import * -except ImportError: - # Python 2 - # pylint: disable=wildcard-import,unused-wildcard-import - try: - from mock import * - except ImportError: - print('You need the mock library installed on python2.x to run tests') - - -# Prior to 3.4.4, mock_open cannot handle binary read_data -if sys.version_info >= (3,) and sys.version_info < (3, 4, 4): - file_spec = None - - def _iterate_read_data(read_data): - # Helper for mock_open: - # Retrieve lines from read_data via a generator so that separate calls to - # readline, read, and readlines are properly interleaved - sep = b'\n' if isinstance(read_data, bytes) else '\n' - data_as_list = [l + sep for l in read_data.split(sep)] - - if data_as_list[-1] == sep: - # If the last line ended in a newline, the list comprehension will have an - # extra entry that's just a newline. Remove this. - data_as_list = data_as_list[:-1] - else: - # If there wasn't an extra newline by itself, then the file being - # emulated doesn't have a newline to end the last line remove the - # newline that our naive format() added - data_as_list[-1] = data_as_list[-1][:-1] - - for line in data_as_list: - yield line - - def mock_open(mock=None, read_data=''): - """ - A helper function to create a mock to replace the use of `open`. It works - for `open` called directly or used as a context manager. - - The `mock` argument is the mock object to configure. If `None` (the - default) then a `MagicMock` will be created for you, with the API limited - to methods or attributes available on standard file handles. - - `read_data` is a string for the `read` methoddline`, and `readlines` of the - file handle to return. This is an empty string by default. - """ - def _readlines_side_effect(*args, **kwargs): - if handle.readlines.return_value is not None: - return handle.readlines.return_value - return list(_data) - - def _read_side_effect(*args, **kwargs): - if handle.read.return_value is not None: - return handle.read.return_value - return type(read_data)().join(_data) - - def _readline_side_effect(): - if handle.readline.return_value is not None: - while True: - yield handle.readline.return_value - for line in _data: - yield line - - global file_spec - if file_spec is None: - import _io - file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) - - if mock is None: - mock = MagicMock(name='open', spec=open) - - handle = MagicMock(spec=file_spec) - handle.__enter__.return_value = handle - - _data = _iterate_read_data(read_data) - - handle.write.return_value = None - handle.read.return_value = None - handle.readline.return_value = None - handle.readlines.return_value = None - - handle.read.side_effect = _read_side_effect - handle.readline.side_effect = _readline_side_effect() - handle.readlines.side_effect = _readlines_side_effect - - mock.return_value = handle - return mock diff --git a/ansible_collections/community/google/tests/unit/compat/unittest.py b/ansible_collections/community/google/tests/unit/compat/unittest.py deleted file mode 100644 index 98f08ad6a..000000000 --- a/ansible_collections/community/google/tests/unit/compat/unittest.py +++ /dev/null @@ -1,38 +0,0 @@ -# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com> -# -# This file is part of Ansible -# -# Ansible 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. -# -# Ansible 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 Ansible. If not, see <http://www.gnu.org/licenses/>. - -# Make coding more python3-ish -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -''' -Compat module for Python2.7's unittest module -''' - -import sys - -# Allow wildcard import because we really do want to import all of -# unittests's symbols into this compat shim -# pylint: disable=wildcard-import,unused-wildcard-import -if sys.version_info < (2, 7): - try: - # Need unittest2 on python2.6 - from unittest2 import * - except ImportError: - print('You need unittest2 installed on python2.6.x to run tests') -else: - from unittest import * diff --git a/ansible_collections/community/google/tests/unit/plugins/module_utils/__init__.py b/ansible_collections/community/google/tests/unit/plugins/module_utils/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/ansible_collections/community/google/tests/unit/plugins/module_utils/__init__.py +++ /dev/null diff --git a/ansible_collections/community/google/tests/unit/plugins/module_utils/test_auth.py b/ansible_collections/community/google/tests/unit/plugins/module_utils/test_auth.py deleted file mode 100644 index 845234ff7..000000000 --- a/ansible_collections/community/google/tests/unit/plugins/module_utils/test_auth.py +++ /dev/null @@ -1,162 +0,0 @@ -# -*- coding: utf-8 -*- -# (c) 2016, Tom Melendez (@supertom) <tom@supertom.com> -# -# 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 - -import os - -import pytest - -from ansible_collections.community.google.tests.unit.compat import mock, unittest -from ansible_collections.community.google.plugins.module_utils.gcp import (_get_gcp_ansible_credentials, _get_gcp_credentials, _get_gcp_environ_var, - _get_gcp_environment_credentials, - _validate_credentials_file) - -# Fake data/function used for testing -fake_env_data = {'GCE_EMAIL': 'gce-email'} - - -def fake_get_gcp_environ_var(var_name, default_value): - if var_name not in fake_env_data: - return default_value - else: - return fake_env_data[var_name] - -# Fake AnsibleModule for use in tests - - -class FakeModule(object): - class Params(): - data = {} - - def get(self, key, alt=None): - if key in self.data: - return self.data[key] - else: - return alt - - def __init__(self, data=None): - data = {} if data is None else data - - self.params = FakeModule.Params() - self.params.data = data - - def fail_json(self, **kwargs): - raise ValueError("fail_json") - - def deprecate(self, **kwargs): - return None - - -class GCPAuthTestCase(unittest.TestCase): - """Tests to verify different Auth mechanisms.""" - - def setup_method(self, method): - global fake_env_data - fake_env_data = {'GCE_EMAIL': 'gce-email'} - - def test_get_gcp_ansible_credentials(self): - input_data = {'service_account_email': 'mysa', - 'credentials_file': 'path-to-file.json', - 'project_id': 'my-cool-project'} - - module = FakeModule(input_data) - actual = _get_gcp_ansible_credentials(module) - expected = tuple(input_data.values()) - self.assertEqual(sorted(expected), sorted(actual)) - - def test_get_gcp_environ_var(self): - # Chose not to mock this so we could really verify that it - # works as expected. - existing_var_name = 'gcp_ansible_auth_test_54321' - non_existing_var_name = 'doesnt_exist_gcp_ansible_auth_test_12345' - os.environ[existing_var_name] = 'foobar' - self.assertEqual('foobar', _get_gcp_environ_var( - existing_var_name, None)) - del os.environ[existing_var_name] - self.assertEqual('default_value', _get_gcp_environ_var( - non_existing_var_name, 'default_value')) - - def test_validate_credentials_file(self): - # TODO(supertom): Only dealing with p12 here, check the other states - # of this function - module = FakeModule() - with mock.patch('ansible_collections.community.google.plugins.module_utils.gcp.open', - mock.mock_open(read_data='foobar'), create=True): - # pem condition, warning is suppressed with the return_value - credentials_file = '/foopath/pem.pem' - with self.assertRaises(ValueError): - _validate_credentials_file(module, - credentials_file=credentials_file, - require_valid_json=False, - check_libcloud=False) - - @mock.patch('ansible_collections.community.google.plugins.module_utils.gcp._get_gcp_environ_var', - side_effect=fake_get_gcp_environ_var) - def test_get_gcp_environment_credentials(self, mockobj): - global fake_env_data - - actual = _get_gcp_environment_credentials(None, None, None) - expected = tuple(['gce-email', None, None]) - self.assertEqual(expected, actual) - - fake_env_data = {'GCE_PEM_FILE_PATH': '/path/to/pem.pem'} - expected = tuple([None, '/path/to/pem.pem', None]) - actual = _get_gcp_environment_credentials(None, None, None) - self.assertEqual(expected, actual) - - # pem and creds are set, expect creds - fake_env_data = {'GCE_PEM_FILE_PATH': '/path/to/pem.pem', - 'GCE_CREDENTIALS_FILE_PATH': '/path/to/creds.json'} - expected = tuple([None, '/path/to/creds.json', None]) - actual = _get_gcp_environment_credentials(None, None, None) - self.assertEqual(expected, actual) - - # expect GOOGLE_APPLICATION_CREDENTIALS over PEM - fake_env_data = {'GCE_PEM_FILE_PATH': '/path/to/pem.pem', - 'GOOGLE_APPLICATION_CREDENTIALS': '/path/to/appcreds.json'} - expected = tuple([None, '/path/to/appcreds.json', None]) - actual = _get_gcp_environment_credentials(None, None, None) - self.assertEqual(expected, actual) - - # project tests - fake_env_data = {'GCE_PROJECT': 'my-project'} - expected = tuple([None, None, 'my-project']) - actual = _get_gcp_environment_credentials(None, None, None) - self.assertEqual(expected, actual) - - fake_env_data = {'GOOGLE_CLOUD_PROJECT': 'my-cloud-project'} - expected = tuple([None, None, 'my-cloud-project']) - actual = _get_gcp_environment_credentials(None, None, None) - self.assertEqual(expected, actual) - - # data passed in, picking up project id only - fake_env_data = {'GOOGLE_CLOUD_PROJECT': 'my-project'} - expected = tuple(['my-sa-email', '/path/to/creds.json', 'my-project']) - actual = _get_gcp_environment_credentials( - 'my-sa-email', '/path/to/creds.json', None) - self.assertEqual(expected, actual) - - @mock.patch('ansible_collections.community.google.plugins.module_utils.gcp._get_gcp_environ_var', - side_effect=fake_get_gcp_environ_var) - def test_get_gcp_credentials(self, mockobj): - global fake_env_data - - fake_env_data = {} - module = FakeModule() - module.params.data = {} - # Nothing is set, calls fail_json - with pytest.raises(ValueError): - _get_gcp_credentials(module) - - # project_id (only) is set from Ansible params. - module.params.data['project_id'] = 'my-project' - actual = _get_gcp_credentials( - module, require_valid_json=True, check_libcloud=False) - expected = {'service_account_email': '', - 'project_id': 'my-project', - 'credentials_file': ''} - self.assertEqual(expected, actual) diff --git a/ansible_collections/community/google/tests/unit/plugins/module_utils/test_utils.py b/ansible_collections/community/google/tests/unit/plugins/module_utils/test_utils.py deleted file mode 100644 index 7098f705d..000000000 --- a/ansible_collections/community/google/tests/unit/plugins/module_utils/test_utils.py +++ /dev/null @@ -1,361 +0,0 @@ -# -*- coding: utf-8 -*- -# (c) 2016, Tom Melendez <tom@supertom.com> -# -# 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 - -from ansible_collections.community.google.tests.unit.compat import mock, unittest -from ansible_collections.community.google.plugins.module_utils.gcp import check_min_pkg_version, GCPUtils, GCPInvalidURLError - - -def build_distribution(version): - obj = mock.MagicMock() - obj.version = '0.5.0' - return obj - - -class GCPUtilsTestCase(unittest.TestCase): - params_dict = { - 'url_map_name': 'foo_url_map_name', - 'description': 'foo_url_map description', - 'host_rules': [ - { - 'description': 'host rules description', - 'hosts': [ - 'www.example.com', - 'www2.example.com' - ], - 'path_matcher': 'host_rules_path_matcher' - } - ], - 'path_matchers': [ - { - 'name': 'path_matcher_one', - 'description': 'path matcher one', - 'defaultService': 'bes-pathmatcher-one-default', - 'pathRules': [ - { - 'service': 'my-one-bes', - 'paths': [ - '/', - '/aboutus' - ] - } - ] - }, - { - 'name': 'path_matcher_two', - 'description': 'path matcher two', - 'defaultService': 'bes-pathmatcher-two-default', - 'pathRules': [ - { - 'service': 'my-two-bes', - 'paths': [ - '/webapp', - '/graphs' - ] - } - ] - } - ] - } - - @mock.patch("pkg_resources.get_distribution", side_effect=build_distribution) - def test_check_minimum_pkg_version(self, mockobj): - self.assertTrue(check_min_pkg_version('foobar', '0.4.0')) - self.assertTrue(check_min_pkg_version('foobar', '0.5.0')) - self.assertFalse(check_min_pkg_version('foobar', '0.6.0')) - - def test_parse_gcp_url(self): - # region, resource, entity, method - input_url = 'https://www.googleapis.com/compute/v1/projects/myproject/regions/us-east1/instanceGroupManagers/my-mig/recreateInstances' - actual = GCPUtils.parse_gcp_url(input_url) - self.assertEqual('compute', actual['service']) - self.assertEqual('v1', actual['api_version']) - self.assertEqual('myproject', actual['project']) - self.assertEqual('us-east1', actual['region']) - self.assertEqual('instanceGroupManagers', actual['resource_name']) - self.assertEqual('my-mig', actual['entity_name']) - self.assertEqual('recreateInstances', actual['method_name']) - - # zone, resource, entity, method - input_url = 'https://www.googleapis.com/compute/v1/projects/myproject/zones/us-east1-c/instanceGroupManagers/my-mig/recreateInstances' - actual = GCPUtils.parse_gcp_url(input_url) - self.assertEqual('compute', actual['service']) - self.assertEqual('v1', actual['api_version']) - self.assertEqual('myproject', actual['project']) - self.assertEqual('us-east1-c', actual['zone']) - self.assertEqual('instanceGroupManagers', actual['resource_name']) - self.assertEqual('my-mig', actual['entity_name']) - self.assertEqual('recreateInstances', actual['method_name']) - - # global, resource - input_url = 'https://www.googleapis.com/compute/v1/projects/myproject/global/urlMaps' - actual = GCPUtils.parse_gcp_url(input_url) - self.assertEqual('compute', actual['service']) - self.assertEqual('v1', actual['api_version']) - self.assertEqual('myproject', actual['project']) - self.assertTrue('global' in actual) - self.assertTrue(actual['global']) - self.assertEqual('urlMaps', actual['resource_name']) - - # global, resource, entity - input_url = 'https://www.googleapis.com/compute/v1/projects/myproject/global/urlMaps/my-url-map' - actual = GCPUtils.parse_gcp_url(input_url) - self.assertEqual('myproject', actual['project']) - self.assertTrue('global' in actual) - self.assertTrue(actual['global']) - self.assertEqual('v1', actual['api_version']) - self.assertEqual('compute', actual['service']) - - # global URL, resource, entity, method_name - input_url = 'https://www.googleapis.com/compute/v1/projects/myproject/global/backendServices/mybackendservice/getHealth' - actual = GCPUtils.parse_gcp_url(input_url) - self.assertEqual('compute', actual['service']) - self.assertEqual('v1', actual['api_version']) - self.assertEqual('myproject', actual['project']) - self.assertTrue('global' in actual) - self.assertTrue(actual['global']) - self.assertEqual('backendServices', actual['resource_name']) - self.assertEqual('mybackendservice', actual['entity_name']) - self.assertEqual('getHealth', actual['method_name']) - - # no location in URL - input_url = 'https://www.googleapis.com/compute/v1/projects/myproject/targetHttpProxies/mytargetproxy/setUrlMap' - actual = GCPUtils.parse_gcp_url(input_url) - self.assertEqual('compute', actual['service']) - self.assertEqual('v1', actual['api_version']) - self.assertEqual('myproject', actual['project']) - self.assertFalse('global' in actual) - self.assertEqual('targetHttpProxies', actual['resource_name']) - self.assertEqual('mytargetproxy', actual['entity_name']) - self.assertEqual('setUrlMap', actual['method_name']) - - input_url = 'https://www.googleapis.com/compute/v1/projects/myproject/targetHttpProxies/mytargetproxy' - actual = GCPUtils.parse_gcp_url(input_url) - self.assertEqual('compute', actual['service']) - self.assertEqual('v1', actual['api_version']) - self.assertEqual('myproject', actual['project']) - self.assertFalse('global' in actual) - self.assertEqual('targetHttpProxies', actual['resource_name']) - self.assertEqual('mytargetproxy', actual['entity_name']) - - input_url = 'https://www.googleapis.com/compute/v1/projects/myproject/targetHttpProxies' - actual = GCPUtils.parse_gcp_url(input_url) - self.assertEqual('compute', actual['service']) - self.assertEqual('v1', actual['api_version']) - self.assertEqual('myproject', actual['project']) - self.assertFalse('global' in actual) - self.assertEqual('targetHttpProxies', actual['resource_name']) - - # test exceptions - no_projects_input_url = 'https://www.googleapis.com/compute/v1/not-projects/myproject/global/backendServices/mybackendservice/getHealth' - no_resource_input_url = 'https://www.googleapis.com/compute/v1/not-projects/myproject/global' - - no_resource_no_loc_input_url = 'https://www.googleapis.com/compute/v1/not-projects/myproject' - - with self.assertRaises(GCPInvalidURLError) as cm: - GCPUtils.parse_gcp_url(no_projects_input_url) - self.assertTrue(cm.exception, GCPInvalidURLError) - - with self.assertRaises(GCPInvalidURLError) as cm: - GCPUtils.parse_gcp_url(no_resource_input_url) - self.assertTrue(cm.exception, GCPInvalidURLError) - - with self.assertRaises(GCPInvalidURLError) as cm: - GCPUtils.parse_gcp_url(no_resource_no_loc_input_url) - self.assertTrue(cm.exception, GCPInvalidURLError) - - def test_params_to_gcp_dict(self): - - expected = { - 'description': 'foo_url_map description', - 'hostRules': [ - { - 'description': 'host rules description', - 'hosts': [ - 'www.example.com', - 'www2.example.com' - ], - 'pathMatcher': 'host_rules_path_matcher' - } - ], - 'name': 'foo_url_map_name', - 'pathMatchers': [ - { - 'defaultService': 'bes-pathmatcher-one-default', - 'description': 'path matcher one', - 'name': 'path_matcher_one', - 'pathRules': [ - { - 'paths': [ - '/', - '/aboutus' - ], - 'service': 'my-one-bes' - } - ] - }, - { - 'defaultService': 'bes-pathmatcher-two-default', - 'description': 'path matcher two', - 'name': 'path_matcher_two', - 'pathRules': [ - { - 'paths': [ - '/webapp', - '/graphs' - ], - 'service': 'my-two-bes' - } - ] - } - ] - } - - actual = GCPUtils.params_to_gcp_dict(self.params_dict, 'url_map_name') - self.assertEqual(expected, actual) - - def test_get_gcp_resource_from_methodId(self): - input_data = 'compute.urlMaps.list' - actual = GCPUtils.get_gcp_resource_from_methodId(input_data) - self.assertEqual('urlMaps', actual) - input_data = None - actual = GCPUtils.get_gcp_resource_from_methodId(input_data) - self.assertFalse(actual) - input_data = 666 - actual = GCPUtils.get_gcp_resource_from_methodId(input_data) - self.assertFalse(actual) - - def test_get_entity_name_from_resource_name(self): - input_data = 'urlMaps' - actual = GCPUtils.get_entity_name_from_resource_name(input_data) - self.assertEqual('urlMap', actual) - input_data = 'targetHttpProxies' - actual = GCPUtils.get_entity_name_from_resource_name(input_data) - self.assertEqual('targetHttpProxy', actual) - input_data = 'globalForwardingRules' - actual = GCPUtils.get_entity_name_from_resource_name(input_data) - self.assertEqual('forwardingRule', actual) - input_data = '' - actual = GCPUtils.get_entity_name_from_resource_name(input_data) - self.assertEqual(None, actual) - input_data = 666 - actual = GCPUtils.get_entity_name_from_resource_name(input_data) - self.assertEqual(None, actual) - - def test_are_params_equal(self): - params1 = {'one': 1} - params2 = {'one': 1} - actual = GCPUtils.are_params_equal(params1, params2) - self.assertTrue(actual) - - params1 = {'one': 1} - params2 = {'two': 2} - actual = GCPUtils.are_params_equal(params1, params2) - self.assertFalse(actual) - - params1 = {'three': 3, 'two': 2, 'one': 1} - params2 = {'one': 1, 'two': 2, 'three': 3} - actual = GCPUtils.are_params_equal(params1, params2) - self.assertTrue(actual) - - params1 = { - "creationTimestamp": "2017-04-21T11:19:20.718-07:00", - "defaultService": "https://www.googleapis.com/compute/v1/projects/myproject/global/backendServices/default-backend-service", - "description": "", - "fingerprint": "ickr_pwlZPU=", - "hostRules": [ - { - "description": "", - "hosts": [ - "*." - ], - "pathMatcher": "path-matcher-one" - } - ], - "id": "8566395781175047111", - "kind": "compute#urlMap", - "name": "newtesturlmap-foo", - "pathMatchers": [ - { - "defaultService": "https://www.googleapis.com/compute/v1/projects/myproject/global/backendServices/bes-pathmatcher-one-default", - "description": "path matcher one", - "name": "path-matcher-one", - "pathRules": [ - { - "paths": [ - "/data", - "/aboutus" - ], - "service": "https://www.googleapis.com/compute/v1/projects/myproject/global/backendServices/my-one-bes" - } - ] - } - ], - "selfLink": "https://www.googleapis.com/compute/v1/projects/myproject/global/urlMaps/newtesturlmap-foo" - } - params2 = { - "defaultService": "https://www.googleapis.com/compute/v1/projects/myproject/global/backendServices/default-backend-service", - "hostRules": [ - { - "description": "", - "hosts": [ - "*." - ], - "pathMatcher": "path-matcher-one" - } - ], - "name": "newtesturlmap-foo", - "pathMatchers": [ - { - "defaultService": "https://www.googleapis.com/compute/v1/projects/myproject/global/backendServices/bes-pathmatcher-one-default", - "description": "path matcher one", - "name": "path-matcher-one", - "pathRules": [ - { - "paths": [ - "/data", - "/aboutus" - ], - "service": "https://www.googleapis.com/compute/v1/projects/myproject/global/backendServices/my-one-bes" - } - ] - } - ], - } - - # params1 has exclude fields, params2 doesn't. Should be equal - actual = GCPUtils.are_params_equal(params1, params2) - self.assertTrue(actual) - - def test_filter_gcp_fields(self): - input_data = { - u'kind': u'compute#httpsHealthCheck', - u'description': u'', - u'timeoutSec': 5, - u'checkIntervalSec': 5, - u'port': 443, - u'healthyThreshold': 2, - u'host': u'', - u'requestPath': u'/', - u'unhealthyThreshold': 2, - u'creationTimestamp': u'2017-05-16T15:09:36.546-07:00', - u'id': u'8727093129334146639', - u'selfLink': u'https://www.googleapis.com/compute/v1/projects/myproject/global/httpsHealthChecks/myhealthcheck', - u'name': u'myhealthcheck'} - - expected = { - 'name': 'myhealthcheck', - 'checkIntervalSec': 5, - 'port': 443, - 'unhealthyThreshold': 2, - 'healthyThreshold': 2, - 'host': '', - 'timeoutSec': 5, - 'requestPath': '/'} - - actual = GCPUtils.filter_gcp_fields(input_data) - self.assertEqual(expected, actual) diff --git a/ansible_collections/community/google/tests/unit/plugins/modules/__init__.py b/ansible_collections/community/google/tests/unit/plugins/modules/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/ansible_collections/community/google/tests/unit/plugins/modules/__init__.py +++ /dev/null diff --git a/ansible_collections/community/google/tests/unit/plugins/modules/test_gce_tag.py b/ansible_collections/community/google/tests/unit/plugins/modules/test_gce_tag.py deleted file mode 100644 index 3a06f18d2..000000000 --- a/ansible_collections/community/google/tests/unit/plugins/modules/test_gce_tag.py +++ /dev/null @@ -1,66 +0,0 @@ -# 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 - -import unittest - -from ansible_collections.community.google.plugins.modules.gce_tag import _get_changed_items, _intersect_items, _union_items - - -class TestGCETag(unittest.TestCase): - """Unit tests for gce_tag module.""" - - def test_union_items(self): - """ - Combine items in both lists - removing duplicates. - """ - listA = [1, 2, 3, 4, 5, 8, 9] - listB = [1, 2, 3, 4, 5, 6, 7] - want = [1, 2, 3, 4, 5, 6, 7, 8, 9] - got = _union_items(listA, listB) - self.assertEqual(want, got) - - def test_intersect_items(self): - """ - All unique items from either list. - """ - listA = [1, 2, 3, 4, 5, 8, 9] - listB = [1, 2, 3, 4, 5, 6, 7] - want = [1, 2, 3, 4, 5] - got = _intersect_items(listA, listB) - self.assertEqual(want, got) - - # tags removed - new_tags = ['one', 'two'] - existing_tags = ['two'] - want = ['two'] # only remove the tag that was present - got = _intersect_items(existing_tags, new_tags) - self.assertEqual(want, got) - - def test_get_changed_items(self): - """ - All the items from left list that don't match - any item from the right list. - """ - listA = [1, 2, 3, 4, 5, 8, 9] - listB = [1, 2, 3, 4, 5, 6, 7] - want = [8, 9] - got = _get_changed_items(listA, listB) - self.assertEqual(want, got) - - # simulate new tags added - tags_to_add = ['one', 'two'] - existing_tags = ['two'] - want = ['one'] - got = _get_changed_items(tags_to_add, existing_tags) - self.assertEqual(want, got) - - # simulate removing tags - # specifying one tag on right that doesn't exist - tags_to_remove = ['one', 'two'] - existing_tags = ['two', 'three'] - want = ['three'] - got = _get_changed_items(existing_tags, tags_to_remove) - self.assertEqual(want, got) diff --git a/ansible_collections/community/google/tests/unit/requirements.txt b/ansible_collections/community/google/tests/unit/requirements.txt deleted file mode 100644 index 16494a447..000000000 --- a/ansible_collections/community/google/tests/unit/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -unittest2 ; python_version < '2.7'
\ No newline at end of file |