Retire Tripleo: remove repo content
TripleO project is retiring - https://review.opendev.org/c/openstack/governance/+/905145 this commit remove the content of this project repo Change-Id: I0b5c328f22d893f7d4e73935b31281f0f9826cd4
This commit is contained in:
parent
c73cb0f636
commit
fd05df0293
25
.gitignore
vendored
25
.gitignore
vendored
@ -1,25 +0,0 @@
|
|||||||
*.py[cod]
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
.coverage
|
|
||||||
cover
|
|
||||||
.tox
|
|
||||||
.testrepository
|
|
||||||
.stestr/
|
|
||||||
|
|
||||||
# Packages
|
|
||||||
.eggs
|
|
||||||
*.egg*
|
|
||||||
dist
|
|
||||||
build
|
|
||||||
eggs
|
|
||||||
parts
|
|
||||||
sdist
|
|
||||||
develop-eggs
|
|
||||||
.installed.cfg
|
|
||||||
lib
|
|
||||||
lib64
|
|
||||||
|
|
||||||
# built ansible collection
|
|
||||||
*.tar.gz
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
[DEFAULT]
|
|
||||||
test_path=./tests/unit
|
|
||||||
top_dir=./
|
|
@ -1,17 +0,0 @@
|
|||||||
If you would like to contribute to the development of OpenStack, you must
|
|
||||||
follow the steps in this page:
|
|
||||||
|
|
||||||
http://docs.openstack.org/infra/manual/developers.html
|
|
||||||
|
|
||||||
If you already have a good understanding of how the system works and your
|
|
||||||
OpenStack accounts are set up, you can skip to the development workflow
|
|
||||||
section of this documentation to learn how changes to OpenStack should be
|
|
||||||
submitted for review via the Gerrit tool:
|
|
||||||
|
|
||||||
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
|
||||||
|
|
||||||
Pull requests submitted through GitHub will be ignored.
|
|
||||||
|
|
||||||
Bugs should be filed on Launchpad, not GitHub:
|
|
||||||
|
|
||||||
https://bugs.launchpad.net/tripleo
|
|
175
LICENSE
175
LICENSE
@ -1,175 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
@ -1,3 +0,0 @@
|
|||||||
global-exclude *.py[cod]
|
|
||||||
global-exclude __pycache__
|
|
||||||
recursive-exclude plugins *
|
|
76
README.rst
76
README.rst
@ -1,70 +1,10 @@
|
|||||||
tripleo-repos
|
This project is no longer maintained.
|
||||||
=============
|
|
||||||
|
|
||||||
A tool for managing tripleo repos from places like RDO Trunk and Ceph.
|
The contents of this repository are still available in the Git
|
||||||
|
source code management system. To see the contents of this
|
||||||
|
repository before it reached its end of life, please check out the
|
||||||
|
previous commit with "git checkout HEAD^1".
|
||||||
|
|
||||||
See: https://blogs.rdoproject.org/2016/04/newbie-in-rdo-2-rdo-trunk-from-a-bird-s-eye-view/
|
For any further questions, please email
|
||||||
|
openstack-discuss@lists.openstack.org or join #openstack-dev on
|
||||||
Also ensures yum-plugin-priorities is installed since the RDO Trunk repos
|
OFTC.
|
||||||
require that to work sanely.
|
|
||||||
|
|
||||||
.. note:: The tool will remove any delorean* repos at the target location
|
|
||||||
to avoid conflicts with older repos. This means you must specify
|
|
||||||
all of the repos you want to enable in one tripleo-repos call.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
Install TripleO CI testing repos for UBI-8 by the distro specific path::
|
|
||||||
|
|
||||||
tripleo-repos -d ubi8 tripleo-ci-testing --output-path /etc/distro.repos.d
|
|
||||||
|
|
||||||
Install current master RDO Trunk repo and the deps repo::
|
|
||||||
|
|
||||||
tripleo-repos current
|
|
||||||
|
|
||||||
Install current-tripleo RDO Trunk repo and the deps repo::
|
|
||||||
|
|
||||||
tripleo-repos current-tripleo
|
|
||||||
|
|
||||||
Install the current-tripleo-dev repo. This will also pull current and deps,
|
|
||||||
and will adjust the priorities of each repo appropriately::
|
|
||||||
|
|
||||||
tripleo-repos current-tripleo-dev
|
|
||||||
|
|
||||||
Install the mitaka RDO Trunk repo and deps::
|
|
||||||
|
|
||||||
tripleo-repos -b mitaka current
|
|
||||||
|
|
||||||
Write repos to a different path::
|
|
||||||
|
|
||||||
tripleo-repos -o ~/test-repos current
|
|
||||||
|
|
||||||
Install the current-tripleo, deps, and ceph repos. NOTE: The Ceph repo is
|
|
||||||
installed from a package and thus does not respect -o::
|
|
||||||
|
|
||||||
tripleo-repos current-tripleo ceph
|
|
||||||
|
|
||||||
TripleO
|
|
||||||
```````
|
|
||||||
|
|
||||||
To use this for TripleO development, replace the tripleo.sh --repo-setup
|
|
||||||
step with the following::
|
|
||||||
|
|
||||||
git clone https://github.com/cybertron/tripleo-repos
|
|
||||||
cd tripleo-repos
|
|
||||||
sudo ./setup.py install
|
|
||||||
sudo tripleo-repos current-tripleo-dev ceph
|
|
||||||
|
|
||||||
Now you're ready to install the undercloud::
|
|
||||||
|
|
||||||
tripleo.sh --undercloud
|
|
||||||
|
|
||||||
And to build images::
|
|
||||||
|
|
||||||
export OVERCLOUD_IMAGES_DIB_YUM_REPO_CONF="$(ls /etc/yum.repos.d/delorean* /etc/yum.repos.d/CentOS-Ceph-*)"
|
|
||||||
tripleo.sh --overcloud-images
|
|
||||||
|
|
||||||
.. note:: This is a tool for bootstrapping the repo setup for TripleO,
|
|
||||||
so it should not have any runtime OpenStack dependencies
|
|
||||||
or we end up in a chicken-and-egg pickle, and let's be honest - no one wants a
|
|
||||||
chicken and egg pickle.
|
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
# Used for testing to avoid using one from outside repository
|
|
||||||
[defaults]
|
|
||||||
# Avoid noise which can make debugging test failures harder
|
|
||||||
deprecation_warnings=False
|
|
@ -1,3 +0,0 @@
|
|||||||
sphinx>=1.8.0,!=2.1.0 # BSD
|
|
||||||
openstackdocstheme>=2.0.0 # Apache-2.0
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('../..'))
|
|
||||||
# -- General configuration ----------------------------------------------------
|
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be
|
|
||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
|
||||||
extensions = [
|
|
||||||
'sphinx.ext.autodoc',
|
|
||||||
#'sphinx.ext.intersphinx',
|
|
||||||
'openstackdocstheme'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
|
||||||
# text edit cycles.
|
|
||||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
|
||||||
|
|
||||||
# The suffix of source filenames.
|
|
||||||
source_suffix = '.rst'
|
|
||||||
|
|
||||||
# The master toctree document.
|
|
||||||
master_doc = 'index'
|
|
||||||
|
|
||||||
# General information about the project.
|
|
||||||
project = u'tripleo-repos'
|
|
||||||
copyright = u'2017, OpenStack Foundation'
|
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
|
||||||
add_function_parentheses = True
|
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
|
||||||
# unit titles (such as .. function::).
|
|
||||||
add_module_names = True
|
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
|
||||||
pygments_style = 'sphinx'
|
|
||||||
|
|
||||||
html_theme = 'openstackdocs'
|
|
||||||
repository_name = 'openstack/tripleo-repos'
|
|
||||||
bug_project = 'tripleo'
|
|
||||||
|
|
||||||
# -- Options for HTML output --------------------------------------------------
|
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
|
||||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
|
||||||
# html_theme_path = ["."]
|
|
||||||
# html_theme = '_theme'
|
|
||||||
# html_static_path = ['static']
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
|
||||||
htmlhelp_basename = '%sdoc' % project
|
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
|
||||||
# (source start file, target name, title, author, documentclass
|
|
||||||
# [howto/manual]).
|
|
||||||
latex_documents = [
|
|
||||||
('index',
|
|
||||||
'%s.tex' % project,
|
|
||||||
u'%s Documentation' % project,
|
|
||||||
u'OpenStack Foundation', 'manual'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
|
||||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
|
@ -1,4 +0,0 @@
|
|||||||
============
|
|
||||||
Contributing
|
|
||||||
============
|
|
||||||
.. include:: ../../CONTRIBUTING.rst
|
|
@ -1,25 +0,0 @@
|
|||||||
.. tripleo-repos documentation master file, created by
|
|
||||||
sphinx-quickstart on Tue Jul 9 22:26:36 2013.
|
|
||||||
You can adapt this file completely to your liking, but it should at least
|
|
||||||
contain the root `toctree` directive.
|
|
||||||
|
|
||||||
Welcome to tripleo-repos's documentation!
|
|
||||||
=========================================
|
|
||||||
|
|
||||||
Contents:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
readme
|
|
||||||
installation
|
|
||||||
usage
|
|
||||||
contributing
|
|
||||||
|
|
||||||
Indices and tables
|
|
||||||
==================
|
|
||||||
|
|
||||||
* :ref:`genindex`
|
|
||||||
* :ref:`modindex`
|
|
||||||
* :ref:`search`
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
|||||||
============
|
|
||||||
Installation
|
|
||||||
============
|
|
||||||
|
|
||||||
At the command line::
|
|
||||||
|
|
||||||
$ pip install tripleo-repos
|
|
||||||
|
|
||||||
Or, if you have virtualenvwrapper installed::
|
|
||||||
|
|
||||||
$ mkvirtualenv tripleo-repos
|
|
||||||
$ pip install tripleo-repos
|
|
@ -1 +0,0 @@
|
|||||||
.. include:: ../../README.rst
|
|
@ -1,3 +0,0 @@
|
|||||||
=====
|
|
||||||
Usage
|
|
||||||
=====
|
|
@ -1,86 +0,0 @@
|
|||||||
# tripleo.repos.get_hash
|
|
||||||
|
|
||||||
## What is tripleo.repos.get_hash
|
|
||||||
|
|
||||||
This utility is meant for use by TripleO deployments, particularly in zuul
|
|
||||||
continuous integration jobs. Given an [RDO named tag](https://docs.openstack.org/tripleo-docs/latest/ci/stages-overview.html#rdo-dlrn-promotion-criteria),
|
|
||||||
such as 'current-tripleo' or 'tripleo-ci-testing' it will return the hash
|
|
||||||
information, including the commit, distro and full hashes where available.
|
|
||||||
|
|
||||||
It includes a simple command line interface. If you clone the source you can
|
|
||||||
try it out of the box without installation invoking it as a module:
|
|
||||||
```
|
|
||||||
tripleo-get-hash # by default centos8, master, current-tripleo.
|
|
||||||
tripleo-get-hash --component tripleo --release victoria --os-version centos8
|
|
||||||
tripleo-get-hash --release master --os-version centos7
|
|
||||||
tripleo-get-hash --release train # by default centos8
|
|
||||||
tripleo-get-hash --os-version rhel8 --release osp16-2 --dlrn-url http://osp-trunk.hosted.upshift.rdu2.redhat.com
|
|
||||||
tripleo-get-hash --help
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
#### Install using setup.py
|
|
||||||
|
|
||||||
It is recommended to perform a user/local installation using python setup.py
|
|
||||||
to avoid the use of sudo. However you may need to set your PYTHONPATH depending
|
|
||||||
on where the python code is installed on your system.
|
|
||||||
|
|
||||||
```
|
|
||||||
python setup.py install --user
|
|
||||||
tripleo-get-hash --help
|
|
||||||
```
|
|
||||||
The tripleo-get-hash utility uses a yaml configuration file named 'config.yaml'.
|
|
||||||
If you install this utility using --user as above, the configuration file
|
|
||||||
is placed in $HOME/.local/etc/tripleo_get_hash/config.yaml (on fedora).
|
|
||||||
If this cannot be found then the config is used directly from the source directory.
|
|
||||||
When you invoke tripleo-get-hash it will tell you which config is in use:
|
|
||||||
```
|
|
||||||
$ tripleo-get-hash
|
|
||||||
2021-10-15 16:22:23,724 - tripleo-get-hash - INFO - Using config file at /home/username/.local/etc/tripleo_get_hash/config.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Install using pip
|
|
||||||
|
|
||||||
You can also install using python pip - you can see the
|
|
||||||
[tripleo-get-hash module here](https://pypi.org/project/tripleo-repos/)
|
|
||||||
|
|
||||||
```
|
|
||||||
pip install tripleo-repos --user
|
|
||||||
```
|
|
||||||
|
|
||||||
After installation you can invoke tripleo-get-hash --help to see the various
|
|
||||||
options:
|
|
||||||
```
|
|
||||||
tripleo-get-hash --help
|
|
||||||
```
|
|
||||||
|
|
||||||
By default this queries the delorean server at "https://trunk.rdoproject.org",
|
|
||||||
with this URL specified in config.yaml. To use a different delorean server you
|
|
||||||
can either update config.yaml or use the --dlrn-url parameter to the cli. If
|
|
||||||
instead you are instantiating TripleOHashInfo objects in code, you can create
|
|
||||||
the objects passing an existing 'config' dictionary. Note this has to contain
|
|
||||||
all of constants.CONFIG_KEYS to avoid explosions.
|
|
||||||
|
|
||||||
## Ansible Module
|
|
||||||
|
|
||||||
It is required that you install `tripleo.repos` collection to use the ansible
|
|
||||||
module.
|
|
||||||
|
|
||||||
See the [example playbook](https://opendev.org/openstack/tripleo-repos/src/branch/master/playbooks/example_get_hash.yaml) included here for examples of
|
|
||||||
usage. You can also test the ansible module is available and working correctly
|
|
||||||
from using shell:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ ansible localhost -m tripleo.repos.get_hash -a "component=compute release=victoria"
|
|
||||||
localhost | SUCCESS => {
|
|
||||||
"changed": false,
|
|
||||||
"commit_hash": "e954a56fec69637ebd671643d41bb0ecc85a2656",
|
|
||||||
"distro_hash": "de7baf4889fba4d42ac39c9e912c42e38abb5193",
|
|
||||||
"dlrn_url": "https://trunk.rdoproject.org/centos8-victoria/component/compute/current-tripleo/commit.yaml",
|
|
||||||
"error": "",
|
|
||||||
"extended_hash": "None",
|
|
||||||
"full_hash": "e954a56fec69637ebd671643d41bb0ecc85a2656_de7baf48",
|
|
||||||
"success": true
|
|
||||||
}
|
|
||||||
```
|
|
@ -1,107 +0,0 @@
|
|||||||
# tripleo.repos.yum_config
|
|
||||||
|
|
||||||
*tripleo-yum-config* utility was designed to simplify the way that TripleO
|
|
||||||
deployments manage their yum configuration. This tool helps on updating
|
|
||||||
specific configuration options for different yum configuration files like yum
|
|
||||||
repos, yum modules and yum global configuration file.
|
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
### Using as a python module
|
|
||||||
|
|
||||||
It is possible to use *tripleo-yum-config* as a standalone module by cloning
|
|
||||||
its repository and invoking in command line:
|
|
||||||
* **repo**
|
|
||||||
|
|
||||||
This subcommand lets you enable or disable a repo and sets its configuration options.
|
|
||||||
The *tripleo-yum-config* module will search for the provided repo name in all *.repo* files at REPO_DIR_PATH.
|
|
||||||
Optionally, you can provide a path where your repo files live or specify the full path of the repo file.
|
|
||||||
By default REPO_DIR_PATH is set to */etc/yum.repos.d/*.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
```
|
|
||||||
sudo python -m tripleo_yum_config repo --name appstream --enable --set-opts baseurl=http://newbaseurl exclude="package*"
|
|
||||||
sudo python -m tripleo_yum_config repo --name epel --disable --config-dir-path=/path/to/yum.repos.d
|
|
||||||
```
|
|
||||||
The parameter *--down-url* can be used to retrieve a configuration file from a URL and populate the destination
|
|
||||||
configuration file with all its content. When used together with *--name*, only the requested repo name will be
|
|
||||||
updated in the process.
|
|
||||||
Examples:
|
|
||||||
```
|
|
||||||
sudo python -m tripleo_yum_config repo --down-url http://remoterepofile.repo --enable --set-opts priority=20 --config-file-path=/path/to/file.repo
|
|
||||||
sudo python -m tripleo_yum_config repo --name appstream --down-url http://remoterepofile.repo --enable
|
|
||||||
```
|
|
||||||
|
|
||||||
* **module**
|
|
||||||
|
|
||||||
This subcommand lets you enable, disable, remove, install or reset a module.
|
|
||||||
Depending on the selected operation and module, the optional parameters 'stream' or 'profile' will also need to be provided:
|
|
||||||
1. when enabling a module, the *stream* version will be required if the module has zero or more than one default stream.
|
|
||||||
2. when installing a module, the *profile* will be required if the enabled stream has no default profile set.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
```
|
|
||||||
sudo tripleo-yum-config module remove tomcat
|
|
||||||
sudo tripleo-yum-config module disable tomcat
|
|
||||||
sudo tripleo-yum-config module enable nginx --stream mainline
|
|
||||||
sudo tripleo-yum-config module install nginx --profile common
|
|
||||||
```
|
|
||||||
* **global**
|
|
||||||
|
|
||||||
This subcommand lets you set any global yum/dnf configuration value under *[main]* section.
|
|
||||||
If no configuration file is found by the module, a new one is created and populated.
|
|
||||||
Optionally you can also provide the path to the configuration file.
|
|
||||||
By default CONFIG_FILE_PATH is set to */etc/yum.conf*
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
sudo python -m tripleo_yum_config global --set-opts keepcache=1 cachedir="/var/cache/dnf"
|
|
||||||
```
|
|
||||||
|
|
||||||
* **enable-compose-repos**
|
|
||||||
|
|
||||||
This subcommand will enable a list os yum repos based on the metadata retrieved from the `compose-url`.
|
|
||||||
The *tripleo-yum-config* module will create new repo files at REPO_DIR_PATH and enable them.
|
|
||||||
Optionally, you can provide a path where your repo files live, specify the variants that should be created and which repos need to be disabled afterwards.
|
|
||||||
By default REPO_DIR_PATH is set to */etc/yum.repos.d/*.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
sudo python -m tripleo_yum_config enable-compose-repos --compose-url https://composes.centos.org/latest-CentOS-Stream-8/compose/ --release centos-stream-8 --disable-all-conflicting
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Install using setup.py
|
|
||||||
|
|
||||||
Installation using python setup.py requires sudo, because the python source
|
|
||||||
is installed at /usr/local/lib/python.
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo python setup.py install
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Install using pip
|
|
||||||
Alternatively you can install tripleo-yum-config with python pip:
|
|
||||||
```
|
|
||||||
pip install tripleo-repos --user
|
|
||||||
```
|
|
||||||
See PyPI [tripleo-repos](https://pypi.org/project/tripleo-repos/)
|
|
||||||
project for more details.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
The utility provides a command line interface with various options. You can
|
|
||||||
invoke *tripleo-yum-config --help* to see all the available commands.
|
|
||||||
```
|
|
||||||
tripleo-yum-config --help
|
|
||||||
```
|
|
||||||
|
|
||||||
## Ansible Module
|
|
||||||
|
|
||||||
It is required that you install `tripleo.repos` collection to use the ansible
|
|
||||||
module.
|
|
||||||
|
|
||||||
An ansible module [tripleo.repos.yum_config](https://opendev.org/openstack/tripleo-repos/src/branch/master/modules/module/modules/yum_config.py)
|
|
||||||
is available for you when you install `tripleo.repos` collection.
|
|
||||||
|
|
||||||
An [example playbook](https://opendev.org/openstack/tripleo-repos/src/branch/master/playbooks/example_yum_config.yaml)
|
|
||||||
is available to assist with module usage.
|
|
39
galaxy.yml
39
galaxy.yml
@ -1,39 +0,0 @@
|
|||||||
name: repos
|
|
||||||
namespace: tripleo
|
|
||||||
version: 0.0.5
|
|
||||||
readme: README.md
|
|
||||||
authors:
|
|
||||||
- Red Hat
|
|
||||||
description: TripleO Repos
|
|
||||||
build_ignore:
|
|
||||||
- "*.egg-info"
|
|
||||||
- .DS_Store
|
|
||||||
- .eggs
|
|
||||||
- .gitignore
|
|
||||||
- .gitreview
|
|
||||||
- .mypy_cache
|
|
||||||
- .pytest_cache
|
|
||||||
- .stestr
|
|
||||||
- .stestr.conf
|
|
||||||
- .tox
|
|
||||||
- .vscode
|
|
||||||
- MANIFEST.in
|
|
||||||
- build
|
|
||||||
- dist
|
|
||||||
- doc
|
|
||||||
- report.html
|
|
||||||
- setup.cfg
|
|
||||||
- setup.py
|
|
||||||
- "tests/unit/*.*"
|
|
||||||
- README.rst
|
|
||||||
- tox.ini
|
|
||||||
- tripleo_repos
|
|
||||||
- zuul.d
|
|
||||||
# excluded because galaxy server refuses uploads with __main___ inside
|
|
||||||
- plugins/module_utils/tripleo_repos/get_hash/__main__.py
|
|
||||||
- plugins/module_utils/tripleo_repos/yum_config/__main__.py
|
|
||||||
# that is not needed for ansible modules and it would upset sanity (pylint)
|
|
||||||
- plugins/module_utils/tripleo_repos/main.py
|
|
||||||
|
|
||||||
repository: https://opendev.org/openstack/tripleo-repos
|
|
||||||
license_file: LICENSE
|
|
@ -1 +0,0 @@
|
|||||||
requires_ansible: ">=2.9.0"
|
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Get the content of {{ item.name }} repo
|
|
||||||
command: "cat {{ item.path }}"
|
|
||||||
register: file_output
|
|
||||||
|
|
||||||
- name: Print {{ item.name }} repo content
|
|
||||||
debug:
|
|
||||||
msg:
|
|
||||||
- "Content of {{ item.name }} repo located at '{{ item.path }}'"
|
|
||||||
- "{{ file_output.stdout_lines }}"
|
|
||||||
|
|
||||||
- set_fact:
|
|
||||||
tmp_repo_file: /tmp/{{ item.name|lower }}.temp
|
|
||||||
|
|
||||||
- name: Retrieve remote repo ini file
|
|
||||||
fetch:
|
|
||||||
src: "{{ item.path }}"
|
|
||||||
dest: "{{ tmp_repo_file }}"
|
|
||||||
flat: yes
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "{{ lookup('ini', '{{ item.key }} section={{ item.section|lower }} file={{ tmp_repo_file }}') }} == {{ item.value }}"
|
|
@ -1,86 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Converge
|
|
||||||
hosts: all
|
|
||||||
tasks:
|
|
||||||
- name: "Check get_hash"
|
|
||||||
tripleo.repos.get_hash:
|
|
||||||
release: master
|
|
||||||
|
|
||||||
- name: "Check get_hash with invalid url"
|
|
||||||
tripleo.repos.get_hash:
|
|
||||||
release: master
|
|
||||||
dlrn_url: 'https://httpbin.org/status/404'
|
|
||||||
register: result
|
|
||||||
failed_when: result is success
|
|
||||||
|
|
||||||
- name: "Test disable system repo"
|
|
||||||
become: true
|
|
||||||
tripleo.repos.yum_config:
|
|
||||||
type: repo
|
|
||||||
name: "{{ 'rt' if (ansible_distribution_major_version is version(8, '>=')) else 'cr' }}"
|
|
||||||
enabled: false
|
|
||||||
tags:
|
|
||||||
# TODO: fix yum_config to correctly report changed state and uncomment
|
|
||||||
# the line below which disables molecule idempotence test.
|
|
||||||
- molecule-idempotence-notest
|
|
||||||
|
|
||||||
- name: "Test create new repo file"
|
|
||||||
become: true
|
|
||||||
tripleo.repos.yum_config:
|
|
||||||
type: repo
|
|
||||||
name: "fakerepo"
|
|
||||||
# Keep it disabled to not affect any other test
|
|
||||||
enabled: false
|
|
||||||
file_path: "/etc/yum.repos.d/fake_repo.repo"
|
|
||||||
set_options:
|
|
||||||
baseurl: "http://fakemirror/fakerepo"
|
|
||||||
priority: "10"
|
|
||||||
gpgcheck: "0"
|
|
||||||
exclude: "fakepkg*"
|
|
||||||
tags:
|
|
||||||
# TODO: fix yum_config to correctly report changed state and uncomment
|
|
||||||
# the line below which disables molecule idempotence test.
|
|
||||||
- molecule-idempotence-notest
|
|
||||||
|
|
||||||
- name: "Test yum-config global config"
|
|
||||||
become: true
|
|
||||||
tripleo.repos.yum_config:
|
|
||||||
type: global
|
|
||||||
file_path: "{{ '/etc/dnf/dnf.conf' if (ansible_distribution_major_version is version(8, '>=')) else '/etc/yum.conf' }}"
|
|
||||||
set_options:
|
|
||||||
skip_if_unavailable: "False"
|
|
||||||
fake_conf: "True"
|
|
||||||
tags:
|
|
||||||
# TODO: fix yum_config to correctly report changed state and uncomment
|
|
||||||
# the line below which disables molecule idempotence test.
|
|
||||||
- molecule-idempotence-notest
|
|
||||||
|
|
||||||
- name: "Test yum_config enable-compose-repos"
|
|
||||||
become: true
|
|
||||||
tripleo.repos.yum_config:
|
|
||||||
type: enable-compose-repos
|
|
||||||
compose_url: https://composes.centos.org/latest-CentOS-Stream-8/compose/
|
|
||||||
centos_release: centos-stream-8
|
|
||||||
variants:
|
|
||||||
- AppStream
|
|
||||||
- BaseOS
|
|
||||||
disable_repos:
|
|
||||||
- /etc/yum.repos.d/CentOS-Stream-AppStream.repo
|
|
||||||
- /etc/yum.repos.d/CentOS-Stream-BaseOS.repo
|
|
||||||
tags:
|
|
||||||
- molecule-idempotence-notest
|
|
||||||
# NOTE: operation available only for CentOS >= 8
|
|
||||||
when: ansible_distribution_major_version is version(8, '>=')
|
|
||||||
- name: "Test create repo from repo file"
|
|
||||||
become: true
|
|
||||||
tripleo.repos.yum_config:
|
|
||||||
type: repo
|
|
||||||
enabled: true
|
|
||||||
file_path: "/etc/yum.repos.d/delorean.repo"
|
|
||||||
down_url: "https://trunk.rdoproject.org/centos8-master/current-tripleo/delorean.repo"
|
|
||||||
set_options:
|
|
||||||
priority: "20"
|
|
||||||
tags:
|
|
||||||
# TODO: fix yum_config to correctly report changed state and uncomment
|
|
||||||
# the line below which disables molecule idempotence test.
|
|
||||||
- molecule-idempotence-notest
|
|
@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
dependency:
|
|
||||||
name: galaxy
|
|
||||||
driver:
|
|
||||||
name: docker
|
|
||||||
platforms:
|
|
||||||
- name: instance
|
|
||||||
image: quay.io/centos/centos:stream8
|
|
||||||
- name: instance_c7
|
|
||||||
image: quay.io/centos/centos:centos7
|
|
||||||
provisioner:
|
|
||||||
name: ansible
|
|
||||||
verifier:
|
|
||||||
name: ansible
|
|
@ -1,7 +0,0 @@
|
|||||||
- hosts: instance_c7
|
|
||||||
tasks:
|
|
||||||
- name: Update ca-certificates
|
|
||||||
become: true
|
|
||||||
package:
|
|
||||||
name: ca-certificates
|
|
||||||
state: latest
|
|
@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Verify
|
|
||||||
hosts: all
|
|
||||||
tasks:
|
|
||||||
- name: Check if RT or CR repos are disabled
|
|
||||||
vars:
|
|
||||||
section_name: "{{ 'rt' if (ansible_distribution_major_version is version(8, '>=')) else 'cr' }}"
|
|
||||||
repo_path: /etc/yum.repos.d/CentOS-{{ 'Stream-RealTime' if (ansible_distribution_major_version is version(8, '>=')) else 'CR' }}.repo
|
|
||||||
include_tasks: assert_ini_key_value.yml
|
|
||||||
with_items:
|
|
||||||
- name: "{{ section_name|upper }}"
|
|
||||||
path: "{{ repo_path }}"
|
|
||||||
section: "{{ section_name }}"
|
|
||||||
key: enabled
|
|
||||||
value: "0"
|
|
||||||
|
|
||||||
- name: Check if yum/dnf conf file was updated
|
|
||||||
vars:
|
|
||||||
conf_file_path: "{{ '/etc/dnf/dnf.conf' if (ansible_distribution_major_version is version(8, '>=')) else '/etc/yum.conf' }}"
|
|
||||||
include_tasks: assert_ini_key_value.yml
|
|
||||||
with_items:
|
|
||||||
- name: global_conf
|
|
||||||
path: "{{ conf_file_path }}"
|
|
||||||
section: main
|
|
||||||
key: skip_if_unavailable
|
|
||||||
value: "False"
|
|
||||||
- name: global_conf
|
|
||||||
path: "{{ conf_file_path }}"
|
|
||||||
section: main
|
|
||||||
key: fake_conf
|
|
||||||
value: "True"
|
|
||||||
|
|
||||||
- name: Validate compose repos outputs
|
|
||||||
include_tasks: verify_compose_repos.yml
|
|
||||||
with_items:
|
|
||||||
- "AppStream"
|
|
||||||
- "BaseOS"
|
|
||||||
# NOTE: operation available only for CentOS >= 8
|
|
||||||
when: ansible_distribution_major_version is version(8, '>=')
|
|
||||||
- name: Check if 'priority' was set to 20 in 'delorean-component-compute'
|
|
||||||
include_tasks: assert_ini_key_value.yml
|
|
||||||
with_items:
|
|
||||||
- name: "delorean-component-compute"
|
|
||||||
path: "/etc/yum.repos.d/delorean.repo"
|
|
||||||
section: "delorean-component-compute"
|
|
||||||
key: priority
|
|
||||||
value: "20"
|
|
@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
- name: "Search for {{ item }} repos"
|
|
||||||
find:
|
|
||||||
paths: "/etc/yum.repos.d"
|
|
||||||
file_type: file
|
|
||||||
use_regex: yes
|
|
||||||
patterns:
|
|
||||||
- "^CentOS-Stream.*{{ item }}.*.repo$"
|
|
||||||
excludes:
|
|
||||||
- "CentOS-Stream-{{ item }}.repo"
|
|
||||||
register: compose_repos
|
|
||||||
failed_when: compose_repos.files|length != 1
|
|
||||||
|
|
||||||
- name: Validate repo file configuration
|
|
||||||
include_tasks: assert_ini_key_value.yml
|
|
||||||
with_items:
|
|
||||||
- name: "{{ item }}"
|
|
||||||
path: "{{ compose_repos.files[0].path }}"
|
|
||||||
section: "{{ item|lower }}"
|
|
||||||
key: enabled
|
|
||||||
value: "1"
|
|
||||||
- name: "{{ item }}"
|
|
||||||
path: "/etc/yum.repos.d/CentOS-Stream-{{ item }}.repo"
|
|
||||||
section: "{{ item|lower }}"
|
|
||||||
key: enabled
|
|
||||||
value: "0"
|
|
@ -1,40 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Example usage for tripleo-get-hash python module
|
|
||||||
hosts: localhost
|
|
||||||
tasks:
|
|
||||||
- name: get component-ci-testing for victoria compute component
|
|
||||||
tripleo.repos.get_hash:
|
|
||||||
os_version: centos8 # default: centos8
|
|
||||||
release: victoria # default: master
|
|
||||||
component: compute # default: None
|
|
||||||
tag: component-ci-testing # default: current-tripleo
|
|
||||||
register: component_ci_testing_victoria_compute
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
msg: "Centos8 component-ci-testing victoria compute component: {{ component_ci_testing_victoria_compute['full_hash'] }}"
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
var: component_ci_testing_victoria_compute
|
|
||||||
|
|
||||||
- name: get centos7 tripleo-ci-testing for train
|
|
||||||
tripleo.repos.get_hash:
|
|
||||||
os_version: centos7
|
|
||||||
release: train
|
|
||||||
tag: tripleo-ci-testing
|
|
||||||
register: centos7_tripleo_ci_testing_train
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
msg: "Centos7 current-tripleo train: {{ centos7_tripleo_ci_testing_train['full_hash'] }}"
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
var: centos7_tripleo_ci_testing_train
|
|
||||||
|
|
||||||
- name: get current-tripleo centos8 for master branch
|
|
||||||
tripleo.repos.get_hash:
|
|
||||||
register: centos8_current_tripleo_master
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
msg: "Centos8 current-tripleo master: {{ centos8_current_tripleo_master['full_hash'] }}"
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
var: centos8_current_tripleo_master
|
|
@ -1,33 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Example usage for tripleo.repos.yum_config ansible module
|
|
||||||
hosts: localhost
|
|
||||||
tasks:
|
|
||||||
- name: Enable appstream yum repo and set exclude packages
|
|
||||||
become: true
|
|
||||||
tripleo.repos.yum_config:
|
|
||||||
type: repo
|
|
||||||
name: appstream
|
|
||||||
enabled: true
|
|
||||||
set_options:
|
|
||||||
exclude:
|
|
||||||
- nodejs*
|
|
||||||
- mariadb*
|
|
||||||
|
|
||||||
- name: Enable and install nginx module
|
|
||||||
become: true
|
|
||||||
tripleo.repos.yum_config:
|
|
||||||
type: module
|
|
||||||
name: nginx
|
|
||||||
enabled: true
|
|
||||||
operation: install
|
|
||||||
profile: common
|
|
||||||
stream: mainline
|
|
||||||
|
|
||||||
- name: Set yum global options in dnf.conf
|
|
||||||
become: true
|
|
||||||
tripleo.repos.yum_config:
|
|
||||||
type: global
|
|
||||||
file_path: /etc/dnf/dnf.conf
|
|
||||||
set_options:
|
|
||||||
skip_if_unavailable: "False"
|
|
||||||
keepcache: "0"
|
|
@ -1,114 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
from tripleo_repos.utils import load_logging
|
|
||||||
from tripleo_repos.get_hash.tripleo_hash_info import TripleOHashInfo
|
|
||||||
import tripleo_repos.get_hash.exceptions as exc
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_args(parsed_args):
|
|
||||||
if parsed_args.os_version == 'centos7' and (
|
|
||||||
parsed_args.component is not None
|
|
||||||
):
|
|
||||||
raise exc.TripleOHashInvalidParameter(
|
|
||||||
'Cannot specify component for centos 7'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
load_logging(module_name="tripleo-get-hash")
|
|
||||||
config = TripleOHashInfo.load_config()
|
|
||||||
parser = argparse.ArgumentParser(description='tripleo-get-hash.py')
|
|
||||||
parser.add_argument(
|
|
||||||
'--component',
|
|
||||||
help=('Use this to specify a component '
|
|
||||||
'This is NOT valid for Centos 7.'),
|
|
||||||
choices=config['tripleo_ci_components'],
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--dlrn-url',
|
|
||||||
help=(
|
|
||||||
'The URL for the delorean server to use. Defaults to '
|
|
||||||
'https://trunk.rdoproject.org'
|
|
||||||
),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--os-version',
|
|
||||||
default='centos8',
|
|
||||||
choices=config['os_versions'],
|
|
||||||
help=('The operating system and version to fetch the build tag for'),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--tag',
|
|
||||||
default='current-tripleo',
|
|
||||||
choices=config['rdo_named_tags'],
|
|
||||||
help=('The known tag to retrieve the hash_info for'),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--release',
|
|
||||||
default='master',
|
|
||||||
help=('The release of OpenStack you want the hash info for. '
|
|
||||||
'Default master'),
|
|
||||||
choices=config['tripleo_releases'],
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'--verbose',
|
|
||||||
action='store_true',
|
|
||||||
help=('Enable verbose log level for debugging'),
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if args.verbose:
|
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
|
||||||
logging.debug("Logging level set to DEBUG")
|
|
||||||
_validate_args(args)
|
|
||||||
|
|
||||||
if args.dlrn_url is not None:
|
|
||||||
logging.debug(
|
|
||||||
"Overriding configuration dlrn_url. Original value {}. "
|
|
||||||
"New value {}".format(config['dlrn_url'], args.dlrn_url)
|
|
||||||
)
|
|
||||||
config['dlrn_url'] = args.dlrn_url
|
|
||||||
logging.debug(
|
|
||||||
"Proceeding with the following configuration: {}".format(config)
|
|
||||||
)
|
|
||||||
|
|
||||||
tripleo_hash_info = TripleOHashInfo(
|
|
||||||
args.os_version,
|
|
||||||
args.release,
|
|
||||||
args.component,
|
|
||||||
args.tag,
|
|
||||||
config,
|
|
||||||
)
|
|
||||||
print(tripleo_hash_info)
|
|
||||||
return tripleo_hash_info
|
|
||||||
|
|
||||||
|
|
||||||
def cli_entrypoint():
|
|
||||||
try:
|
|
||||||
main()
|
|
||||||
sys.exit(0)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logging.info("Exiting on user interrupt")
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
@ -1,53 +0,0 @@
|
|||||||
# This file is installed to the path in [options.data_files] of the project
|
|
||||||
# setup.cfg file. It *must* contain all the keys specified in constants
|
|
||||||
# CONFIG_KEYS or there will be explosions.
|
|
||||||
|
|
||||||
dlrn_url: 'https://trunk.rdoproject.org'
|
|
||||||
|
|
||||||
tripleo_releases:
|
|
||||||
- master
|
|
||||||
- zed
|
|
||||||
- wallaby
|
|
||||||
- victoria
|
|
||||||
- ussuri
|
|
||||||
- train
|
|
||||||
- stein
|
|
||||||
- queens
|
|
||||||
- osp16-2
|
|
||||||
- osp17
|
|
||||||
- osp17-1
|
|
||||||
- osp18
|
|
||||||
|
|
||||||
tripleo_ci_components:
|
|
||||||
- baremetal
|
|
||||||
- cinder
|
|
||||||
- clients
|
|
||||||
- cloudops
|
|
||||||
- common
|
|
||||||
- compute
|
|
||||||
- glance
|
|
||||||
- manila
|
|
||||||
- network
|
|
||||||
- octavia
|
|
||||||
- security
|
|
||||||
- swift
|
|
||||||
- tempest
|
|
||||||
- tripleo
|
|
||||||
- ui
|
|
||||||
- validation
|
|
||||||
|
|
||||||
rdo_named_tags:
|
|
||||||
- current
|
|
||||||
- consistent
|
|
||||||
- component-ci-testing
|
|
||||||
- promoted-components
|
|
||||||
- tripleo-ci-testing
|
|
||||||
- current-tripleo
|
|
||||||
- current-tripleo-rdo
|
|
||||||
|
|
||||||
os_versions:
|
|
||||||
- centos7
|
|
||||||
- centos8
|
|
||||||
- centos9
|
|
||||||
- rhel8
|
|
||||||
- rhel9
|
|
@ -1,90 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
|
||||||
|
|
||||||
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
These are the keys we expect to find in a well-formed config.yaml
|
|
||||||
If any keys are missing from the configuration hash resolution doesn't proceed.
|
|
||||||
"""
|
|
||||||
CONFIG_KEYS = [
|
|
||||||
'dlrn_url',
|
|
||||||
'tripleo_releases',
|
|
||||||
'tripleo_ci_components',
|
|
||||||
'rdo_named_tags',
|
|
||||||
'os_versions',
|
|
||||||
]
|
|
||||||
|
|
||||||
"""
|
|
||||||
This is the path that we expect to find the system installed config.yaml.
|
|
||||||
The path is specified in [options.data_files] of the project setup.cfg.
|
|
||||||
"""
|
|
||||||
CONFIG_PATH = '/usr/local/etc/tripleo_get_hash/config.yaml'
|
|
||||||
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"tripleo_releases": [
|
|
||||||
"master",
|
|
||||||
"zed",
|
|
||||||
"wallaby",
|
|
||||||
"victoria",
|
|
||||||
"ussuri",
|
|
||||||
"train",
|
|
||||||
"stein",
|
|
||||||
"queens",
|
|
||||||
"osp16-2",
|
|
||||||
"osp17",
|
|
||||||
"osp17-1",
|
|
||||||
"osp18"
|
|
||||||
],
|
|
||||||
"dlrn_url": "https://trunk.rdoproject.org",
|
|
||||||
"rdo_named_tags": [
|
|
||||||
"current",
|
|
||||||
"consistent",
|
|
||||||
"component-ci-testing",
|
|
||||||
"promoted-components",
|
|
||||||
"tripleo-ci-testing",
|
|
||||||
"current-tripleo",
|
|
||||||
"current-tripleo-rdo"
|
|
||||||
],
|
|
||||||
"tripleo_ci_components": [
|
|
||||||
"baremetal",
|
|
||||||
"cinder",
|
|
||||||
"clients",
|
|
||||||
"cloudops",
|
|
||||||
"common",
|
|
||||||
"compute",
|
|
||||||
"glance",
|
|
||||||
"manila",
|
|
||||||
"network",
|
|
||||||
"octavia",
|
|
||||||
"security",
|
|
||||||
"swift",
|
|
||||||
"tempest",
|
|
||||||
"tripleo",
|
|
||||||
"ui",
|
|
||||||
"validation"
|
|
||||||
],
|
|
||||||
"os_versions": [
|
|
||||||
"centos7",
|
|
||||||
"centos8",
|
|
||||||
"centos9",
|
|
||||||
"rhel8",
|
|
||||||
"rhel9"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
|
||||||
|
|
||||||
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class Base(Exception):
|
|
||||||
"""Base Exception"""
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOHashMissingConfig(Base):
|
|
||||||
"""Missing configuration file for TripleOHashInfo. This is thrown
|
|
||||||
when there is no config.yaml in constants.CONFIG_PATH or the local
|
|
||||||
directory assuming execution from a source checkout.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, error_msg):
|
|
||||||
super(TripleOHashMissingConfig, self).__init__(error_msg)
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOHashInvalidConfig(Base):
|
|
||||||
"""Invalid configuration file for TripleOHashInfo. This is used when
|
|
||||||
any of they keys in constants.CONFIG_KEYS is not found in config.yaml.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, error_msg):
|
|
||||||
super(TripleOHashInvalidConfig, self).__init__(error_msg)
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOHashInvalidParameter(Base):
|
|
||||||
"""Invalid parameters passed for TripleOHashInfo. This is thrown when
|
|
||||||
the user passed invalid combination ofparameters parameters to the cli
|
|
||||||
entrypoint, for example specifying --component with centos7.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, error_msg):
|
|
||||||
super(TripleOHashInvalidParameter, self).__init__(error_msg)
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOHashInvalidDLRNResponse(Base):
|
|
||||||
"""Invalid response received from the DLRN server. This is seen if
|
|
||||||
the delorean server replies with a status code other than 200 OK for
|
|
||||||
a query to commit.yaml or delorean.repo.md5.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, error_msg):
|
|
||||||
super(TripleOHashInvalidDLRNResponse, self).__init__(error_msg)
|
|
@ -1,226 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
from .constants import CONFIG_PATH, CONFIG_KEYS, DEFAULT_CONFIG
|
|
||||||
from .exceptions import (
|
|
||||||
TripleOHashInvalidConfig, TripleOHashInvalidDLRNResponse
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
from tripleo_repos.utils import http_get
|
|
||||||
except ImportError:
|
|
||||||
from ansible_collections.tripleo.repos.plugins.module_utils. \
|
|
||||||
tripleo_repos.utils import http_get
|
|
||||||
|
|
||||||
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOHashInfo:
|
|
||||||
"""
|
|
||||||
Objects of type TripleOHashInfo contain the attributes required to
|
|
||||||
represent a particular delorean build hash. This includes the full, commit,
|
|
||||||
distro and extended hashes (where applicable), as well as the release,
|
|
||||||
OS name and version, component name (if applicable), named tag
|
|
||||||
(current-tripleo, tripleo-ci-testing etc) as well as the URL to the
|
|
||||||
delorean server that provided the information used to build each object
|
|
||||||
instance.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_yaml(cls, filename):
|
|
||||||
import yaml
|
|
||||||
return yaml.safe_load(filename)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _resolve_local_config_path(cls):
|
|
||||||
""" Load local config from disk from expected locations. """
|
|
||||||
paths = [
|
|
||||||
# pip install --user
|
|
||||||
os.path.expanduser(
|
|
||||||
"~/.local/etc/tripleo_get_hash/config.yaml"),
|
|
||||||
# root install
|
|
||||||
"/etc/tripleo_get_hash/config.yaml",
|
|
||||||
# embedded config.yaml as fallback
|
|
||||||
os.path.join(
|
|
||||||
os.path.dirname(os.path.abspath(__file__)), "config.yaml")
|
|
||||||
]
|
|
||||||
for _local_config in paths:
|
|
||||||
if cls._check_read_file(_local_config):
|
|
||||||
return _local_config
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _check_read_file(cls, filepath):
|
|
||||||
if os.path.isfile(filepath) and os.access(filepath, os.R_OK):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_config(cls, passed_config=None):
|
|
||||||
"""
|
|
||||||
This is a class method since we call it from the CLI entrypoint
|
|
||||||
before the TripleOHashInfo object is created. The method will first
|
|
||||||
try to use constants.CONFIG_PATH. If that is missing it tries to use
|
|
||||||
a local config.yaml for example for invocations from a source checkout
|
|
||||||
directory. If the file is not found TripleOHashMissingConfig is raised.
|
|
||||||
If any of the contants.CONFIG_KEYS is missing from config.yaml then
|
|
||||||
TripleOHashInvalidConfig is raised. If the passed_config dict contains
|
|
||||||
a given config value then that is used instead of the value from the
|
|
||||||
loaded config file. Returns a dictionary containing
|
|
||||||
the key->value for all the keys in constants.CONFIG_KEYS.
|
|
||||||
|
|
||||||
:param passed_config: dict with configuration overrides
|
|
||||||
:raises TripleOHashMissingConfig for missing config.yaml
|
|
||||||
:raises TripleOHashInvalidConfig for missing keys in config.yaml
|
|
||||||
:return: a config dictionary with the keys in constants.CONFIG_KEYS
|
|
||||||
"""
|
|
||||||
|
|
||||||
passed_config = passed_config or {}
|
|
||||||
result_config = {}
|
|
||||||
config_path = ''
|
|
||||||
local_config = cls._resolve_local_config_path()
|
|
||||||
# prefer const.CONFIG_PATH then local_config
|
|
||||||
if cls._check_read_file(CONFIG_PATH):
|
|
||||||
config_path = CONFIG_PATH
|
|
||||||
elif local_config:
|
|
||||||
config_path = local_config
|
|
||||||
else:
|
|
||||||
logging.info("Using embedded config file")
|
|
||||||
loaded_config = DEFAULT_CONFIG
|
|
||||||
logging.info("Using config file at %s", config_path)
|
|
||||||
if config_path != '':
|
|
||||||
with open(config_path, 'r') as config_yaml:
|
|
||||||
loaded_config = cls.load_yaml(config_yaml)
|
|
||||||
for k in CONFIG_KEYS:
|
|
||||||
if k not in loaded_config:
|
|
||||||
error_str = (
|
|
||||||
"Malformed config file - missing {0}. Expected all"
|
|
||||||
"of these configuration items: {1}"
|
|
||||||
).format(
|
|
||||||
k, ", ".join(CONFIG_KEYS)
|
|
||||||
)
|
|
||||||
logging.error(error_str)
|
|
||||||
raise TripleOHashInvalidConfig(error_str)
|
|
||||||
# if the passed config contains the key then use that value
|
|
||||||
if passed_config.get(k):
|
|
||||||
result_config[k] = passed_config[k]
|
|
||||||
else:
|
|
||||||
result_config[k] = loaded_config[k]
|
|
||||||
return result_config
|
|
||||||
|
|
||||||
def __init__(self, os_version, release, component, tag, config=None):
|
|
||||||
"""Create a new TripleOHashInfo object
|
|
||||||
|
|
||||||
:param os_version: The OS and version e.g. centos8
|
|
||||||
:param release: The OpenStack release e.g. wallaby
|
|
||||||
:param component: The tripleo-ci component e.g. 'common' or None
|
|
||||||
:param tag: The Delorean server named tag e.g. current-tripleo
|
|
||||||
:param config: Use an existing config dictionary and don't load it
|
|
||||||
"""
|
|
||||||
config = TripleOHashInfo.load_config(config)
|
|
||||||
|
|
||||||
self.os_version = os_version
|
|
||||||
self.release = release
|
|
||||||
self.component = component
|
|
||||||
self.tag = tag
|
|
||||||
|
|
||||||
repo_url = self._resolve_repo_url(config['dlrn_url'])
|
|
||||||
self.dlrn_url = repo_url
|
|
||||||
|
|
||||||
repo_url_response, status = http_get(repo_url)
|
|
||||||
|
|
||||||
if status != 200:
|
|
||||||
error_str = (
|
|
||||||
"Invalid response received from the delorean server. Queried "
|
|
||||||
"URL: {0}. Response code: {1}. Response text: {2}. Failed to "
|
|
||||||
"create TripleOHashInfo object."
|
|
||||||
).format(repo_url, status, repo_url_response)
|
|
||||||
logging.error(error_str)
|
|
||||||
raise TripleOHashInvalidDLRNResponse(error_str)
|
|
||||||
|
|
||||||
if repo_url.endswith('commit.yaml'):
|
|
||||||
from_commit_yaml = self._hashes_from_commit_yaml(repo_url_response)
|
|
||||||
self.full_hash = from_commit_yaml[0]
|
|
||||||
self.commit_hash = from_commit_yaml[1]
|
|
||||||
self.distro_hash = from_commit_yaml[2]
|
|
||||||
self.extended_hash = from_commit_yaml[3]
|
|
||||||
else:
|
|
||||||
self.full_hash = repo_url_response
|
|
||||||
self.commit_hash = None
|
|
||||||
self.distro_hash = None
|
|
||||||
self.extended_hash = None
|
|
||||||
|
|
||||||
def _resolve_repo_url(self, dlrn_url):
|
|
||||||
"""Resolve the delorean server URL given the various attributes of
|
|
||||||
this TripleOHashInfo object. The only passed parameter is the
|
|
||||||
dlrn_url. There are three main cases:
|
|
||||||
* centos8/rhel8 component https://trunk.rdoproject.org/centos8/component/common/current-tripleo/commit.yaml
|
|
||||||
* centos7 https://trunk.rdoproject.org/centos7/current-tripleo/commit.yaml
|
|
||||||
* centos8/rhel8 non component https://trunk.rdoproject.org/centos8/current-tripleo/delorean.repo.md5
|
|
||||||
Returns a string which is the full URL to the required item (i.e.
|
|
||||||
commit.yaml or repo.md5 depending on the case).
|
|
||||||
|
|
||||||
:param dlrn_url: The base url for the delorean server
|
|
||||||
:returns string URL to required commit.yaml or repo.md5
|
|
||||||
""" # noqa
|
|
||||||
repo_url = ''
|
|
||||||
if 'centos7' in self.os_version:
|
|
||||||
repo_url = "%s/%s-%s/%s/commit.yaml" % (
|
|
||||||
dlrn_url,
|
|
||||||
self.os_version,
|
|
||||||
self.release,
|
|
||||||
self.tag,
|
|
||||||
)
|
|
||||||
elif self.component is not None:
|
|
||||||
repo_url = "%s/%s-%s/component/%s/%s/commit.yaml" % (
|
|
||||||
dlrn_url,
|
|
||||||
self.os_version,
|
|
||||||
self.release,
|
|
||||||
self.component,
|
|
||||||
self.tag,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
repo_url = "%s/%s-%s/%s/delorean.repo.md5" % (
|
|
||||||
dlrn_url,
|
|
||||||
self.os_version,
|
|
||||||
self.release,
|
|
||||||
self.tag,
|
|
||||||
)
|
|
||||||
logging.debug("repo_url is %s", repo_url)
|
|
||||||
return repo_url
|
|
||||||
|
|
||||||
def _hashes_from_commit_yaml(self, delorean_result):
|
|
||||||
"""This function is used when a commit.yaml file is returned
|
|
||||||
by _resolve_repo_url. Returns a tuple containing the various
|
|
||||||
extracted hashes: full, commit, distro and extended
|
|
||||||
|
|
||||||
:returns tuple of strings full, commit, distro, extended hashes
|
|
||||||
"""
|
|
||||||
parsed_yaml = self.load_yaml(delorean_result)
|
|
||||||
commit = parsed_yaml['commits'][0]['commit_hash']
|
|
||||||
distro = parsed_yaml['commits'][0]['distro_hash']
|
|
||||||
full = "%s_%s" % (commit, distro[0:8])
|
|
||||||
extended = parsed_yaml['commits'][0]['extended_hash']
|
|
||||||
logging.debug(
|
|
||||||
"delorean commit.yaml results %s", parsed_yaml['commits'][0])
|
|
||||||
return full, commit, distro, extended
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
"""Returns a string representation of the object"""
|
|
||||||
attrs = vars(self)
|
|
||||||
return ',\n'.join('%s: %s' % item for item in attrs.items())
|
|
@ -1,632 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright 2016 Red Hat, Inc.
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
__metaclass__ = type
|
|
||||||
TITLE_RE = re.compile('\\[(.*)\\]')
|
|
||||||
NAME_RE = re.compile('name=(.+)')
|
|
||||||
PRIORITY_RE = re.compile('priority=\\d+')
|
|
||||||
# Packages to be included from delorean-current when using current-tripleo
|
|
||||||
INCLUDE_PKGS = ('includepkgs=instack,instack-undercloud,'
|
|
||||||
'os-apply-config,os-collect-config,os-net-config,'
|
|
||||||
'os-refresh-config,python*-tripleoclient,'
|
|
||||||
'openstack-tripleo-*,openstack-puppet-modules,'
|
|
||||||
'ansible-role-tripleo*,puppet-*,python*-tripleo-common,'
|
|
||||||
'python*-paunch*,tripleo-ansible,ansible-config_template')
|
|
||||||
DEFAULT_OUTPUT_PATH = '/etc/yum.repos.d'
|
|
||||||
DEFAULT_RDO_MIRROR = 'https://trunk.rdoproject.org'
|
|
||||||
|
|
||||||
# RHEL is only provided to licensed cloud providers via RHUI
|
|
||||||
DEFAULT_MIRROR_MAP = {
|
|
||||||
'fedora': 'https://mirrors.fedoraproject.org',
|
|
||||||
'centos7': 'http://mirror.centos.org',
|
|
||||||
'centos8': 'http://mirror.centos.org',
|
|
||||||
'centos9': 'http://mirror.stream.centos.org',
|
|
||||||
'ubi8': 'http://mirror.centos.org',
|
|
||||||
'ubi9': 'http://mirror.stream.centos.org',
|
|
||||||
'rhel8': 'https://trunk.rdoproject.org',
|
|
||||||
'rhel9': 'https://trunk.rdoproject.org',
|
|
||||||
}
|
|
||||||
CEPH_REPO_TEMPLATE = '''
|
|
||||||
[tripleo-centos-ceph-%(ceph_release)s]
|
|
||||||
name=tripleo-centos-ceph-%(ceph_release)s
|
|
||||||
baseurl=%(mirror)s/centos/%(centos_release)s/storage/$basearch/ceph-%(ceph_release)s/
|
|
||||||
gpgcheck=0
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
CEPH_SIG_REPO_TEMPLATE = '''
|
|
||||||
[tripleo-centos-ceph-%(ceph_release)s]
|
|
||||||
name=tripleo-centos-ceph-%(ceph_release)s
|
|
||||||
baseurl=%(mirror)s/SIGs/%(centos_release)s/storage/$basearch/ceph-%(ceph_release)s/
|
|
||||||
gpgcheck=0
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
CEPH_RDO_REPO_TEMPLATE = '''
|
|
||||||
[tripleo-centos-ceph-%(ceph_release)s]
|
|
||||||
name=tripleo-centos-ceph-%(ceph_release)s
|
|
||||||
baseurl=https://trunk.rdoproject.org/centos8-master/deps/storage/%(ceph_release)s/
|
|
||||||
gpgcheck=0
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
OPSTOOLS_REPO_TEMPLATE = '''
|
|
||||||
[tripleo-centos-opstools]
|
|
||||||
name=tripleo-centos-opstools
|
|
||||||
baseurl=%(mirror)s/centos/7/opstools/$basearch/
|
|
||||||
gpgcheck=0
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
# centos-8 only
|
|
||||||
HIGHAVAILABILITY_REPO_TEMPLATE = '''
|
|
||||||
[tripleo-centos-highavailability]
|
|
||||||
name=tripleo-centos-highavailability
|
|
||||||
baseurl=%(mirror)s/%(legacy_url)s%(stream)s/HighAvailability/$basearch/os/
|
|
||||||
gpgcheck=0
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
# centos-8 only
|
|
||||||
POWERTOOLS_REPO_TEMPLATE = '''
|
|
||||||
[tripleo-centos-powertools]
|
|
||||||
name=tripleo-centos-powertools
|
|
||||||
baseurl=%(mirror)s/%(legacy_url)s%(stream)s/%(pt_name)s/$basearch/os/
|
|
||||||
gpgcheck=0
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
# ubi-8 only
|
|
||||||
APPSTREAM_REPO_TEMPLATE = '''
|
|
||||||
[tripleo-centos-appstream]
|
|
||||||
name=tripleo-centos-appstream
|
|
||||||
baseurl=%(mirror)s/%(legacy_url)s%(stream)s/AppStream/$basearch/os/
|
|
||||||
gpgcheck=0
|
|
||||||
enabled=1
|
|
||||||
%(extra)s
|
|
||||||
'''
|
|
||||||
BASE_REPO_TEMPLATE = '''
|
|
||||||
[tripleo-centos-baseos]
|
|
||||||
name=tripleo-centos-baseos
|
|
||||||
baseurl=%(mirror)s/%(legacy_url)s%(stream)s/BaseOS/$basearch/os/
|
|
||||||
gpgcheck=0
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
# unversioned fedora added for backwards compatibility
|
|
||||||
SUPPORTED_DISTROS = [
|
|
||||||
('centos', '7'),
|
|
||||||
('centos', '8'),
|
|
||||||
('centos', '9'),
|
|
||||||
('fedora', ''),
|
|
||||||
('rhel', '8'),
|
|
||||||
('rhel', '9'),
|
|
||||||
('ubi', '8'),
|
|
||||||
('ubi', '9') # a subcase of the rhel distro
|
|
||||||
]
|
|
||||||
DISTRO_CHOICES = ["".join(distro_pair)
|
|
||||||
for distro_pair in SUPPORTED_DISTROS]
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidArguments(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NoRepoTitle(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def _get_distro():
|
|
||||||
"""Get distro info from os-release
|
|
||||||
|
|
||||||
returns: distro_id, distro_major_version_id, distro_name
|
|
||||||
"""
|
|
||||||
# Avoids a crash on unsupported platforms which would prevent even
|
|
||||||
# running with `--help`.
|
|
||||||
if not os.path.exists('/etc/os-release'):
|
|
||||||
return platform.system(), 'unknown', 'unknown'
|
|
||||||
|
|
||||||
output = subprocess.Popen(
|
|
||||||
'source /etc/os-release && echo -e -n "$ID\n$VERSION_ID\n$NAME"',
|
|
||||||
shell=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=open(os.devnull, 'w'),
|
|
||||||
executable='/bin/bash',
|
|
||||||
universal_newlines=True).communicate()
|
|
||||||
|
|
||||||
# distro_id and distro_version_id will always be at least an empty string
|
|
||||||
distro_id, distro_version_id, distro_name = output[0].split('\n')
|
|
||||||
|
|
||||||
# if distro_version_id is empty string the major version will be empty
|
|
||||||
# string too
|
|
||||||
distro_major_version_id = distro_version_id.split('.')[0]
|
|
||||||
|
|
||||||
# check if that is UBI subcase?
|
|
||||||
if os.path.exists('/etc/yum.repos.d/ubi.repo'):
|
|
||||||
distro_id = 'ubi'
|
|
||||||
|
|
||||||
if (distro_id, distro_major_version_id) not in SUPPORTED_DISTROS:
|
|
||||||
print(
|
|
||||||
"WARNING: Unsupported platform '{0}{1}' detected by tripleo-repos,"
|
|
||||||
" centos7 will be used unless you use CLI param to change it."
|
|
||||||
"".format(distro_id, distro_major_version_id), file=sys.stderr)
|
|
||||||
distro_id = 'centos'
|
|
||||||
distro_major_version_id = '7'
|
|
||||||
|
|
||||||
if distro_id == 'ubi':
|
|
||||||
print(
|
|
||||||
"WARNING: Centos{0} Base and AppStream will be installed for "
|
|
||||||
"this UBI distro".format(distro_major_version_id))
|
|
||||||
|
|
||||||
return distro_id, distro_major_version_id, distro_name
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_args(distro_id, distro_major_version_id):
|
|
||||||
|
|
||||||
distro = "{0}{1}".format(distro_id, distro_major_version_id)
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description='Download and install repos necessary for TripleO. Note '
|
|
||||||
'that some of these repos require yum-plugin-priorities, '
|
|
||||||
'so that will also be installed.',
|
|
||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
||||||
parser.add_argument('repos', metavar='REPO', nargs='+',
|
|
||||||
choices=['current', 'deps', 'current-tripleo',
|
|
||||||
'current-tripleo-dev', 'ceph', 'opstools',
|
|
||||||
'tripleo-ci-testing', 'current-tripleo-rdo'],
|
|
||||||
help='A list of repos. Available repos: '
|
|
||||||
'%(choices)s. The deps repo will always be '
|
|
||||||
'included when using current or '
|
|
||||||
'current-tripleo. current-tripleo-dev '
|
|
||||||
'downloads the current-tripleo, current, and '
|
|
||||||
'deps repos, but sets the current repo to only '
|
|
||||||
'be used for TripleO projects. It also modifies '
|
|
||||||
'each repo\'s priority so packages are installed '
|
|
||||||
'from the appropriate location.')
|
|
||||||
parser.add_argument('-d', '--distro',
|
|
||||||
default=distro,
|
|
||||||
choices=DISTRO_CHOICES,
|
|
||||||
nargs='?',
|
|
||||||
help='Target distro with default detected at runtime. '
|
|
||||||
)
|
|
||||||
parser.add_argument('-b', '--branch',
|
|
||||||
default='master',
|
|
||||||
help='Target branch. Should be the lowercase name of '
|
|
||||||
'the OpenStack release. e.g. liberty')
|
|
||||||
parser.add_argument('-o', '--output-path',
|
|
||||||
default=DEFAULT_OUTPUT_PATH,
|
|
||||||
help='Directory in which to save the selected repos.')
|
|
||||||
parser.add_argument('--mirror',
|
|
||||||
help='Server from which to install base OS packages. '
|
|
||||||
'Default value is based on distro param.')
|
|
||||||
parser.add_argument('--rdo-mirror',
|
|
||||||
default=DEFAULT_RDO_MIRROR,
|
|
||||||
help='Server from which to install RDO packages.')
|
|
||||||
stream_group = parser.add_mutually_exclusive_group()
|
|
||||||
stream_group.add_argument('--stream',
|
|
||||||
action='store_true',
|
|
||||||
default=True,
|
|
||||||
help='Enable stream support for CentOS repos')
|
|
||||||
stream_group.add_argument('--no-stream',
|
|
||||||
action='store_true',
|
|
||||||
default=False,
|
|
||||||
help='Disable stream support for CentOS repos')
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
if args.no_stream:
|
|
||||||
args.stream = False
|
|
||||||
|
|
||||||
# Default mirror for args.distro (which defaults to 'distro')
|
|
||||||
default_mirror = DEFAULT_MIRROR_MAP.get(args.distro, None)
|
|
||||||
if default_mirror is None and 'fedora' in args.distro:
|
|
||||||
# We don't have different mirrors for specific fedora releases
|
|
||||||
default_mirror = DEFAULT_MIRROR_MAP.get('fedora', None)
|
|
||||||
|
|
||||||
if args.mirror is None:
|
|
||||||
args.mirror = default_mirror
|
|
||||||
args.old_mirror = default_mirror
|
|
||||||
|
|
||||||
return args
|
|
||||||
|
|
||||||
|
|
||||||
def _get_repo(path, args):
|
|
||||||
|
|
||||||
# lazy import
|
|
||||||
if 'requests' not in globals():
|
|
||||||
import requests
|
|
||||||
|
|
||||||
r = requests.get(path)
|
|
||||||
if r.status_code == 200:
|
|
||||||
return _inject_mirrors(r.text, args)
|
|
||||||
else:
|
|
||||||
r.raise_for_status()
|
|
||||||
|
|
||||||
|
|
||||||
def _write_repo(content, target, name=None):
|
|
||||||
if not name:
|
|
||||||
m = TITLE_RE.search(content)
|
|
||||||
if not m:
|
|
||||||
raise NoRepoTitle('Could not find repo title in: \n%s' % content)
|
|
||||||
name = m.group(1)
|
|
||||||
# centos-8 dlrn repos have changed. repos per component
|
|
||||||
# are folded into a single repo.
|
|
||||||
if 'component' in name:
|
|
||||||
name = 'delorean'
|
|
||||||
filename = name + '.repo'
|
|
||||||
filename = os.path.join(target, filename)
|
|
||||||
with open(filename, 'w') as f:
|
|
||||||
f.write(content)
|
|
||||||
print('Installed repo %s to %s' % (name, filename))
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_distro_repos(args):
|
|
||||||
"""Validate requested repos are valid for the distro"""
|
|
||||||
valid_repos = []
|
|
||||||
if 'fedora' in args.distro:
|
|
||||||
valid_repos = ['current', 'current-tripleo', 'ceph', 'deps',
|
|
||||||
'tripleo-ci-testing']
|
|
||||||
elif args.distro in DISTRO_CHOICES:
|
|
||||||
valid_repos = ['ceph', 'current', 'current-tripleo',
|
|
||||||
'current-tripleo-dev', 'deps', 'tripleo-ci-testing',
|
|
||||||
'opstools', 'current-tripleo-rdo']
|
|
||||||
invalid_repos = [x for x in args.repos if x not in valid_repos]
|
|
||||||
if len(invalid_repos) > 0:
|
|
||||||
raise InvalidArguments(
|
|
||||||
'{0} repo(s) are not valid for {1}. Valid repos '
|
|
||||||
'are: {2}'.format(invalid_repos, args.distro, valid_repos))
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_current_tripleo(repos):
|
|
||||||
"""Validate current usage
|
|
||||||
|
|
||||||
current and current-tripleo cannot be specified with each other and
|
|
||||||
current-tripleo-dev is a mix of current, current-tripleo and deps
|
|
||||||
so they should not be specified on the command line with each other.
|
|
||||||
"""
|
|
||||||
if 'current-tripleo' in repos and 'current' in repos:
|
|
||||||
raise InvalidArguments('Cannot use current and current-tripleo at the '
|
|
||||||
'same time.')
|
|
||||||
if 'current-tripleo-dev' not in repos:
|
|
||||||
return True
|
|
||||||
if 'current' in repos or 'current-tripleo' in repos or 'deps' in repos:
|
|
||||||
raise InvalidArguments('current-tripleo-dev should not be used with '
|
|
||||||
'any other RDO Trunk repos.')
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_tripleo_ci_testing(repos):
|
|
||||||
"""Validate tripleo-ci-testing
|
|
||||||
|
|
||||||
With tripleo-ci-testing for repo (currently only periodic container build)
|
|
||||||
no other repos expected except optionally deps|ceph|opstools
|
|
||||||
which is enabled regardless.
|
|
||||||
"""
|
|
||||||
if 'tripleo-ci-testing' in repos and len(repos) > 1:
|
|
||||||
if 'deps' in repos or 'ceph' in repos or 'opstools' in repos:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
raise InvalidArguments('Cannot use tripleo-ci-testing at the '
|
|
||||||
'same time as other repos, except '
|
|
||||||
'deps|ceph|opstools.')
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_distro_stream(args, distro_name, distro_major_version_id):
|
|
||||||
"""Validate stream related args vs host
|
|
||||||
|
|
||||||
Fails if stream is to be used but the host isn't a stream OS or vice versa
|
|
||||||
"""
|
|
||||||
if args.output_path != DEFAULT_OUTPUT_PATH:
|
|
||||||
# don't validate distro name because the output path is not
|
|
||||||
# /etc/yum.repos.d, so the repo files may not be used to install
|
|
||||||
# packages on this host
|
|
||||||
return True
|
|
||||||
if 'centos' not in distro_name.lower():
|
|
||||||
return True
|
|
||||||
if distro_name.lower() == 'centos' and distro_major_version_id != '8':
|
|
||||||
return True
|
|
||||||
is_stream = args.stream and not args.no_stream
|
|
||||||
if is_stream and 'stream' not in distro_name.lower():
|
|
||||||
raise InvalidArguments('--stream provided, but OS is not the Stream '
|
|
||||||
'version. Please ensure the host is Stream.')
|
|
||||||
elif not is_stream and 'stream' in distro_name.lower():
|
|
||||||
raise InvalidArguments('--no-stream provided, but OS is the Stream '
|
|
||||||
'version. Please ensure the host is not the '
|
|
||||||
'Stream version.')
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_args(args, distro_name, distro_major_version_id):
|
|
||||||
_validate_current_tripleo(args.repos)
|
|
||||||
_validate_distro_repos(args)
|
|
||||||
_validate_tripleo_ci_testing(args.repos)
|
|
||||||
_validate_distro_stream(args, distro_name, distro_major_version_id)
|
|
||||||
|
|
||||||
|
|
||||||
def _remove_existing(args):
|
|
||||||
"""Remove any delorean* or opstools repos that already exist"""
|
|
||||||
if args.distro in ['ubi8', 'ubi9']:
|
|
||||||
regex = '^(BaseOS|AppStream|delorean|tripleo-centos-' \
|
|
||||||
'(opstools|ceph|highavailability|powertools)).*.repo'
|
|
||||||
else:
|
|
||||||
regex = '^(delorean|tripleo-centos-' \
|
|
||||||
'(opstools|ceph|highavailability|powertools)).*.repo'
|
|
||||||
pattern = re.compile(regex)
|
|
||||||
if os.path.exists("/etc/distro.repos.d"):
|
|
||||||
paths = set(
|
|
||||||
os.listdir(args.output_path) + os.listdir("/etc/distro.repos.d"))
|
|
||||||
else:
|
|
||||||
paths = os.listdir(args.output_path)
|
|
||||||
for f in paths:
|
|
||||||
if pattern.match(f):
|
|
||||||
filename = os.path.join(args.output_path, f)
|
|
||||||
if os.path.exists(filename):
|
|
||||||
os.remove(filename)
|
|
||||||
print('Removed old repo "%s"' % filename)
|
|
||||||
filename = os.path.join("/etc/distro.repos.d", f)
|
|
||||||
if os.path.exists(filename):
|
|
||||||
os.remove(filename)
|
|
||||||
print('Removed old repo "%s"' % filename)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_base_path(args):
|
|
||||||
if args.distro in ['ubi8', 'ubi9']:
|
|
||||||
# there are no base paths for UBI that work well
|
|
||||||
distro = args.distro.replace('ubi', 'centos')
|
|
||||||
else:
|
|
||||||
distro = args.distro
|
|
||||||
|
|
||||||
# The mirror url with /$DISTRO$VERSION path for master branch is
|
|
||||||
# deprecated.
|
|
||||||
# The default for rdo mirrors is $DISTRO$VERSION-$BRANCH
|
|
||||||
# it should work for every (distro, branch) pair that
|
|
||||||
# makes sense
|
|
||||||
# Any exception should be corrected at source, not here.
|
|
||||||
distro_branch = '%s-%s' % (distro, args.branch)
|
|
||||||
return '%s/%s/' % (args.rdo_mirror, distro_branch)
|
|
||||||
|
|
||||||
|
|
||||||
def _install_priorities():
|
|
||||||
try:
|
|
||||||
subprocess.check_call(['yum', 'install', '-y',
|
|
||||||
'yum-plugin-priorities'])
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
print('ERROR: Failed to install yum-plugin-priorities\n%s\n%s' %
|
|
||||||
(e.cmd, e.output))
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def _create_ceph(args, release):
|
|
||||||
"""Generate a Ceph repo file for release"""
|
|
||||||
centos_release = '9-stream'
|
|
||||||
template = CEPH_SIG_REPO_TEMPLATE
|
|
||||||
if args.distro == 'centos7':
|
|
||||||
centos_release = '7'
|
|
||||||
template = CEPH_REPO_TEMPLATE
|
|
||||||
elif args.distro == 'centos8' and release == 'nautilus':
|
|
||||||
template = CEPH_RDO_REPO_TEMPLATE
|
|
||||||
elif args.distro == 'centos8':
|
|
||||||
centos_release = '8-stream'
|
|
||||||
template = CEPH_REPO_TEMPLATE
|
|
||||||
|
|
||||||
return template % {'centos_release': centos_release,
|
|
||||||
'ceph_release': release,
|
|
||||||
'mirror': args.mirror}
|
|
||||||
|
|
||||||
|
|
||||||
def _change_priority(content, new_priority):
|
|
||||||
new_content = PRIORITY_RE.sub('priority=%d' % new_priority, content)
|
|
||||||
# This shouldn't happen, but let's be safe.
|
|
||||||
if not PRIORITY_RE.search(new_content):
|
|
||||||
new_content = []
|
|
||||||
for line in content.split("\n"):
|
|
||||||
new_content.append(line)
|
|
||||||
if line.startswith('['):
|
|
||||||
new_content.append('priority=%d' % new_priority)
|
|
||||||
new_content = "\n".join(new_content)
|
|
||||||
return new_content
|
|
||||||
|
|
||||||
|
|
||||||
def _add_includepkgs(content):
|
|
||||||
new_content = []
|
|
||||||
for line in content.split("\n"):
|
|
||||||
new_content.append(line)
|
|
||||||
if line.startswith('['):
|
|
||||||
new_content.append(INCLUDE_PKGS)
|
|
||||||
return "\n".join(new_content)
|
|
||||||
|
|
||||||
|
|
||||||
def _inject_mirrors(content, args):
|
|
||||||
"""Replace any references to the default mirrors in repo content
|
|
||||||
|
|
||||||
In some cases we want to use mirrors whose repo files still point to the
|
|
||||||
default servers. If the user specified to use the mirror, we want to
|
|
||||||
replace any such references with the mirror address. This function
|
|
||||||
handles that by using a regex to swap out the baseurl server.
|
|
||||||
"""
|
|
||||||
|
|
||||||
content = re.sub('baseurl=%s' % DEFAULT_RDO_MIRROR,
|
|
||||||
'baseurl=%s' % args.rdo_mirror,
|
|
||||||
content)
|
|
||||||
|
|
||||||
if args.old_mirror:
|
|
||||||
content = re.sub('baseurl=%s' % args.old_mirror,
|
|
||||||
'baseurl=%s' % args.mirror,
|
|
||||||
content)
|
|
||||||
|
|
||||||
return content
|
|
||||||
|
|
||||||
|
|
||||||
def _install_repos(args, base_path):
|
|
||||||
def install_deps(args, base_path):
|
|
||||||
content = _get_repo(base_path + 'delorean-deps.repo', args)
|
|
||||||
_write_repo(content, args.output_path)
|
|
||||||
|
|
||||||
for repo in args.repos:
|
|
||||||
if repo == 'current':
|
|
||||||
content = _get_repo(base_path + 'current/delorean.repo', args)
|
|
||||||
_write_repo(content, args.output_path, name='delorean')
|
|
||||||
install_deps(args, base_path)
|
|
||||||
elif repo == 'deps':
|
|
||||||
install_deps(args, base_path)
|
|
||||||
elif repo == 'current-tripleo':
|
|
||||||
content = _get_repo(base_path + 'current-tripleo/delorean.repo',
|
|
||||||
args)
|
|
||||||
_write_repo(content, args.output_path)
|
|
||||||
install_deps(args, base_path)
|
|
||||||
elif repo == 'current-tripleo-dev':
|
|
||||||
content = _get_repo(base_path + 'delorean-deps.repo', args)
|
|
||||||
_write_repo(content, args.output_path)
|
|
||||||
content = _get_repo(base_path + 'current-tripleo/delorean.repo',
|
|
||||||
args)
|
|
||||||
content = TITLE_RE.sub('[\\1-current-tripleo]', content)
|
|
||||||
content = NAME_RE.sub('name=\\1-current-tripleo', content)
|
|
||||||
# We need to twiddle priorities since we're mixing multiple repos
|
|
||||||
# that are generated with the same priority.
|
|
||||||
content = _change_priority(content, 20)
|
|
||||||
_write_repo(content, args.output_path,
|
|
||||||
name='delorean-current-tripleo')
|
|
||||||
content = _get_repo(base_path + 'current/delorean.repo', args)
|
|
||||||
content = _add_includepkgs(content)
|
|
||||||
content = _change_priority(content, 10)
|
|
||||||
_write_repo(content, args.output_path, name='delorean')
|
|
||||||
elif repo == 'tripleo-ci-testing':
|
|
||||||
content = _get_repo(base_path + 'tripleo-ci-testing/delorean.repo',
|
|
||||||
args)
|
|
||||||
_write_repo(content, args.output_path)
|
|
||||||
install_deps(args, base_path)
|
|
||||||
elif repo == 'current-tripleo-rdo':
|
|
||||||
content = _get_repo(
|
|
||||||
base_path + 'current-tripleo-rdo/delorean.repo', args)
|
|
||||||
_write_repo(content, args.output_path)
|
|
||||||
install_deps(args, base_path)
|
|
||||||
elif repo == 'ceph':
|
|
||||||
if args.branch in ['liberty', 'mitaka']:
|
|
||||||
content = _create_ceph(args, 'hammer')
|
|
||||||
elif args.branch in ['newton', 'ocata', 'pike']:
|
|
||||||
content = _create_ceph(args, 'jewel')
|
|
||||||
elif args.branch in ['queens', 'rocky']:
|
|
||||||
content = _create_ceph(args, 'luminous')
|
|
||||||
elif args.branch in ['stein', 'train', 'ussuri', 'victoria']:
|
|
||||||
content = _create_ceph(args, 'nautilus')
|
|
||||||
else:
|
|
||||||
content = _create_ceph(args, 'pacific')
|
|
||||||
_write_repo(content, args.output_path)
|
|
||||||
elif repo == 'opstools':
|
|
||||||
content = OPSTOOLS_REPO_TEMPLATE % {'mirror': args.mirror}
|
|
||||||
_write_repo(content, args.output_path)
|
|
||||||
else:
|
|
||||||
raise InvalidArguments('Invalid repo "%s" specified' % repo)
|
|
||||||
|
|
||||||
distro = args.distro
|
|
||||||
# CentOS-8 AppStream is required for UBI-8
|
|
||||||
legacy_url = 'centos/'
|
|
||||||
if distro in ['ubi8', 'ubi9']:
|
|
||||||
if not os.path.exists("/etc/distro.repos.d"):
|
|
||||||
print('WARNING: For UBI it is recommended to create '
|
|
||||||
'/etc/distro.repos.d and rerun!')
|
|
||||||
dp_exists = False
|
|
||||||
else:
|
|
||||||
dp_exists = True
|
|
||||||
if args.output_path == DEFAULT_OUTPUT_PATH and dp_exists:
|
|
||||||
distro_path = "/etc/distro.repos.d"
|
|
||||||
else:
|
|
||||||
distro_path = args.output_path
|
|
||||||
# TODO: Remove it once bugs are fixed
|
|
||||||
# Add extra options to APPSTREAM_REPO_TEMPLATE because of
|
|
||||||
# rhbz/1961558 and lpbz/1929634
|
|
||||||
extra = ''
|
|
||||||
if args.branch in ['train', 'ussuri', 'victoria']:
|
|
||||||
extra = 'exclude=edk2-ovmf-20200602gitca407c7246bf-5*'
|
|
||||||
|
|
||||||
distro_name = str(distro[-1]) + '-stream'
|
|
||||||
content = APPSTREAM_REPO_TEMPLATE % {'mirror': args.mirror,
|
|
||||||
'extra': extra,
|
|
||||||
'legacy_url': legacy_url,
|
|
||||||
'stream': distro_name}
|
|
||||||
_write_repo(content, distro_path)
|
|
||||||
content = BASE_REPO_TEMPLATE % {'mirror': args.mirror,
|
|
||||||
'legacy_url': legacy_url,
|
|
||||||
'stream': distro_name}
|
|
||||||
_write_repo(content, distro_path)
|
|
||||||
if distro in ['centos8', 'centos9', 'ubi8', 'ubi9']:
|
|
||||||
distro = 'centos' + str(distro[-1])
|
|
||||||
|
|
||||||
if 'centos' in distro:
|
|
||||||
stream = str(distro[-1])
|
|
||||||
# HA, Powertools are required for CentOS-8
|
|
||||||
if int(stream) >= 8:
|
|
||||||
if args.stream and not args.no_stream:
|
|
||||||
stream = stream + '-stream'
|
|
||||||
|
|
||||||
pt_name = 'PowerTools'
|
|
||||||
if '9' in stream:
|
|
||||||
legacy_url = ''
|
|
||||||
pt_name = 'CRB'
|
|
||||||
|
|
||||||
content = HIGHAVAILABILITY_REPO_TEMPLATE % {
|
|
||||||
'mirror': args.mirror,
|
|
||||||
'stream': stream,
|
|
||||||
'legacy_url': legacy_url}
|
|
||||||
_write_repo(content, args.output_path)
|
|
||||||
|
|
||||||
content = POWERTOOLS_REPO_TEMPLATE % {'mirror': args.mirror,
|
|
||||||
'stream': stream,
|
|
||||||
'legacy_url': legacy_url,
|
|
||||||
'pt_name': pt_name}
|
|
||||||
_write_repo(content, args.output_path)
|
|
||||||
|
|
||||||
if '9' in stream:
|
|
||||||
content = APPSTREAM_REPO_TEMPLATE % {'mirror': args.mirror,
|
|
||||||
'extra': '',
|
|
||||||
'legacy_url': legacy_url,
|
|
||||||
'stream': stream}
|
|
||||||
_write_repo(content, args.output_path)
|
|
||||||
|
|
||||||
content = BASE_REPO_TEMPLATE % {'mirror': args.mirror,
|
|
||||||
'legacy_url': legacy_url,
|
|
||||||
'stream': stream}
|
|
||||||
_write_repo(content, args.output_path)
|
|
||||||
|
|
||||||
|
|
||||||
def _run_pkg_clean(distro):
|
|
||||||
pkg_mgr = 'yum' if distro == 'centos7' else 'dnf'
|
|
||||||
try:
|
|
||||||
subprocess.check_call([pkg_mgr, 'clean', 'metadata'])
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
print('ERROR: Failed to clean yum metadata.')
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
distro_id, distro_major_version_id, distro_name = _get_distro()
|
|
||||||
args = _parse_args(distro_id, distro_major_version_id)
|
|
||||||
_validate_args(args, distro_name, distro_major_version_id)
|
|
||||||
base_path = _get_base_path(args)
|
|
||||||
if (distro_name.lower(), distro_major_version_id) == ("centos", "7"):
|
|
||||||
_install_priorities()
|
|
||||||
_remove_existing(args)
|
|
||||||
_install_repos(args, base_path)
|
|
||||||
_run_pkg_clean(args.distro)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,83 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
# portable http_get that uses either ansible recommended way or python native
|
|
||||||
# urllib. Also deals with python2 vs python3 for centos7 train jobs.
|
|
||||||
py_version = sys.version_info.major
|
|
||||||
if py_version < 3:
|
|
||||||
import urllib2
|
|
||||||
|
|
||||||
def http_get(url):
|
|
||||||
try:
|
|
||||||
response = urllib2.urlopen(url)
|
|
||||||
return (
|
|
||||||
response.read().decode('utf-8'),
|
|
||||||
int(response.code))
|
|
||||||
except Exception as e:
|
|
||||||
return (str(e), -1)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.urls import open_url
|
|
||||||
|
|
||||||
def http_get(url):
|
|
||||||
try:
|
|
||||||
response = open_url(url, method='GET')
|
|
||||||
return (response.read().decode('utf-8'), response.status)
|
|
||||||
except Exception as e:
|
|
||||||
return (str(e), -1)
|
|
||||||
except ImportError:
|
|
||||||
from urllib.request import urlopen
|
|
||||||
|
|
||||||
def http_get(url):
|
|
||||||
try:
|
|
||||||
response = urlopen(url)
|
|
||||||
return (
|
|
||||||
response.read().decode('utf-8'),
|
|
||||||
int(response.status))
|
|
||||||
except Exception as e:
|
|
||||||
return (str(e), -1)
|
|
||||||
|
|
||||||
|
|
||||||
def load_logging(level=logging.INFO, module_name="tripleo-repos"):
|
|
||||||
"""Load and set logging level. Default is set to logging.INFO level."""
|
|
||||||
logger = logging.getLogger()
|
|
||||||
# Only add logger once to avoid duplicated streams in tests
|
|
||||||
if not logger.handlers:
|
|
||||||
stdout_handlers = [
|
|
||||||
_handler
|
|
||||||
for _handler in logger.handlers
|
|
||||||
if
|
|
||||||
(
|
|
||||||
hasattr(_handler, 'stream') and 'stdout' in
|
|
||||||
_handler.stream.name
|
|
||||||
)
|
|
||||||
]
|
|
||||||
if stdout_handlers == []:
|
|
||||||
formatter = logging.Formatter(
|
|
||||||
(
|
|
||||||
"%(asctime)s - " + module_name + " - %(levelname)s - "
|
|
||||||
"%(message)s"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
handler = logging.StreamHandler(sys.stdout)
|
|
||||||
handler.setFormatter(formatter)
|
|
||||||
logger.addHandler(handler)
|
|
||||||
logger.setLevel(level)
|
|
@ -1,306 +0,0 @@
|
|||||||
|
|
||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from tripleo_repos.utils import load_logging
|
|
||||||
import tripleo_repos.yum_config.constants as const
|
|
||||||
import tripleo_repos.yum_config.yum_config as cfg
|
|
||||||
import tripleo_repos.yum_config.utils as utils
|
|
||||||
|
|
||||||
|
|
||||||
def options_to_dict(options):
|
|
||||||
opt_dict = {}
|
|
||||||
if options:
|
|
||||||
for opt in options:
|
|
||||||
try:
|
|
||||||
k, v = opt.split('=')
|
|
||||||
except ValueError:
|
|
||||||
msg = 'Set options must be provided as "key=value" pairs'
|
|
||||||
logging.error(msg)
|
|
||||||
sys.exit(2)
|
|
||||||
opt_dict[k] = v
|
|
||||||
return opt_dict
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
load_logging(module_name="tripleo-yum-config")
|
|
||||||
# Get release model and version
|
|
||||||
distro, major_version, __ = utils.get_distro_info()
|
|
||||||
py_version = sys.version_info.major
|
|
||||||
if py_version < 3:
|
|
||||||
logging.warning("Some operations will be disabled when running with "
|
|
||||||
"python 2.")
|
|
||||||
|
|
||||||
# Repo arguments
|
|
||||||
repo_args_parser = argparse.ArgumentParser(add_help=False)
|
|
||||||
repo_args_parser.add_argument(
|
|
||||||
'--name',
|
|
||||||
help='name of the repo to be modified'
|
|
||||||
)
|
|
||||||
|
|
||||||
environment_parse = argparse.ArgumentParser(add_help=False)
|
|
||||||
environment_parse.add_argument(
|
|
||||||
'--environment-file',
|
|
||||||
dest='env_file',
|
|
||||||
default=None,
|
|
||||||
help=('path to an environment file to be read before creating repo '
|
|
||||||
'files'),
|
|
||||||
)
|
|
||||||
|
|
||||||
parser_enable_group = repo_args_parser.add_mutually_exclusive_group()
|
|
||||||
parser_enable_group.add_argument(
|
|
||||||
'--enable',
|
|
||||||
action='store_true',
|
|
||||||
dest='enable',
|
|
||||||
default=None,
|
|
||||||
help='enable a yum repo or module'
|
|
||||||
)
|
|
||||||
parser_enable_group.add_argument(
|
|
||||||
'--disable',
|
|
||||||
action='store_false',
|
|
||||||
dest='enable',
|
|
||||||
default=None,
|
|
||||||
help='disable a yum repo or module'
|
|
||||||
)
|
|
||||||
repo_args_parser.add_argument(
|
|
||||||
'--config-dir-path',
|
|
||||||
dest='config_dir_path',
|
|
||||||
default=const.YUM_REPO_DIR,
|
|
||||||
help=(
|
|
||||||
'set the absolute directory path that holds all repo '
|
|
||||||
'configuration files')
|
|
||||||
)
|
|
||||||
repo_args_parser.add_argument(
|
|
||||||
'--down-url',
|
|
||||||
dest='down_url',
|
|
||||||
help=(
|
|
||||||
'URL of a repo file to be used as base to create or update '
|
|
||||||
'a repo configuration file.')
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generic key-value options
|
|
||||||
options_parse = argparse.ArgumentParser(add_help=False)
|
|
||||||
options_parse.add_argument(
|
|
||||||
'--set-opts',
|
|
||||||
dest='set_opts',
|
|
||||||
nargs='+',
|
|
||||||
help='sets config options as key=value pairs for a specific '
|
|
||||||
'configuration file'
|
|
||||||
)
|
|
||||||
|
|
||||||
# dnf module parser
|
|
||||||
dnf_module_parser = argparse.ArgumentParser(add_help=False)
|
|
||||||
dnf_module_parser.add_argument(
|
|
||||||
'operation',
|
|
||||||
choices=['enable', 'disable', 'install', 'remove', 'reset'],
|
|
||||||
help="dnf module operation to be executed"
|
|
||||||
)
|
|
||||||
dnf_module_parser.add_argument(
|
|
||||||
'name',
|
|
||||||
help='name of the module to be modified'
|
|
||||||
)
|
|
||||||
dnf_module_parser.add_argument(
|
|
||||||
'--stream',
|
|
||||||
help="sets module stream"
|
|
||||||
)
|
|
||||||
dnf_module_parser.add_argument(
|
|
||||||
'--profile',
|
|
||||||
help="sets module profile"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Compose repo arguments
|
|
||||||
compose_args_parser = argparse.ArgumentParser(add_help=False)
|
|
||||||
compose_args_parser.add_argument(
|
|
||||||
'--compose-url',
|
|
||||||
dest='compose_url',
|
|
||||||
required=True,
|
|
||||||
help='CentOS compose URL'
|
|
||||||
)
|
|
||||||
compose_args_parser.add_argument(
|
|
||||||
'--release',
|
|
||||||
dest='release',
|
|
||||||
choices=const.COMPOSE_REPOS_RELEASES,
|
|
||||||
default='centos-stream-8',
|
|
||||||
help='target CentOS release.'
|
|
||||||
)
|
|
||||||
compose_args_parser.add_argument(
|
|
||||||
'--arch',
|
|
||||||
choices=const.COMPOSE_REPOS_SUPPORTED_ARCHS,
|
|
||||||
default='x86_64',
|
|
||||||
help='set the architecture for the destination repos.'
|
|
||||||
)
|
|
||||||
compose_args_parser.add_argument(
|
|
||||||
'--disable-repos',
|
|
||||||
nargs='+',
|
|
||||||
help='list of repo names or repo absolute file paths to be disabled.'
|
|
||||||
)
|
|
||||||
compose_args_parser.add_argument(
|
|
||||||
'--disable-all-conflicting',
|
|
||||||
action='store_true',
|
|
||||||
dest='disable_conflicting',
|
|
||||||
default=False,
|
|
||||||
help='after enabling compose repos, disable all other repos that '
|
|
||||||
'match variant names.'
|
|
||||||
)
|
|
||||||
compose_args_parser.add_argument(
|
|
||||||
'--variants',
|
|
||||||
nargs='+',
|
|
||||||
help='Name of the repos to be enabled. Default behavior is to enable '
|
|
||||||
'all that match a specific release and architecture.'
|
|
||||||
)
|
|
||||||
compose_args_parser.add_argument(
|
|
||||||
'--config-dir-path',
|
|
||||||
dest='config_dir_path',
|
|
||||||
default=const.YUM_REPO_DIR,
|
|
||||||
help='set the absolute directory path that holds all repo '
|
|
||||||
'configuration files'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Common file path argument
|
|
||||||
common_parse = argparse.ArgumentParser(add_help=False)
|
|
||||||
common_parse.add_argument(
|
|
||||||
'--config-file-path',
|
|
||||||
dest='config_file_path',
|
|
||||||
help=('set the absolute file path of the configuration file to be '
|
|
||||||
'updated.')
|
|
||||||
)
|
|
||||||
|
|
||||||
# Main parser
|
|
||||||
main_parser = argparse.ArgumentParser()
|
|
||||||
main_parser.add_argument(
|
|
||||||
'--verbose', '-v',
|
|
||||||
action='store_true',
|
|
||||||
default=False,
|
|
||||||
help='enable verbose log level for debugging',
|
|
||||||
)
|
|
||||||
subparsers = main_parser.add_subparsers(dest='command')
|
|
||||||
|
|
||||||
# Subcommands
|
|
||||||
subparsers.add_parser(
|
|
||||||
'repo',
|
|
||||||
parents=[common_parse, environment_parse, repo_args_parser,
|
|
||||||
options_parse],
|
|
||||||
help='updates a yum repository options'
|
|
||||||
)
|
|
||||||
subparsers.add_parser(
|
|
||||||
'global',
|
|
||||||
parents=[common_parse, environment_parse, options_parse],
|
|
||||||
help='updates global yum configuration options'
|
|
||||||
)
|
|
||||||
|
|
||||||
if py_version >= 3:
|
|
||||||
subparsers.add_parser(
|
|
||||||
'enable-compose-repos',
|
|
||||||
parents=[compose_args_parser, environment_parse],
|
|
||||||
help='enable CentOS compose repos based on an compose url.'
|
|
||||||
)
|
|
||||||
|
|
||||||
for min_distro_ver in const.DNF_MODULE_MINIMAL_DISTRO_VERSIONS:
|
|
||||||
if (distro == min_distro_ver.get('distro') and int(
|
|
||||||
major_version) >= min_distro_ver.get('min_version')):
|
|
||||||
subparsers.add_parser(
|
|
||||||
'module',
|
|
||||||
parents=[dnf_module_parser],
|
|
||||||
help='updates yum module options'
|
|
||||||
)
|
|
||||||
break
|
|
||||||
|
|
||||||
args = main_parser.parse_args()
|
|
||||||
if args.command is None:
|
|
||||||
main_parser.print_help()
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
if args.verbose:
|
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
|
||||||
logging.debug('Logging level set to DEBUG')
|
|
||||||
|
|
||||||
if args.command == 'repo':
|
|
||||||
set_dict = options_to_dict(args.set_opts)
|
|
||||||
config_obj = cfg.TripleOYumRepoConfig(
|
|
||||||
dir_path=args.config_dir_path,
|
|
||||||
environment_file=args.env_file)
|
|
||||||
if args.name is not None:
|
|
||||||
config_obj.add_or_update_section(args.name, set_dict=set_dict,
|
|
||||||
file_path=args.config_file_path,
|
|
||||||
enabled=args.enable,
|
|
||||||
from_url=args.down_url)
|
|
||||||
else:
|
|
||||||
# When no section (name) is provided, we consider all sections from
|
|
||||||
# repo file downloaded from the URL, otherwise fail.
|
|
||||||
if args.down_url is None:
|
|
||||||
logging.error("You must provide a repo 'name' or a valid "
|
|
||||||
"'url' where repo info can be downloaded.")
|
|
||||||
sys.exit(2)
|
|
||||||
config_obj.add_or_update_all_sections_from_url(
|
|
||||||
args.down_url, file_path=args.config_file_path,
|
|
||||||
set_dict=set_dict, enabled=args.enable)
|
|
||||||
|
|
||||||
elif args.command == 'module':
|
|
||||||
import tripleo_repos.yum_config.dnf_manager as dnf_mgr
|
|
||||||
dnf_mod_mgr = dnf_mgr.DnfModuleManager()
|
|
||||||
dnf_method = getattr(dnf_mod_mgr, args.operation + "_module")
|
|
||||||
dnf_method(args.name, stream=args.stream, profile=args.profile)
|
|
||||||
|
|
||||||
elif args.command == 'global':
|
|
||||||
set_dict = options_to_dict(args.set_opts)
|
|
||||||
config_obj = cfg.TripleOYumGlobalConfig(
|
|
||||||
file_path=args.config_file_path,
|
|
||||||
environment_file=args.env_file)
|
|
||||||
|
|
||||||
config_obj.update_section('main', set_dict)
|
|
||||||
|
|
||||||
elif args.command == 'enable-compose-repos':
|
|
||||||
import tripleo_repos.yum_config.compose_repos as compose_repos
|
|
||||||
repo_obj = compose_repos.TripleOYumComposeRepoConfig(
|
|
||||||
args.compose_url,
|
|
||||||
args.release,
|
|
||||||
dir_path=args.config_dir_path,
|
|
||||||
arch=args.arch,
|
|
||||||
environment_file=args.env_file)
|
|
||||||
|
|
||||||
repo_obj.enable_compose_repos(variants=args.variants,
|
|
||||||
override_repos=args.disable_conflicting)
|
|
||||||
if args.disable_repos:
|
|
||||||
for file in args.disable_repos:
|
|
||||||
valid_path = None
|
|
||||||
rel_path = os.path.join(args.config_dir_path, file)
|
|
||||||
|
|
||||||
if cfg.validated_file_path(file):
|
|
||||||
valid_path = file
|
|
||||||
elif cfg.validated_file_path(rel_path):
|
|
||||||
valid_path = rel_path
|
|
||||||
|
|
||||||
if valid_path is not None:
|
|
||||||
repo_obj.update_all_sections(valid_path, enabled=False)
|
|
||||||
|
|
||||||
|
|
||||||
def cli_entrypoint():
|
|
||||||
try:
|
|
||||||
main()
|
|
||||||
sys.exit(0)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logging.info("Exiting on user interrupt")
|
|
||||||
sys.exit(2)
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(str(e))
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
cli_entrypoint()
|
|
@ -1,206 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
from .constants import (
|
|
||||||
YUM_REPO_DIR,
|
|
||||||
YUM_REPO_FILE_EXTENSION,
|
|
||||||
YUM_REPO_SUPPORTED_OPTIONS,
|
|
||||||
COMPOSE_REPOS_RELEASES,
|
|
||||||
COMPOSE_REPOS_INFO_PATH,
|
|
||||||
COMPOSE_REPOS_URL_PATTERN,
|
|
||||||
COMPOSE_REPOS_URL_REPLACE_STR,
|
|
||||||
)
|
|
||||||
from .exceptions import (
|
|
||||||
TripleOYumConfigInvalidSection,
|
|
||||||
TripleOYumConfigComposeError,
|
|
||||||
)
|
|
||||||
from .yum_config import (
|
|
||||||
TripleOYumConfig
|
|
||||||
)
|
|
||||||
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOYumComposeRepoConfig(TripleOYumConfig):
|
|
||||||
"""Manages yum repo configuration files for CentOS Compose."""
|
|
||||||
|
|
||||||
def __init__(self, compose_url, release, dir_path=None, arch=None,
|
|
||||||
environment_file=None):
|
|
||||||
conf_dir_path = dir_path or YUM_REPO_DIR
|
|
||||||
self.arch = arch or 'x86_64'
|
|
||||||
|
|
||||||
# 1. validate release name
|
|
||||||
if release not in COMPOSE_REPOS_RELEASES:
|
|
||||||
msg = 'CentOS release not supported.'
|
|
||||||
raise TripleOYumConfigComposeError(error_msg=msg)
|
|
||||||
self.release = release
|
|
||||||
|
|
||||||
# 2. Validate URL
|
|
||||||
pattern = re.compile(COMPOSE_REPOS_URL_PATTERN[self.release])
|
|
||||||
if not pattern.match(compose_url):
|
|
||||||
msg = 'The provided URL does not match the expect pattern.'
|
|
||||||
raise TripleOYumConfigComposeError(error_msg=msg)
|
|
||||||
|
|
||||||
# 3. Get compose info from url
|
|
||||||
segments = [compose_url,
|
|
||||||
COMPOSE_REPOS_INFO_PATH[self.release]]
|
|
||||||
self.compose_info_url = '/'.join(s.strip('/') for s in segments)
|
|
||||||
self.compose_info = self._get_compose_info()
|
|
||||||
|
|
||||||
# 4. Get compose-id from metadata
|
|
||||||
self.compose_id = self.compose_info['compose']['id']
|
|
||||||
|
|
||||||
# 5. Replace the compose-id from url to avoid 'labels'
|
|
||||||
repl_args = {'compose_id': self.compose_id}
|
|
||||||
self.compose_url = (
|
|
||||||
pattern.sub(
|
|
||||||
COMPOSE_REPOS_URL_REPLACE_STR[self.release] % repl_args,
|
|
||||||
compose_url)
|
|
||||||
)
|
|
||||||
|
|
||||||
super(TripleOYumComposeRepoConfig, self).__init__(
|
|
||||||
valid_options=YUM_REPO_SUPPORTED_OPTIONS,
|
|
||||||
dir_path=conf_dir_path,
|
|
||||||
file_extension=YUM_REPO_FILE_EXTENSION,
|
|
||||||
environment_file=environment_file)
|
|
||||||
|
|
||||||
def _get_compose_info(self):
|
|
||||||
"""Retrieve compose info for a provided compose-id url."""
|
|
||||||
# NOTE(dviroel): works for both centos 8 and 9
|
|
||||||
import urllib.request
|
|
||||||
try:
|
|
||||||
logging.debug("Retrieving compose info from url: %s",
|
|
||||||
self.compose_info_url)
|
|
||||||
res = urllib.request.urlopen(self.compose_info_url)
|
|
||||||
except Exception:
|
|
||||||
msg = ("Failed to retrieve compose info from url: %s"
|
|
||||||
% self.compose_info_url)
|
|
||||||
raise TripleOYumConfigComposeError(error_msg=msg)
|
|
||||||
compose_info = json.loads(res.read())
|
|
||||||
if compose_info['header']['version'] != "1.2":
|
|
||||||
# NOTE(dviroel): Log a warning just in case we receive a different
|
|
||||||
# version here. Code may fail depending on the change.
|
|
||||||
logging.warning("Expecting compose info version '1.2' but got %s.",
|
|
||||||
compose_info['header']['version'])
|
|
||||||
return compose_info['payload']
|
|
||||||
|
|
||||||
def _get_repo_name(self, variant):
|
|
||||||
return " ".join([self.compose_id, variant])
|
|
||||||
|
|
||||||
def _get_repo_filename(self, variant):
|
|
||||||
return "-".join([self.compose_id, variant]) + '.repo'
|
|
||||||
|
|
||||||
def _get_repo_base_url(self, variant):
|
|
||||||
"""Build the base_url based on variant name and system architecture."""
|
|
||||||
variant_info = self.compose_info['variants'][variant]
|
|
||||||
if not variant_info['paths'].get('repository', {}).get(self.arch):
|
|
||||||
# Variant has no support yet
|
|
||||||
return None
|
|
||||||
segments = [self.compose_url,
|
|
||||||
variant_info['paths']['repository'][self.arch]]
|
|
||||||
return '/'.join(s.strip('/') for s in segments)
|
|
||||||
|
|
||||||
def get_compose_variants(self):
|
|
||||||
return self.compose_info['variants'].keys()
|
|
||||||
|
|
||||||
def enable_compose_repos(self, variants=None, override_repos=False):
|
|
||||||
"""Enable CentOS compose repos of a given variant list.
|
|
||||||
|
|
||||||
This function will build from scratch all repos for a given compose-id
|
|
||||||
url. If a list of variants is not provided, it will enable all for all
|
|
||||||
variants returned from compose info.
|
|
||||||
|
|
||||||
:param variants: A list of variant names to be enabled.
|
|
||||||
:param override_repos: True if all matching variants in the same
|
|
||||||
repo directory should be disable in favor of the new repos.
|
|
||||||
"""
|
|
||||||
if variants:
|
|
||||||
for var in variants:
|
|
||||||
if not (var in self.compose_info['variants'].keys()):
|
|
||||||
msg = 'One or more provided variants are invalid.'
|
|
||||||
raise TripleOYumConfigComposeError(error_msg=msg)
|
|
||||||
|
|
||||||
else:
|
|
||||||
variants = self.compose_info['variants'].keys()
|
|
||||||
|
|
||||||
updated_repos = {}
|
|
||||||
for var in variants:
|
|
||||||
base_url = self._get_repo_base_url(var)
|
|
||||||
if not base_url:
|
|
||||||
continue
|
|
||||||
add_dict = {
|
|
||||||
'name': self._get_repo_name(var),
|
|
||||||
'baseurl': base_url,
|
|
||||||
'enabled': '1',
|
|
||||||
'gpgcheck': '0',
|
|
||||||
}
|
|
||||||
filename = self._get_repo_filename(var)
|
|
||||||
file_path = os.path.join(self.dir_path, filename)
|
|
||||||
# create a file if doesn't exist and add a section to it
|
|
||||||
try:
|
|
||||||
self.add_section(var.lower(), add_dict, file_path)
|
|
||||||
except TripleOYumConfigInvalidSection:
|
|
||||||
logging.debug("Section '%s' that already exists in this file. "
|
|
||||||
"Trying to update it...", var)
|
|
||||||
self.update_section(var.lower(),
|
|
||||||
set_dict=add_dict,
|
|
||||||
file_path=file_path)
|
|
||||||
# needed to override other repos
|
|
||||||
updated_repos[var.lower()] = file_path
|
|
||||||
|
|
||||||
if override_repos:
|
|
||||||
for var in updated_repos:
|
|
||||||
config_files = self._get_config_files(var)
|
|
||||||
for file in config_files:
|
|
||||||
if file != updated_repos[var]:
|
|
||||||
msg = ("Disabling matching section '%(section)s' in "
|
|
||||||
"configuration file: %(file)s.")
|
|
||||||
msg_args = {
|
|
||||||
'section': var,
|
|
||||||
'file': file,
|
|
||||||
}
|
|
||||||
logging.debug(msg, msg_args)
|
|
||||||
self.update_section(var, enabled=False, file_path=file)
|
|
||||||
|
|
||||||
def add_section(self, section, add_dict, file_path):
|
|
||||||
# Create a new file if it does not exists
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
with open(file_path, 'w+'):
|
|
||||||
pass
|
|
||||||
super(TripleOYumComposeRepoConfig, self).add_section(
|
|
||||||
section, add_dict, file_path)
|
|
||||||
|
|
||||||
def update_section(
|
|
||||||
self, section, set_dict=None, enabled=None, file_path=None):
|
|
||||||
update_dict = set_dict or {}
|
|
||||||
if enabled is not None:
|
|
||||||
update_dict['enabled'] = '1' if enabled else '0'
|
|
||||||
if update_dict:
|
|
||||||
super(TripleOYumComposeRepoConfig, self).update_section(
|
|
||||||
section, update_dict, file_path=file_path)
|
|
||||||
|
|
||||||
def update_all_sections(self, file_path, set_dict=None, enabled=None):
|
|
||||||
update_dict = set_dict or {}
|
|
||||||
if enabled is not None:
|
|
||||||
update_dict['enabled'] = '1' if enabled else '0'
|
|
||||||
if update_dict:
|
|
||||||
super(TripleOYumComposeRepoConfig, self).update_all_sections(
|
|
||||||
update_dict, file_path)
|
|
@ -1,87 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
|
||||||
"""
|
|
||||||
List of options that can be updated for yum repo files.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
YUM_REPO_SUPPORTED_OPTIONS = [
|
|
||||||
'baseurl',
|
|
||||||
'cost',
|
|
||||||
'enabled',
|
|
||||||
'exclude',
|
|
||||||
'excludepkgs',
|
|
||||||
'gpgcheck',
|
|
||||||
'gpgkey',
|
|
||||||
'includepkgs',
|
|
||||||
'metalink',
|
|
||||||
'mirrorlist',
|
|
||||||
'module_hotfixes',
|
|
||||||
'name',
|
|
||||||
'priority',
|
|
||||||
'skip_if_unavailable',
|
|
||||||
]
|
|
||||||
|
|
||||||
"""
|
|
||||||
Default constants for yum repo operations.
|
|
||||||
"""
|
|
||||||
YUM_REPO_DIR = '/etc/yum.repos.d'
|
|
||||||
YUM_REPO_FILE_EXTENSION = '.repo'
|
|
||||||
|
|
||||||
"""
|
|
||||||
Default constants for yum/dnf global configurations.
|
|
||||||
"""
|
|
||||||
YUM_GLOBAL_CONFIG_FILE_PATH = '/etc/yum.conf'
|
|
||||||
|
|
||||||
"""
|
|
||||||
CentOS Stream compose repos defaults
|
|
||||||
"""
|
|
||||||
COMPOSE_REPOS_RELEASES = [
|
|
||||||
"centos-stream-8",
|
|
||||||
"centos-stream-9"
|
|
||||||
]
|
|
||||||
|
|
||||||
COMPOSE_REPOS_SUPPORTED_ARCHS = [
|
|
||||||
"aarch64",
|
|
||||||
"ppc64le",
|
|
||||||
"x86_64"
|
|
||||||
]
|
|
||||||
|
|
||||||
COMPOSE_REPOS_URL_PATTERN = {
|
|
||||||
"centos-stream-8": r"(^https:.*.centos.org/)([^/]*)(/compose/?$)",
|
|
||||||
"centos-stream-9": r"(^https:.*.centos.org/.*/)(.*)(/compose/?$)",
|
|
||||||
}
|
|
||||||
|
|
||||||
COMPOSE_REPOS_URL_REPLACE_STR = {
|
|
||||||
"centos-stream-8": r"\1%(compose_id)s\3",
|
|
||||||
"centos-stream-9": r"\1%(compose_id)s\3",
|
|
||||||
}
|
|
||||||
|
|
||||||
COMPOSE_REPOS_INFO_PATH = {
|
|
||||||
"centos-stream-8": "metadata/composeinfo.json",
|
|
||||||
"centos-stream-9": "metadata/composeinfo.json",
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
|
||||||
DNF Manager constants
|
|
||||||
"""
|
|
||||||
DNF_MODULE_MINIMAL_DISTRO_VERSIONS = [
|
|
||||||
{'distro': 'centos', 'min_version': 8},
|
|
||||||
{'distro': 'rhel', 'min_version': 8},
|
|
||||||
{'distro': 'fedora', 'min_version': 22},
|
|
||||||
]
|
|
@ -1,92 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
|
||||||
import logging
|
|
||||||
|
|
||||||
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class DnfModuleManager:
|
|
||||||
"""Class that manages dnf modules."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
# lazy import to allow CLI to start without dnf
|
|
||||||
import dnf
|
|
||||||
self.base = dnf.Base()
|
|
||||||
self.base.conf.read()
|
|
||||||
self.base.conf.best = True
|
|
||||||
self.base.read_all_repos()
|
|
||||||
self.base.fill_sack()
|
|
||||||
self.module_base = dnf.module.module_base.ModuleBase(self.base)
|
|
||||||
|
|
||||||
def _get_module_spec(self, name, stream=None, profile=None):
|
|
||||||
"""Return a module spec string based on stream and/or profile."""
|
|
||||||
module_spec = name
|
|
||||||
if stream:
|
|
||||||
module_spec += ':{0}'.format(stream)
|
|
||||||
if profile:
|
|
||||||
module_spec += '/{0}'.format(profile)
|
|
||||||
return module_spec
|
|
||||||
|
|
||||||
def _do_transaction(self):
|
|
||||||
"""Perform the resolved transaction."""
|
|
||||||
try:
|
|
||||||
self.base.do_transaction()
|
|
||||||
except RuntimeError:
|
|
||||||
logging.error('This command has to be run with superuser '
|
|
||||||
'privileges.')
|
|
||||||
raise
|
|
||||||
|
|
||||||
def enable_module(self, name, stream=None, profile=None):
|
|
||||||
"""Enable a module stream."""
|
|
||||||
self.module_base.enable(
|
|
||||||
[self._get_module_spec(name, stream=stream, profile=profile)]
|
|
||||||
)
|
|
||||||
self._do_transaction()
|
|
||||||
logging.info("Module %s was enabled.", name)
|
|
||||||
|
|
||||||
def disable_module(self, name, stream=None, profile=None):
|
|
||||||
"""Disable a module stream."""
|
|
||||||
self.module_base.disable(
|
|
||||||
[self._get_module_spec(name, stream=stream, profile=profile)]
|
|
||||||
)
|
|
||||||
self._do_transaction()
|
|
||||||
logging.info("Module %s was disabled.", name)
|
|
||||||
|
|
||||||
def reset_module(self, name, stream=None, profile=None):
|
|
||||||
"""Reset a module. It will no longer be enabled or disabled."""
|
|
||||||
self.module_base.reset(
|
|
||||||
[self._get_module_spec(name, stream=stream, profile=profile)]
|
|
||||||
)
|
|
||||||
self._do_transaction()
|
|
||||||
logging.info("Module %s was reset.", name)
|
|
||||||
|
|
||||||
def install_module(self, name, stream=None, profile=None):
|
|
||||||
"""Install packages of a module profile."""
|
|
||||||
self.module_base.install(
|
|
||||||
[self._get_module_spec(name, stream=stream, profile=profile)]
|
|
||||||
)
|
|
||||||
self.base.resolve()
|
|
||||||
self.base.download_packages(self.base.transaction.install_set)
|
|
||||||
self._do_transaction()
|
|
||||||
logging.info("Module %s was installed.", name)
|
|
||||||
|
|
||||||
def remove_module(self, name, stream=None, profile=None):
|
|
||||||
"""Remove packages of a module profile."""
|
|
||||||
self.module_base.remove(
|
|
||||||
[self._get_module_spec(name, stream=stream, profile=profile)]
|
|
||||||
)
|
|
||||||
self._do_transaction()
|
|
||||||
logging.info("Module %s was removed.", name)
|
|
@ -1,76 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
|
||||||
|
|
||||||
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class Base(Exception):
|
|
||||||
"""Base Exception class."""
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOYumConfigNotFound(Base):
|
|
||||||
"""A configuration file was not found in the provided file path."""
|
|
||||||
|
|
||||||
def __init__(self, error_msg):
|
|
||||||
super(TripleOYumConfigNotFound, self).__init__(error_msg)
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOYumConfigPermissionDenied(Base):
|
|
||||||
"""THh user has no permission to modify the configuration file."""
|
|
||||||
|
|
||||||
def __init__(self, error_msg):
|
|
||||||
super(TripleOYumConfigPermissionDenied, self).__init__(error_msg)
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOYumConfigFileParseError(Base):
|
|
||||||
"""Encountered an error while parsing the configuration file."""
|
|
||||||
|
|
||||||
def __init__(self, error_msg):
|
|
||||||
super(TripleOYumConfigFileParseError, self).__init__(error_msg)
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOYumConfigInvalidSection(Base):
|
|
||||||
"""The configuration file does not have the requested section.
|
|
||||||
|
|
||||||
This exception is raised when the expected section in the configuration
|
|
||||||
file does not exist and the class will not create a new one.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, error_msg):
|
|
||||||
super(TripleOYumConfigInvalidSection, self).__init__(error_msg)
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOYumConfigInvalidOption(Base):
|
|
||||||
"""One or more options are not valid for this configuration file."""
|
|
||||||
|
|
||||||
def __init__(self, error_msg):
|
|
||||||
super(TripleOYumConfigInvalidOption, self).__init__(error_msg)
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOYumConfigComposeError(Base):
|
|
||||||
"""An error occurred while configuring CentOS compose repos."""
|
|
||||||
|
|
||||||
def __init__(self, error_msg):
|
|
||||||
super(TripleOYumConfigComposeError, self).__init__(error_msg)
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOYumConfigUrlError(Base):
|
|
||||||
"""An error occurred while fetching repo from the url."""
|
|
||||||
|
|
||||||
def __init__(self, error_msg):
|
|
||||||
super(TripleOYumConfigUrlError, self).__init__(error_msg)
|
|
@ -1,50 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
# TODO(dviroel): Merge in a utils file when refactoring tripleo-repos.
|
|
||||||
def get_distro_info():
|
|
||||||
"""Get distro info from os-release file.
|
|
||||||
|
|
||||||
:return: distro_id, distro_major_version_id and distro_name
|
|
||||||
"""
|
|
||||||
if not os.path.exists('/etc/os-release'):
|
|
||||||
return platform.system(), 'unknown', 'unknown'
|
|
||||||
|
|
||||||
output = subprocess.Popen(
|
|
||||||
'source /etc/os-release && echo -e -n "$ID\n$VERSION_ID\n$NAME"',
|
|
||||||
shell=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=open(os.devnull, 'w'),
|
|
||||||
executable='/bin/bash',
|
|
||||||
universal_newlines=True).communicate()
|
|
||||||
|
|
||||||
# distro_id and distro_version_id will always be at least an empty string
|
|
||||||
distro_id, distro_version_id, distro_name = output[0].split('\n')
|
|
||||||
|
|
||||||
# if distro_version_id is empty string the major version will be empty
|
|
||||||
# string too
|
|
||||||
distro_major_version_id = distro_version_id.split('.')[0]
|
|
||||||
|
|
||||||
# check if that is UBI subcase?
|
|
||||||
if os.path.exists('/etc/yum.repos.d/ubi.repo'):
|
|
||||||
distro_id = 'ubi'
|
|
||||||
|
|
||||||
return distro_id, distro_major_version_id, distro_name
|
|
@ -1,449 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
from __future__ import (absolute_import, division, print_function)
|
|
||||||
|
|
||||||
|
|
||||||
import io
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from .constants import (
|
|
||||||
YUM_GLOBAL_CONFIG_FILE_PATH,
|
|
||||||
YUM_REPO_DIR,
|
|
||||||
YUM_REPO_FILE_EXTENSION,
|
|
||||||
YUM_REPO_SUPPORTED_OPTIONS,
|
|
||||||
)
|
|
||||||
from .exceptions import (
|
|
||||||
TripleOYumConfigFileParseError,
|
|
||||||
TripleOYumConfigInvalidOption,
|
|
||||||
TripleOYumConfigInvalidSection,
|
|
||||||
TripleOYumConfigNotFound,
|
|
||||||
TripleOYumConfigUrlError,
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
import tripleo_repos.utils as repos_utils
|
|
||||||
except ImportError:
|
|
||||||
import ansible_collections.tripleo.repos.plugins.module_utils.\
|
|
||||||
tripleo_repos.utils as repos_utils
|
|
||||||
|
|
||||||
|
|
||||||
py_version = sys.version_info.major
|
|
||||||
if py_version < 3:
|
|
||||||
import ConfigParser as cfg_parser
|
|
||||||
|
|
||||||
def save_section_to_file(file_path, config, section, updates):
|
|
||||||
"""Updates a specific 'section' in a 'config' and write to disk.
|
|
||||||
|
|
||||||
:param file_path: Absolute path to the file to be updated.
|
|
||||||
:param config: configparser object created from the file.
|
|
||||||
:param section: section name to be updated.
|
|
||||||
:param updates: dict with options to update in section.
|
|
||||||
"""
|
|
||||||
|
|
||||||
for k, v in updates.items():
|
|
||||||
config.set(section, k, v)
|
|
||||||
with open(file_path, 'w') as f:
|
|
||||||
config.write(f)
|
|
||||||
|
|
||||||
# NOTE(dviroel) Need to manually remove whitespaces around "=", to
|
|
||||||
# avoid legacy scripts failing on parsing ini files.
|
|
||||||
with open(file_path, 'r+') as f:
|
|
||||||
lines = f.readlines()
|
|
||||||
# erase content before writing again
|
|
||||||
f.truncate(0)
|
|
||||||
f.seek(0)
|
|
||||||
for line in lines:
|
|
||||||
line = line.strip()
|
|
||||||
if "=" in line:
|
|
||||||
option_kv = line.split("=", 1)
|
|
||||||
option_kv = list(map(str.strip, option_kv))
|
|
||||||
f.write("%s%s%s\n" % (option_kv[0], "=", option_kv[1]))
|
|
||||||
else:
|
|
||||||
f.write(line + "\n")
|
|
||||||
|
|
||||||
else:
|
|
||||||
import configparser as cfg_parser
|
|
||||||
|
|
||||||
def save_section_to_file(file_path, config, section, updates):
|
|
||||||
"""Updates a specific 'section' in a 'config' and write to disk.
|
|
||||||
|
|
||||||
:param file_path: Absolute path to the file to be updated.
|
|
||||||
:param config: configparser object created from the file.
|
|
||||||
:param section: section name to be updated.
|
|
||||||
:param updates: dict with options to update in section.
|
|
||||||
"""
|
|
||||||
config[section].update(updates)
|
|
||||||
with open(file_path, 'w') as f:
|
|
||||||
config.write(f, space_around_delimiters=False)
|
|
||||||
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
def validated_file_path(file_path):
|
|
||||||
if os.path.isfile(file_path) and os.access(file_path, os.W_OK):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def source_env_file(source_file, update=True):
|
|
||||||
"""Source a file and get all environment variables in a dict format."""
|
|
||||||
p_open = subprocess.Popen(". %s; env" % source_file,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
shell=True)
|
|
||||||
data = p_open.communicate()[0].decode('ascii')
|
|
||||||
|
|
||||||
env_dict = dict(
|
|
||||||
line.split("=", 1) for line in data.splitlines()
|
|
||||||
if len(line.split("=", 1)) > 1)
|
|
||||||
if update:
|
|
||||||
os.environ.update(env_dict)
|
|
||||||
return env_dict
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOYumConfig:
|
|
||||||
"""
|
|
||||||
This class is a base class for updating yum configuration files in
|
|
||||||
ini format. The class validates the if the configuration files exists and
|
|
||||||
if it has the the permissions needed. A list of updatable options may be
|
|
||||||
provided to the class constructor.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, valid_options=None, dir_path=None, file_extension=None,
|
|
||||||
environment_file=None):
|
|
||||||
"""
|
|
||||||
Creates a TripleOYumConfig object that holds configuration file
|
|
||||||
information.
|
|
||||||
|
|
||||||
:param valid_options: A list of options that can be updated on this
|
|
||||||
file.
|
|
||||||
:param dir_path: The directory path that this class can use to search
|
|
||||||
for configuration files to be updated.
|
|
||||||
:param: file_extension: File extension to filter configuration files
|
|
||||||
in the search directory.
|
|
||||||
:param environment_file: File to be read before updating environment
|
|
||||||
variables.
|
|
||||||
"""
|
|
||||||
self.dir_path = dir_path
|
|
||||||
self.file_extension = file_extension
|
|
||||||
self.valid_options = valid_options
|
|
||||||
self.env_file = environment_file
|
|
||||||
|
|
||||||
# Sanity checks
|
|
||||||
if dir_path:
|
|
||||||
if not os.path.isdir(dir_path):
|
|
||||||
msg = ('The configuration dir "{0}" was not found in the '
|
|
||||||
'provided path.').format(dir_path)
|
|
||||||
raise TripleOYumConfigNotFound(error_msg=msg)
|
|
||||||
|
|
||||||
if self.env_file:
|
|
||||||
source_env_file(os.path.expanduser(self.env_file), update=True)
|
|
||||||
|
|
||||||
def _read_config_file(self, file_path, section=None):
|
|
||||||
"""Reads a configuration file.
|
|
||||||
|
|
||||||
:param section: The name of the section that will be update. Only used
|
|
||||||
to fail earlier if the section is not found.
|
|
||||||
:return: a config parser object and the full file path.
|
|
||||||
"""
|
|
||||||
config = cfg_parser.ConfigParser()
|
|
||||||
file_paths = [file_path]
|
|
||||||
if self.dir_path:
|
|
||||||
# if dir_path is configured, we can search for filename there
|
|
||||||
file_paths.append(os.path.join(self.dir_path, file_path))
|
|
||||||
|
|
||||||
valid_file_path = None
|
|
||||||
for file in file_paths:
|
|
||||||
if validated_file_path(file):
|
|
||||||
valid_file_path = file
|
|
||||||
break
|
|
||||||
if not valid_file_path:
|
|
||||||
msg = ('The configuration file "{0}" was '
|
|
||||||
'not found.'.format(file_path))
|
|
||||||
raise TripleOYumConfigNotFound(error_msg=msg)
|
|
||||||
|
|
||||||
try:
|
|
||||||
config.read(valid_file_path)
|
|
||||||
except cfg_parser.Error:
|
|
||||||
msg = 'Unable to parse configuration file {0}.'.format(
|
|
||||||
valid_file_path)
|
|
||||||
raise TripleOYumConfigFileParseError(error_msg=msg)
|
|
||||||
|
|
||||||
if section and section not in config.sections():
|
|
||||||
msg = ('The provided section "{0}" was not found in the '
|
|
||||||
'configuration file {1}.').format(
|
|
||||||
section, valid_file_path)
|
|
||||||
raise TripleOYumConfigInvalidSection(error_msg=msg)
|
|
||||||
|
|
||||||
return config, valid_file_path
|
|
||||||
|
|
||||||
def _get_config_files(self, section):
|
|
||||||
"""Gets all configuration file paths for a given section.
|
|
||||||
|
|
||||||
This method will search for a 'section' name in all files inside the
|
|
||||||
configuration directory. All files with 'section' will be returned.
|
|
||||||
|
|
||||||
:param section: Section to be found inside configuration files.
|
|
||||||
:return: A list of config file paths.
|
|
||||||
"""
|
|
||||||
# Search for a configuration file that has the provided section
|
|
||||||
config_files_path = []
|
|
||||||
if section and self.dir_path:
|
|
||||||
for file in os.listdir(self.dir_path):
|
|
||||||
# Skip files that don't match the file extension or are not
|
|
||||||
# writable
|
|
||||||
if self.file_extension and not file.endswith(
|
|
||||||
self.file_extension):
|
|
||||||
continue
|
|
||||||
if not os.access(os.path.join(self.dir_path, file), os.W_OK):
|
|
||||||
continue
|
|
||||||
|
|
||||||
tmp_config = cfg_parser.ConfigParser()
|
|
||||||
try:
|
|
||||||
tmp_config.read(os.path.join(self.dir_path, file))
|
|
||||||
except cfg_parser.Error:
|
|
||||||
continue
|
|
||||||
if section in tmp_config.sections():
|
|
||||||
config_files_path.append(os.path.join(self.dir_path, file))
|
|
||||||
|
|
||||||
return config_files_path
|
|
||||||
|
|
||||||
def update_section(self, section, set_dict, file_path=None):
|
|
||||||
"""Updates a set of options of a section.
|
|
||||||
|
|
||||||
If a file path is not provided by the caller, this function will search
|
|
||||||
for the section in all files located in the working directory and
|
|
||||||
update each one of them.
|
|
||||||
|
|
||||||
:param section: Name of the section on the configuration file that will
|
|
||||||
be updated.
|
|
||||||
:param set_dict: Dict with all options and values to be updated in the
|
|
||||||
configuration file section.
|
|
||||||
:param file_path: Path to the configuration file to be updated.
|
|
||||||
"""
|
|
||||||
if self.valid_options:
|
|
||||||
if not all(key in self.valid_options for key in set_dict.keys()):
|
|
||||||
msg = 'One or more provided options are not valid.'
|
|
||||||
raise TripleOYumConfigInvalidOption(error_msg=msg)
|
|
||||||
|
|
||||||
files = [file_path] if file_path else self._get_config_files(section)
|
|
||||||
if not files:
|
|
||||||
msg = ('No configuration files were found for the provided '
|
|
||||||
'section {0}'.format(section))
|
|
||||||
raise TripleOYumConfigNotFound(error_msg=msg)
|
|
||||||
|
|
||||||
for k, v in set_dict.items():
|
|
||||||
set_dict[k] = os.path.expandvars(v)
|
|
||||||
for file in files:
|
|
||||||
config, file = self._read_config_file(file, section=section)
|
|
||||||
# Update configuration file with dict updates
|
|
||||||
save_section_to_file(file, config, section, set_dict)
|
|
||||||
|
|
||||||
logging.info("Section '%s' was successfully "
|
|
||||||
"updated.", section)
|
|
||||||
|
|
||||||
def add_section(self, section, add_dict, file_path):
|
|
||||||
""" Adds a new section with options in a provided config file.
|
|
||||||
|
|
||||||
:param section: Section name to be added to the config file.
|
|
||||||
:param add_dict: Dict with all options and values to be added into the
|
|
||||||
new section.
|
|
||||||
:param file_path: Path to the configuration file to be updated.
|
|
||||||
"""
|
|
||||||
if self.valid_options:
|
|
||||||
if not all(key in self.valid_options for key in add_dict.keys()):
|
|
||||||
msg = 'One or more provided options are not valid.'
|
|
||||||
raise TripleOYumConfigInvalidOption(error_msg=msg)
|
|
||||||
|
|
||||||
# This section shouldn't exist in the provided file
|
|
||||||
config, file_path = self._read_config_file(file_path=file_path)
|
|
||||||
if section in config.sections():
|
|
||||||
msg = ("Section '%s' already exists in the configuration "
|
|
||||||
"file.", section)
|
|
||||||
raise TripleOYumConfigInvalidSection(error_msg=msg)
|
|
||||||
|
|
||||||
for k, v in add_dict.items():
|
|
||||||
add_dict[k] = os.path.expandvars(v)
|
|
||||||
# Add new section
|
|
||||||
config.add_section(section)
|
|
||||||
# Update configuration file with dict updates
|
|
||||||
save_section_to_file(file_path, config, section, add_dict)
|
|
||||||
|
|
||||||
logging.info("Section '%s' was successfully "
|
|
||||||
"added.", section)
|
|
||||||
|
|
||||||
def update_all_sections(self, set_dict, file_path):
|
|
||||||
"""Updates all section of a given configuration file.
|
|
||||||
|
|
||||||
:param set_dict: Dict with all options and values to be updated in
|
|
||||||
the configuration file.
|
|
||||||
:param file_path: Path to the configuration file to be updated.
|
|
||||||
"""
|
|
||||||
if self.valid_options:
|
|
||||||
if not all(key in self.valid_options for key in set_dict.keys()):
|
|
||||||
msg = 'One or more provided options are not valid.'
|
|
||||||
raise TripleOYumConfigInvalidOption(error_msg=msg)
|
|
||||||
|
|
||||||
config, file_path = self._read_config_file(file_path)
|
|
||||||
for section in config.sections():
|
|
||||||
save_section_to_file(file_path, config, section, set_dict)
|
|
||||||
|
|
||||||
logging.info("All sections for '%s' were successfully "
|
|
||||||
"updated.", file_path)
|
|
||||||
|
|
||||||
def get_config_from_url(self, url):
|
|
||||||
content, status = repos_utils.http_get(url)
|
|
||||||
if status != 200:
|
|
||||||
msg = ("Invalid response code received from provided url: "
|
|
||||||
"{0}. Response code: {1}."
|
|
||||||
).format(url, status)
|
|
||||||
logging.error(msg)
|
|
||||||
raise TripleOYumConfigUrlError(error_msg=msg)
|
|
||||||
config = cfg_parser.ConfigParser()
|
|
||||||
if py_version < 3:
|
|
||||||
sfile = io.StringIO(content)
|
|
||||||
config.readfp(sfile)
|
|
||||||
else:
|
|
||||||
config.read_string(content)
|
|
||||||
return config
|
|
||||||
|
|
||||||
def get_options_from_url(self, url, section):
|
|
||||||
config = self.get_config_from_url(url)
|
|
||||||
if section not in config.sections():
|
|
||||||
msg = ("Section '{0}' was not found in the configuration file "
|
|
||||||
"provided by the url {1}.").format(section, url)
|
|
||||||
raise TripleOYumConfigInvalidSection(error_msg=msg)
|
|
||||||
return dict(config.items(section))
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOYumRepoConfig(TripleOYumConfig):
|
|
||||||
"""Manages yum repo configuration files."""
|
|
||||||
|
|
||||||
def __init__(self, dir_path=None, environment_file=None):
|
|
||||||
conf_dir_path = dir_path or YUM_REPO_DIR
|
|
||||||
|
|
||||||
super(TripleOYumRepoConfig, self).__init__(
|
|
||||||
valid_options=YUM_REPO_SUPPORTED_OPTIONS,
|
|
||||||
dir_path=conf_dir_path,
|
|
||||||
file_extension=YUM_REPO_FILE_EXTENSION,
|
|
||||||
environment_file=environment_file)
|
|
||||||
|
|
||||||
def update_section(
|
|
||||||
self, section, set_dict=None, file_path=None, enabled=None,
|
|
||||||
from_url=None):
|
|
||||||
update_dict = (
|
|
||||||
self.get_options_from_url(from_url, section) if from_url else {})
|
|
||||||
if set_dict:
|
|
||||||
update_dict.update(set_dict)
|
|
||||||
if enabled is not None:
|
|
||||||
update_dict['enabled'] = '1' if enabled else '0'
|
|
||||||
if update_dict:
|
|
||||||
super(TripleOYumRepoConfig, self).update_section(
|
|
||||||
section, update_dict, file_path=file_path)
|
|
||||||
|
|
||||||
def add_section(self, section, add_dict, file_path, enabled=None,
|
|
||||||
from_url=None):
|
|
||||||
update_dict = (
|
|
||||||
self.get_options_from_url(from_url, section) if from_url else {})
|
|
||||||
update_dict.update(add_dict)
|
|
||||||
|
|
||||||
if enabled is not None:
|
|
||||||
update_dict['enabled'] = '1' if enabled else '0'
|
|
||||||
super(TripleOYumRepoConfig, self).add_section(
|
|
||||||
section, update_dict, file_path)
|
|
||||||
|
|
||||||
def add_or_update_section(self, section, set_dict=None,
|
|
||||||
file_path=None, enabled=None,
|
|
||||||
create_if_not_exists=True, from_url=None):
|
|
||||||
new_set_dict = (
|
|
||||||
self.get_options_from_url(from_url, section) if from_url else {})
|
|
||||||
new_set_dict.update(set_dict)
|
|
||||||
# make sure that it has a name
|
|
||||||
if 'name' not in new_set_dict.keys():
|
|
||||||
new_set_dict['name'] = section
|
|
||||||
# Try to update existing repos
|
|
||||||
try:
|
|
||||||
self.update_section(
|
|
||||||
section, set_dict=new_set_dict, file_path=file_path,
|
|
||||||
enabled=enabled)
|
|
||||||
except TripleOYumConfigNotFound:
|
|
||||||
if not create_if_not_exists or file_path is None:
|
|
||||||
# there is nothing to do, we can't create a new config file
|
|
||||||
raise
|
|
||||||
# Create a new file if it does not exists
|
|
||||||
with open(file_path, 'w+'):
|
|
||||||
pass
|
|
||||||
self.add_section(section, new_set_dict, file_path, enabled=enabled)
|
|
||||||
|
|
||||||
except TripleOYumConfigInvalidSection:
|
|
||||||
self.add_section(section, new_set_dict, file_path, enabled=enabled)
|
|
||||||
|
|
||||||
def add_or_update_all_sections_from_url(
|
|
||||||
self, from_url, file_path=None, set_dict=None, enabled=None,
|
|
||||||
create_if_not_exists=True):
|
|
||||||
"""Adds or updates all sections based on repo file from a URL."""
|
|
||||||
tmp_config = self.get_config_from_url(from_url)
|
|
||||||
if file_path is None:
|
|
||||||
# Build a file_path based on download url. If not compatible,
|
|
||||||
# don't fill file_path and let the code search for sections in all
|
|
||||||
# repo files inside config dir_path.
|
|
||||||
file_name = from_url.split('/')[-1]
|
|
||||||
if file_name.endswith(".repo"):
|
|
||||||
# Expecting a '*.repo' filename here, since the file can't be
|
|
||||||
# created with a different extension
|
|
||||||
file_path = os.path.join(self.dir_path, file_name)
|
|
||||||
|
|
||||||
for section in tmp_config.sections():
|
|
||||||
update_dict = dict(tmp_config.items(section))
|
|
||||||
update_dict.update(set_dict)
|
|
||||||
self.add_or_update_section(
|
|
||||||
section, set_dict=update_dict,
|
|
||||||
file_path=file_path, enabled=enabled,
|
|
||||||
create_if_not_exists=create_if_not_exists)
|
|
||||||
|
|
||||||
|
|
||||||
class TripleOYumGlobalConfig(TripleOYumConfig):
|
|
||||||
"""Manages yum global configuration file."""
|
|
||||||
|
|
||||||
def __init__(self, file_path=None, environment_file=None):
|
|
||||||
self.conf_file_path = file_path or YUM_GLOBAL_CONFIG_FILE_PATH
|
|
||||||
logging.info("Using '%s' as yum global configuration "
|
|
||||||
"file.", self.conf_file_path)
|
|
||||||
if file_path is not None:
|
|
||||||
# validate user provided file path
|
|
||||||
validated_file_path(file_path)
|
|
||||||
else:
|
|
||||||
# If there is no default 'yum.conf' configuration file, we need to
|
|
||||||
# create it. If the user specify another conf file that doesn't
|
|
||||||
# exists, the operation will fail.
|
|
||||||
if not os.path.isfile(self.conf_file_path):
|
|
||||||
config = cfg_parser.ConfigParser()
|
|
||||||
config.read(self.conf_file_path)
|
|
||||||
config.add_section('main')
|
|
||||||
with open(self.conf_file_path, 'w+') as file:
|
|
||||||
config.write(file)
|
|
||||||
|
|
||||||
super(TripleOYumGlobalConfig, self).__init__(
|
|
||||||
environment_file=environment_file)
|
|
||||||
|
|
||||||
def update_section(self, section, set_dict, file_path=None):
|
|
||||||
super(TripleOYumGlobalConfig, self).update_section(
|
|
||||||
section, set_dict, file_path=(file_path or self.conf_file_path))
|
|
||||||
|
|
||||||
def add_section(self, section, add_dict, file_path=None):
|
|
||||||
add_file_path = file_path or self.conf_file_path
|
|
||||||
super(TripleOYumGlobalConfig, self).add_section(
|
|
||||||
section, add_dict, add_file_path)
|
|
@ -1,145 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# Copyright 2021 Red Hat, 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 = r'''
|
|
||||||
---
|
|
||||||
module: get_hash
|
|
||||||
|
|
||||||
short_description: Resolve rdo named tag to commit, full and distro hashes
|
|
||||||
|
|
||||||
version_added: "1.0.0"
|
|
||||||
|
|
||||||
description: ""
|
|
||||||
|
|
||||||
options:
|
|
||||||
os_version:
|
|
||||||
description: The operating system and version to fetch hashes for
|
|
||||||
required: false
|
|
||||||
type: str
|
|
||||||
default: centos8
|
|
||||||
release:
|
|
||||||
description: The release of OpenStack you want the hash info for
|
|
||||||
required: false
|
|
||||||
type: str
|
|
||||||
default: master
|
|
||||||
component:
|
|
||||||
description: The tripleo-ci component you are interested in
|
|
||||||
required: false
|
|
||||||
type: str
|
|
||||||
tag:
|
|
||||||
description: The named tag to fetch
|
|
||||||
required: false
|
|
||||||
type: str
|
|
||||||
default: current-tripleo
|
|
||||||
dlrn_url:
|
|
||||||
description: The url of the DLRN server to use for hash queries
|
|
||||||
required: false
|
|
||||||
type: str
|
|
||||||
default: https://trunk.rdoproject.org
|
|
||||||
|
|
||||||
author:
|
|
||||||
- Marios Andreou (@marios)
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = r'''
|
|
||||||
- name: Get the latest hash info for victoria centos8 tripleo component
|
|
||||||
tripleo_get_hash:
|
|
||||||
os_version: centos8
|
|
||||||
release: victoria
|
|
||||||
component: tripleo
|
|
||||||
dlrn_url: 'https://foo.bar.baz'
|
|
||||||
'''
|
|
||||||
|
|
||||||
RETURN = r'''
|
|
||||||
full_hash:
|
|
||||||
description: The full hash that identifies the build
|
|
||||||
type: str
|
|
||||||
returned: always
|
|
||||||
sample: 'f47f1db5af04ddd1ab4cc3ccadf95884d335b3f3_92f50ace'
|
|
||||||
distro_hash:
|
|
||||||
description: The distro hash
|
|
||||||
type: str
|
|
||||||
returned: when available
|
|
||||||
sample: '92f50acecd0a218936b7163e8362e75913b62af2'
|
|
||||||
commit_hash:
|
|
||||||
description: The commit hash
|
|
||||||
type: str
|
|
||||||
returned: when available
|
|
||||||
sample: 'f47f1db5af04ddd1ab4cc3ccadf95884d335b3f3'
|
|
||||||
extended_hash:
|
|
||||||
description: The extended hash
|
|
||||||
type: str
|
|
||||||
returned: when available
|
|
||||||
sample: 'f47f1db5af04ddd1ab4cc3ccadf95884d335b3f3'
|
|
||||||
dlrn_url:
|
|
||||||
description: The dlrn server url from which hash info was collected.
|
|
||||||
type: str
|
|
||||||
returned: always
|
|
||||||
sample: 'https://trunk.rdoproject.org/centos8-master/current-tripleo/delorean.repo.md5' # noqa E501
|
|
||||||
'''
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
def run_module():
|
|
||||||
result = dict(
|
|
||||||
success=False,
|
|
||||||
changed=False,
|
|
||||||
error="",
|
|
||||||
)
|
|
||||||
|
|
||||||
argument_spec = dict(
|
|
||||||
os_version=dict(type='str', required=False, default='centos8'),
|
|
||||||
release=dict(type='str', required=False, default='master'),
|
|
||||||
component=dict(type='str', required=False, default=None),
|
|
||||||
tag=dict(type='str', required=False, default='current-tripleo'),
|
|
||||||
dlrn_url=dict(type='str',
|
|
||||||
required=False,
|
|
||||||
default='https://trunk.rdoproject.org'),
|
|
||||||
)
|
|
||||||
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec,
|
|
||||||
supports_check_mode=False
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
from ansible_collections.tripleo.repos.plugins.module_utils.\
|
|
||||||
tripleo_repos.get_hash.tripleo_hash_info import TripleOHashInfo
|
|
||||||
|
|
||||||
os_version = module.params.get('os_version')
|
|
||||||
release = module.params.get('release')
|
|
||||||
component = module.params.get('component')
|
|
||||||
tag = module.params.get('tag')
|
|
||||||
dlrn_url = module.params.get('dlrn_url')
|
|
||||||
|
|
||||||
hash_result = TripleOHashInfo(os_version, release, component, tag,
|
|
||||||
config={'dlrn_url': dlrn_url})
|
|
||||||
result['commit_hash'] = hash_result.commit_hash
|
|
||||||
result['distro_hash'] = hash_result.distro_hash
|
|
||||||
result['full_hash'] = hash_result.full_hash
|
|
||||||
result['extended_hash'] = hash_result.extended_hash
|
|
||||||
result['dlrn_url'] = hash_result.dlrn_url
|
|
||||||
result['success'] = True
|
|
||||||
except Exception as exc:
|
|
||||||
result['error'] = str(exc)
|
|
||||||
result['msg'] = "Error something went wrong fetching hash info"
|
|
||||||
module.fail_json(**result)
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
run_module()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,373 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# Copyright 2021 Red Hat, 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 = r'''
|
|
||||||
---
|
|
||||||
module: yum_config
|
|
||||||
|
|
||||||
short_description: Update yum configuration files for TripleO deployments.
|
|
||||||
|
|
||||||
version_added: "1.0.0"
|
|
||||||
|
|
||||||
description:
|
|
||||||
- Update specific options for different yum configuration files like
|
|
||||||
yum repos, yum modules and yum global configuration.
|
|
||||||
|
|
||||||
options:
|
|
||||||
type:
|
|
||||||
description:
|
|
||||||
- The type of yum configuration to be changed.
|
|
||||||
required: true
|
|
||||||
type: str
|
|
||||||
choices: [repo, module, global, 'enable-compose-repos']
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- Name of the repo or module to be changed. This options is
|
|
||||||
mandatory only for 'repo' when no 'down_url' is provided. This
|
|
||||||
options is always mandatory for 'module' type.
|
|
||||||
type: str
|
|
||||||
enabled:
|
|
||||||
description:
|
|
||||||
- Change the yum repo or module to enabled or disabled.
|
|
||||||
- This options is ignored for yum global configuration.
|
|
||||||
type: bool
|
|
||||||
default: true
|
|
||||||
down_url:
|
|
||||||
description:
|
|
||||||
- URL of a downloadable repo file to be used as base to construct a
|
|
||||||
new repo file. When used together with 'name', will update only the
|
|
||||||
requested section, without a specific section 'name' will add or
|
|
||||||
update all sections available in the downloaded file.
|
|
||||||
type: str
|
|
||||||
operation:
|
|
||||||
description:
|
|
||||||
- Operation to be execute within a dnf module.
|
|
||||||
type: str
|
|
||||||
choices: [install, remove, reset]
|
|
||||||
stream:
|
|
||||||
description:
|
|
||||||
- Sets a module stream. This options is recommended when enabling a
|
|
||||||
module that doesn't have a default stream.
|
|
||||||
type: str
|
|
||||||
profile:
|
|
||||||
description:
|
|
||||||
- Sets a module profile. This options is recommended when installing
|
|
||||||
a module that doesn't have a default profile.
|
|
||||||
type: str
|
|
||||||
set_options:
|
|
||||||
description:
|
|
||||||
- Dictionary with options to be updated. All dictionary values must
|
|
||||||
be string or list of strings.
|
|
||||||
type: dict
|
|
||||||
file_path:
|
|
||||||
description:
|
|
||||||
- Absolute path of the configuration file to be changed.
|
|
||||||
type: path
|
|
||||||
dir_path:
|
|
||||||
description:
|
|
||||||
- Absolute path of the directory that contains the configuration
|
|
||||||
file to be changed.
|
|
||||||
type: path
|
|
||||||
default: /etc/yum.repos.d
|
|
||||||
environment_file:
|
|
||||||
description:
|
|
||||||
- Absolute path to an environment file to be read before updating or
|
|
||||||
creating yum config and repo files.
|
|
||||||
type: path
|
|
||||||
compose_url:
|
|
||||||
description:
|
|
||||||
- URL that contains CentOS compose repositories.
|
|
||||||
type: str
|
|
||||||
centos_release:
|
|
||||||
description:
|
|
||||||
- Target CentOS release.
|
|
||||||
type: str
|
|
||||||
choices: [centos-stream-8, centos-stream-9]
|
|
||||||
arch:
|
|
||||||
description:
|
|
||||||
- System architecture which the repos will be configure.
|
|
||||||
type: str
|
|
||||||
choices: [aarch64, ppc64le, x86_64]
|
|
||||||
default: x86_64
|
|
||||||
variants:
|
|
||||||
description:
|
|
||||||
- Repository variants that should be configured. If not provided,
|
|
||||||
all available variants will be configured.
|
|
||||||
type: list
|
|
||||||
elements: str
|
|
||||||
disable_conflicting_variants:
|
|
||||||
description:
|
|
||||||
- Disable all repos from the same directory that match variants'
|
|
||||||
name.
|
|
||||||
type: bool
|
|
||||||
default: false
|
|
||||||
disable_repos:
|
|
||||||
description:
|
|
||||||
- List with file path of repos that should be disabled after
|
|
||||||
successfully enabling all compose repos.
|
|
||||||
type: list
|
|
||||||
elements: str
|
|
||||||
|
|
||||||
author:
|
|
||||||
- Douglas Viroel (@viroel)
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = r'''
|
|
||||||
# Set yum 'appstream' repo to enabled and exclude a list of packages
|
|
||||||
- name: Enable appstream repo and exclude nodejs and mariadb packages
|
|
||||||
become: true
|
|
||||||
become_user: root
|
|
||||||
tripleo_yum_config:
|
|
||||||
type: repo
|
|
||||||
name: appstream
|
|
||||||
enabled: true
|
|
||||||
set_options:
|
|
||||||
exclude:
|
|
||||||
- nodejs*
|
|
||||||
- mariadb*
|
|
||||||
|
|
||||||
# Enable and install a yum/dnf module
|
|
||||||
- name: Enable nginx module
|
|
||||||
become: true
|
|
||||||
become_user: root
|
|
||||||
tripleo_yum_config:
|
|
||||||
type: module
|
|
||||||
name: tomcat
|
|
||||||
enabled: false
|
|
||||||
stream: "1.18"
|
|
||||||
|
|
||||||
- name: Enable nginx module
|
|
||||||
become: true
|
|
||||||
become_user: root
|
|
||||||
tripleo_yum_config:
|
|
||||||
type: module
|
|
||||||
name: nginx
|
|
||||||
operation: install
|
|
||||||
profile: common
|
|
||||||
|
|
||||||
# Set yum global configuration options
|
|
||||||
- name: Set yum global options
|
|
||||||
become: true
|
|
||||||
become_user: root
|
|
||||||
tripleo_yum_config:
|
|
||||||
type: global
|
|
||||||
file_path: /etc/dnf/dnf.conf
|
|
||||||
set_options:
|
|
||||||
skip_if_unavailable: "False"
|
|
||||||
keepcache: "0"
|
|
||||||
|
|
||||||
- name: Configure a set of repos based on latest CentOS Stream 8 compose
|
|
||||||
become: true
|
|
||||||
become_user: root
|
|
||||||
tripleo_yum_config:
|
|
||||||
compose_url: https://composes.centos.org/latest-CentOS-Stream-8/compose/
|
|
||||||
centos_release: centos-stream-8
|
|
||||||
variants:
|
|
||||||
- AppStream
|
|
||||||
- BaseOS
|
|
||||||
disable_conflicting_variants: true
|
|
||||||
disable_repos:
|
|
||||||
- /etc/yum.repos.d/CentOS-Linux-AppStream.repo
|
|
||||||
- /etc/yum.repos.d/CentOS-Linux-BaseOS.repo
|
|
||||||
'''
|
|
||||||
|
|
||||||
RETURN = r''' # '''
|
|
||||||
|
|
||||||
import os # noqa: E402
|
|
||||||
|
|
||||||
from ansible.module_utils import six # noqa: E402
|
|
||||||
from ansible.module_utils.basic import AnsibleModule # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
def run_module():
|
|
||||||
try:
|
|
||||||
import ansible_collections.tripleo.repos.plugins.module_utils. \
|
|
||||||
tripleo_repos.yum_config.constants as const
|
|
||||||
from ansible_collections.tripleo.repos.plugins.module_utils. \
|
|
||||||
tripleo_repos.yum_config import utils
|
|
||||||
except ImportError:
|
|
||||||
import tripleo_repos.yum_config.constants as const
|
|
||||||
from tripleo_repos.yum_config import utils
|
|
||||||
|
|
||||||
supported_config_types = ['repo', 'global', 'module',
|
|
||||||
'enable-compose-repos']
|
|
||||||
supported_module_operations = ['install', 'remove', 'reset']
|
|
||||||
module_args = dict(
|
|
||||||
type=dict(type='str', required=True, choices=supported_config_types),
|
|
||||||
name=dict(type='str'),
|
|
||||||
enabled=dict(type='bool', default=True),
|
|
||||||
down_url=dict(type='str'),
|
|
||||||
operation=dict(type='str', choices=supported_module_operations),
|
|
||||||
stream=dict(type='str'),
|
|
||||||
profile=dict(type='str'),
|
|
||||||
set_options=dict(type='dict', default={}),
|
|
||||||
file_path=dict(type='path'),
|
|
||||||
dir_path=dict(type='path', default=const.YUM_REPO_DIR),
|
|
||||||
environment_file=dict(type='path'),
|
|
||||||
compose_url=dict(type='str'),
|
|
||||||
centos_release=dict(type='str',
|
|
||||||
choices=const.COMPOSE_REPOS_RELEASES),
|
|
||||||
arch=dict(type='str', choices=const.COMPOSE_REPOS_SUPPORTED_ARCHS,
|
|
||||||
default='x86_64'),
|
|
||||||
variants=dict(type='list', default=[],
|
|
||||||
elements='str'),
|
|
||||||
disable_conflicting_variants=dict(type='bool', default=False),
|
|
||||||
disable_repos=dict(type='list', default=[],
|
|
||||||
elements='str'),
|
|
||||||
)
|
|
||||||
required_if_params = [
|
|
||||||
["type", "module", ["name"]],
|
|
||||||
["type", "enable-compose-repos", ["compose_url"]]
|
|
||||||
]
|
|
||||||
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec=module_args,
|
|
||||||
required_if=required_if_params,
|
|
||||||
supports_check_mode=False
|
|
||||||
)
|
|
||||||
|
|
||||||
operations_not_supp_in_py2 = ['module', 'enable-compose-repos']
|
|
||||||
if six.PY2 and module.params['type'] in operations_not_supp_in_py2:
|
|
||||||
msg = ("The configuration type '{0}' is not "
|
|
||||||
"supported with python 2.").format(module.params['type'])
|
|
||||||
module.fail_json(msg=msg)
|
|
||||||
|
|
||||||
if (module.params['type'] == 'repo' and not
|
|
||||||
module.params['name'] and not module.params['down_url']):
|
|
||||||
msg = ("When using configuration type '{0}' you must provide a repo "
|
|
||||||
"'name' or a 'down_url'.").format(module.params['type'])
|
|
||||||
module.fail_json(msg=msg)
|
|
||||||
|
|
||||||
distro, major_version, __ = utils.get_distro_info()
|
|
||||||
dnf_module_support = False
|
|
||||||
for min_distro_ver in const.DNF_MODULE_MINIMAL_DISTRO_VERSIONS:
|
|
||||||
if (distro == min_distro_ver.get('distro') and int(
|
|
||||||
major_version) >= min_distro_ver.get('min_version')):
|
|
||||||
dnf_module_support = True
|
|
||||||
break
|
|
||||||
if module.params['type'] == 'module' and not dnf_module_support:
|
|
||||||
msg = ("The configuration type 'module' is not "
|
|
||||||
"supported in this distro version "
|
|
||||||
"({0}-{1}).".format(distro, major_version))
|
|
||||||
module.fail_json(msg=msg)
|
|
||||||
|
|
||||||
# 'set_options' expects a dict that can also contains a list of values.
|
|
||||||
# List of elements will be converted to a comma-separated list
|
|
||||||
m_set_opts = module.params.get('set_options')
|
|
||||||
if m_set_opts:
|
|
||||||
for k, v in m_set_opts.items():
|
|
||||||
if isinstance(v, list):
|
|
||||||
m_set_opts[k] = ','.join([str(elem) for elem in v])
|
|
||||||
elif not isinstance(v, str):
|
|
||||||
m_set_opts[k] = str(v)
|
|
||||||
|
|
||||||
# Module execution
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
import ansible_collections.tripleo.repos.plugins.module_utils.\
|
|
||||||
tripleo_repos.yum_config.yum_config as cfg
|
|
||||||
except ImportError:
|
|
||||||
import tripleo_repos.yum_config.yum_config as cfg
|
|
||||||
|
|
||||||
if module.params['type'] == 'repo':
|
|
||||||
config_obj = cfg.TripleOYumRepoConfig(
|
|
||||||
dir_path=module.params['dir_path'],
|
|
||||||
environment_file=module.params['environment_file'])
|
|
||||||
if module.params['name']:
|
|
||||||
config_obj.add_or_update_section(
|
|
||||||
module.params['name'],
|
|
||||||
set_dict=m_set_opts,
|
|
||||||
file_path=module.params['file_path'],
|
|
||||||
enabled=module.params['enabled'],
|
|
||||||
from_url=module.params['down_url'])
|
|
||||||
else:
|
|
||||||
config_obj.add_or_update_all_sections_from_url(
|
|
||||||
module.params['down_url'],
|
|
||||||
set_dict=m_set_opts,
|
|
||||||
file_path=module.params['file_path'],
|
|
||||||
enabled=module.params['enabled'])
|
|
||||||
|
|
||||||
elif module.params['type'] == 'global':
|
|
||||||
config_obj = cfg.TripleOYumGlobalConfig(
|
|
||||||
file_path=module.params['file_path'],
|
|
||||||
environment_file=module.params['environment_file'])
|
|
||||||
config_obj.update_section('main', m_set_opts)
|
|
||||||
|
|
||||||
elif module.params['type'] == 'enable-compose-repos':
|
|
||||||
try:
|
|
||||||
import ansible_collections.tripleo.repos.plugins.module_utils.\
|
|
||||||
tripleo_repos.yum_config.compose_repos as repos
|
|
||||||
except ImportError:
|
|
||||||
import tripleo_repos.yum_config.compose_repos as repos
|
|
||||||
|
|
||||||
# 1. Create compose repo config object
|
|
||||||
repo_obj = repos.TripleOYumComposeRepoConfig(
|
|
||||||
module.params['compose_url'],
|
|
||||||
module.params['centos_release'],
|
|
||||||
dir_path=module.params['dir_path'],
|
|
||||||
arch=module.params['arch'],
|
|
||||||
environment_file=module.params['environment_file'])
|
|
||||||
# 2. enable CentOS compose repos
|
|
||||||
repo_obj.enable_compose_repos(
|
|
||||||
variants=module.params['variants'],
|
|
||||||
override_repos=module.params['disable_conflicting_variants'])
|
|
||||||
# 3. Disable all repos provided in disable_repos
|
|
||||||
for file in module.params['disable_repos']:
|
|
||||||
valid_path = None
|
|
||||||
rel_path = os.path.join(module.params['dir_path'], file)
|
|
||||||
|
|
||||||
if cfg.validated_file_path(file):
|
|
||||||
valid_path = file
|
|
||||||
elif cfg.validated_file_path(rel_path):
|
|
||||||
valid_path = rel_path
|
|
||||||
|
|
||||||
if valid_path is not None:
|
|
||||||
repo_obj.update_all_sections(valid_path, enabled=False)
|
|
||||||
|
|
||||||
elif module.params['type'] == 'module':
|
|
||||||
try:
|
|
||||||
import ansible_collections.tripleo.repos.plugins.module_utils.\
|
|
||||||
tripleo_repos.yum_config.dnf_manager as dnf_mgr
|
|
||||||
except ImportError:
|
|
||||||
import tripleo_repos.yum_config.dnf_manager as dnf_mgr
|
|
||||||
|
|
||||||
dnf_mod_mgr = dnf_mgr.DnfModuleManager()
|
|
||||||
if module.params['enabled']:
|
|
||||||
dnf_mod_mgr.enable_module(module.params['name'],
|
|
||||||
stream=module.params['stream'],
|
|
||||||
profile=module.params['profile'])
|
|
||||||
else:
|
|
||||||
dnf_mod_mgr.disable_module(module.params['name'],
|
|
||||||
stream=module.params['stream'],
|
|
||||||
profile=module.params['profile'])
|
|
||||||
if module.params['operation']:
|
|
||||||
dnf_method = getattr(dnf_mod_mgr,
|
|
||||||
module.params['operation'] + "_module")
|
|
||||||
dnf_method(module.params['name'],
|
|
||||||
stream=module.params['stream'],
|
|
||||||
profile=module.params['profile'])
|
|
||||||
|
|
||||||
except Exception as exc:
|
|
||||||
module.fail_json(msg=str(exc))
|
|
||||||
|
|
||||||
# Successful module execution
|
|
||||||
result = {
|
|
||||||
'changed': True,
|
|
||||||
'msg': "Yum {0} configuration was successfully updated.".format(
|
|
||||||
module.params['type'])
|
|
||||||
}
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
run_module()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
features:
|
|
||||||
- |
|
|
||||||
tripleo-repos now runs with the --stream argument enabled by default. Use
|
|
||||||
the --no-stream argument when using tripleo-repos without CentOS stream.
|
|
@ -1,7 +0,0 @@
|
|||||||
# The order of packages is significant, because pip processes them in the order
|
|
||||||
# of appearance. Changing the order has an impact on the overall integration
|
|
||||||
# process, which may cause wedges in the gate later.
|
|
||||||
|
|
||||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
|
||||||
requests>=2.10.0 # Apache-2.0
|
|
||||||
PyYAML>=3.12 # MIT
|
|
40
setup.cfg
40
setup.cfg
@ -1,40 +0,0 @@
|
|||||||
[metadata]
|
|
||||||
name = tripleo-repos
|
|
||||||
summary = A tool for managing tripleo repos
|
|
||||||
description-file =
|
|
||||||
README.rst
|
|
||||||
author = OpenStack
|
|
||||||
author-email = openstack-discuss@lists.openstack.org
|
|
||||||
home-page = https://docs.openstack.org/tripleo-docs/latest/
|
|
||||||
classifier =
|
|
||||||
Environment :: OpenStack
|
|
||||||
Intended Audience :: Information Technology
|
|
||||||
Intended Audience :: System Administrators
|
|
||||||
License :: OSI Approved :: Apache Software License
|
|
||||||
Operating System :: POSIX :: Linux
|
|
||||||
Programming Language :: Python
|
|
||||||
Programming Language :: Python :: Implementation :: CPython
|
|
||||||
Programming Language :: Python :: 3 :: Only
|
|
||||||
Programming Language :: Python :: 3
|
|
||||||
Programming Language :: Python :: 3.6
|
|
||||||
Programming Language :: Python :: 3.7
|
|
||||||
Programming Language :: Python :: 3.8
|
|
||||||
Programming Language :: Python :: 3.9
|
|
||||||
|
|
||||||
[files]
|
|
||||||
packages =
|
|
||||||
tripleo_repos
|
|
||||||
data_files =
|
|
||||||
etc/tripleo_get_hash/ = tripleo_repos/get_hash/config.yaml
|
|
||||||
share/ansible/plugins/modules/ = plugins/modules/*
|
|
||||||
share/ansible/plugins/module_utils/ = plugins/module_utils/*
|
|
||||||
|
|
||||||
[entry_points]
|
|
||||||
console_scripts =
|
|
||||||
tripleo-repos = tripleo_repos.main:main
|
|
||||||
tripleo-yum-config = tripleo_repos.yum_config.__main__:cli_entrypoint
|
|
||||||
tripleo-get-hash = tripleo_repos.get_hash.__main__:cli_entrypoint
|
|
||||||
|
|
||||||
[pbr]
|
|
||||||
skip_authors = True
|
|
||||||
skip_changelog = True
|
|
20
setup.py
20
setup.py
@ -1,20 +0,0 @@
|
|||||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
import setuptools
|
|
||||||
|
|
||||||
setuptools.setup(
|
|
||||||
setup_requires=['pbr>=2.0'],
|
|
||||||
pbr=True)
|
|
@ -1,15 +0,0 @@
|
|||||||
# The order of packages is significant, because pip processes them in the order
|
|
||||||
# of appearance. Changing the order has an impact on the overall integration
|
|
||||||
# process, which may cause wedges in the gate later.
|
|
||||||
|
|
||||||
hacking>=3.0,<=3.1.0 # Apache-2.0
|
|
||||||
|
|
||||||
coverage>=4.0,!=4.4 # Apache-2.0
|
|
||||||
ddt>=1.0.1 # MIT
|
|
||||||
python-subunit>=0.0.18 # Apache-2.0/BSD
|
|
||||||
oslotest>=1.10.0 # Apache-2.0
|
|
||||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
|
||||||
testscenarios>=0.4 # Apache-2.0/BSD
|
|
||||||
testtools>=1.4.0 # MIT
|
|
||||||
stestr>=2.0.0 # Apache-2.0
|
|
||||||
fixtures>=3.0.0 # Apache-2.0/BSD
|
|
@ -1 +0,0 @@
|
|||||||
ignore.txt
|
|
@ -1 +0,0 @@
|
|||||||
ignore.txt
|
|
@ -1 +0,0 @@
|
|||||||
ignore.txt
|
|
@ -1 +0,0 @@
|
|||||||
ignore.txt
|
|
@ -1,3 +0,0 @@
|
|||||||
plugins/module_utils/tripleo_repos/utils.py replace-urlopen
|
|
||||||
plugins/module_utils/tripleo_repos/utils.py pylint:ansible-bad-import
|
|
||||||
plugins/module_utils/tripleo_repos/yum_config/compose_repos.py replace-urlopen
|
|
@ -1 +0,0 @@
|
|||||||
PyYAML
|
|
@ -1,115 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
TEST_COMMIT_YAML_COMPONENT = """
|
|
||||||
commits:
|
|
||||||
- artifacts: repos/component/common/47/6a/476a52df13202a44336c8b01419f8b73b93d93eb_1f5a41f3/openstack-tacker-4.1.0-0.20210325043415.476a52d.el8.src.rpm,repos/component/common/47/6a/476a52df13202a44336c8b01419f8b73b93d93eb_1f5a41f3/python3-tacker-doc-4.1.0-0.20210325043415.476a52d.el8.noarch.rpm,repos/component/common/47/6a/476a52df13202a44336c8b01419f8b73b93d93eb_1f5a41f3/python3-tacker-tests-4.1.0-0.20210325043415.476a52d.el8.noarch.rpm,repos/component/common/47/6a/476a52df13202a44336c8b01419f8b73b93d93eb_1f5a41f3/openstack-tacker-common-4.1.0-0.20210325043415.476a52d.el8.noarch.rpm,repos/component/common/47/6a/476a52df13202a44336c8b01419f8b73b93d93eb_1f5a41f3/python3-tacker-4.1.0-0.20210325043415.476a52d.el8.noarch.rpm,repos/component/common/47/6a/476a52df13202a44336c8b01419f8b73b93d93eb_1f5a41f3/openstack-tacker-4.1.0-0.20210325043415.476a52d.el8.noarch.rpm
|
|
||||||
civotes: '[]'
|
|
||||||
commit_branch: master
|
|
||||||
commit_hash: 476a52df13202a44336c8b01419f8b73b93d93eb
|
|
||||||
component: common
|
|
||||||
distgit_dir: /home/centos8-master-uc/data/openstack-tacker_distro/
|
|
||||||
distro_hash: 1f5a41f31db8e3eb51caa9c0e201ab0583747be8
|
|
||||||
dt_build: '1616646776'
|
|
||||||
dt_commit: '1616646661.0'
|
|
||||||
dt_distro: '1616411951'
|
|
||||||
dt_extended: '0'
|
|
||||||
extended_hash: None
|
|
||||||
flags: '0'
|
|
||||||
id: '21047'
|
|
||||||
notes: OK
|
|
||||||
project_name: openstack-tacker
|
|
||||||
promotions: '[]'
|
|
||||||
repo_dir: /home/centos8-master-uc/data/openstack-tacker
|
|
||||||
status: SUCCESS
|
|
||||||
type: rpm
|
|
||||||
""" # noqa
|
|
||||||
|
|
||||||
TEST_COMMIT_YAML_CENTOS_7 = """
|
|
||||||
commits:
|
|
||||||
- artifacts: repos/b5/ef/b5ef03c9c939db551b03e9490edc6981ff582035_76ebc465/openstack-tripleo-heat-templates-12.1.1-0.20200227052810.b5ef03c.el7.src.rpm,repos/b5/ef/b5ef03c9c939db551b03e9490edc6981ff582035_76ebc465/openstack-tripleo-heat-templates-12.1.1-0.20200227052810.b5ef03c.el7.noarch.rpm
|
|
||||||
commit_branch: master
|
|
||||||
commit_hash: b5ef03c9c939db551b03e9490edc6981ff582035
|
|
||||||
component: None
|
|
||||||
distgit_dir: /home/centos-master-uc/data/openstack-tripleo-heat-templates_distro/
|
|
||||||
distro_hash: 76ebc4655502820b7677579349fd500eeca292e6
|
|
||||||
dt_build: '1582781227'
|
|
||||||
dt_commit: '1582780705.0'
|
|
||||||
dt_distro: '1580409403'
|
|
||||||
dt_extended: '0'
|
|
||||||
extended_hash: None
|
|
||||||
flags: '0'
|
|
||||||
id: '86894'
|
|
||||||
notes: OK
|
|
||||||
project_name: openstack-tripleo-heat-templates
|
|
||||||
repo_dir: /home/centos-master-uc/data/openstack-tripleo-heat-templates
|
|
||||||
status: SUCCESS
|
|
||||||
type: rpm
|
|
||||||
""" # noqa
|
|
||||||
|
|
||||||
TEST_REPO_MD5 = 'a96366960d5f9b08f78075b7560514e7'
|
|
||||||
|
|
||||||
BAD_CONFIG_FILE = """
|
|
||||||
awoo: 'foo'
|
|
||||||
"""
|
|
||||||
|
|
||||||
CONFIG_FILE = """
|
|
||||||
dlrn_url: 'https://trunk.rdoproject.org'
|
|
||||||
|
|
||||||
tripleo_releases:
|
|
||||||
- master
|
|
||||||
- zed
|
|
||||||
- wallaby
|
|
||||||
- victoria
|
|
||||||
- ussuri
|
|
||||||
- train
|
|
||||||
- osp16-2
|
|
||||||
- osp17
|
|
||||||
|
|
||||||
tripleo_ci_components:
|
|
||||||
- baremetal
|
|
||||||
- cinder
|
|
||||||
- clients
|
|
||||||
- cloudops
|
|
||||||
- common
|
|
||||||
- compute
|
|
||||||
- glance
|
|
||||||
- manila
|
|
||||||
- network
|
|
||||||
- octavia
|
|
||||||
- security
|
|
||||||
- swift
|
|
||||||
- tempest
|
|
||||||
- tripleo
|
|
||||||
- ui
|
|
||||||
- validation
|
|
||||||
|
|
||||||
rdo_named_tags:
|
|
||||||
- current
|
|
||||||
- consistent
|
|
||||||
- component-ci-testing
|
|
||||||
- tripleo-ci-testing
|
|
||||||
- current-tripleo
|
|
||||||
- current-tripleo-rdo
|
|
||||||
|
|
||||||
os_versions:
|
|
||||||
- centos7
|
|
||||||
- centos8
|
|
||||||
- centos9
|
|
||||||
- rhel8
|
|
||||||
- rhel9
|
|
||||||
|
|
||||||
"""
|
|
@ -1,231 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
from unittest import mock
|
|
||||||
from unittest.mock import mock_open, MagicMock, patch
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
import tripleo_repos.get_hash.exceptions as exc
|
|
||||||
import tripleo_repos.get_hash.__main__ as tgh
|
|
||||||
from . import fakes as test_fakes
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch(
|
|
||||||
'builtins.open', new_callable=mock_open, read_data=test_fakes.CONFIG_FILE
|
|
||||||
)
|
|
||||||
class TestGetHash(unittest.TestCase):
|
|
||||||
"""In this class we test the CLI invocations for this module.
|
|
||||||
The builtin 'open' function is mocked at a
|
|
||||||
class level so we can mock the config.yaml with the contents of the
|
|
||||||
fakes.CONFIG_FILE
|
|
||||||
"""
|
|
||||||
|
|
||||||
def test_centos_8_current_tripleo_stable(self, mock_config):
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_REPO_MD5, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
args = ['--os-version', 'centos8', '--release', 'victoria']
|
|
||||||
sys.argv[1:] = args
|
|
||||||
main_res = tgh.main()
|
|
||||||
self.assertEqual(main_res.full_hash, test_fakes.TEST_REPO_MD5)
|
|
||||||
self.assertEqual(
|
|
||||||
'https://trunk.rdoproject.org/centos8-victoria/current-tripleo/delorean.repo.md5', # noqa
|
|
||||||
main_res.dlrn_url,
|
|
||||||
)
|
|
||||||
self.assertEqual('centos8', main_res.os_version)
|
|
||||||
self.assertEqual('victoria', main_res.release)
|
|
||||||
|
|
||||||
# TODO(marios) reenable https://bugs.launchpad.net/tripleo/+bug/2002112
|
|
||||||
# def test_verbose_logging_on(self, mock_config):
|
|
||||||
# args = ['--verbose']
|
|
||||||
# debug_msgs = []
|
|
||||||
#
|
|
||||||
# mocked = MagicMock(
|
|
||||||
# return_value=(test_fakes.TEST_REPO_MD5, 200))
|
|
||||||
# with patch(
|
|
||||||
# 'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
# with self.assertLogs() as captured:
|
|
||||||
# sys.argv[1:] = args
|
|
||||||
# tgh.main()
|
|
||||||
# debug_msgs = [
|
|
||||||
# record.message
|
|
||||||
# for record in captured.records
|
|
||||||
# if record.levelname == 'DEBUG'
|
|
||||||
# ]
|
|
||||||
# self.assertIn('Logging level set to DEBUG', debug_msgs)
|
|
||||||
#
|
|
||||||
# def test_verbose_logging_off(self, mock_config):
|
|
||||||
# debug_msgs = []
|
|
||||||
#
|
|
||||||
# mocked = MagicMock(
|
|
||||||
# return_value=(test_fakes.TEST_REPO_MD5, 200))
|
|
||||||
# with patch(
|
|
||||||
# 'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
#
|
|
||||||
# args = ['--tag', 'current-tripleo', '--os-version', 'centos8']
|
|
||||||
# with self.assertLogs() as captured:
|
|
||||||
# sys.argv[1:] = args
|
|
||||||
# tgh.main()
|
|
||||||
# debug_msgs = [
|
|
||||||
# record.message
|
|
||||||
# for record in captured.records
|
|
||||||
# if record.levelname == 'DEBUG'
|
|
||||||
# ]
|
|
||||||
# self.assertEqual(debug_msgs, [])
|
|
||||||
|
|
||||||
def test_invalid_unknown_components(self, mock_config):
|
|
||||||
args = ['--component', 'nosuchcomponent']
|
|
||||||
sys.argv[1:] = args
|
|
||||||
self.assertRaises(SystemExit, lambda: tgh.main())
|
|
||||||
|
|
||||||
def test_valid_tripleo_ci_components(self, mock_config):
|
|
||||||
config_file = open("fake_config_file") # open is mocked at class level
|
|
||||||
config_yaml = yaml.safe_load(config_file.read())
|
|
||||||
config_file.close()
|
|
||||||
# interate for each of config components
|
|
||||||
for component in config_yaml['tripleo_ci_components']:
|
|
||||||
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_COMMIT_YAML_COMPONENT, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get',
|
|
||||||
mocked):
|
|
||||||
|
|
||||||
args = ['--component', "{}".format(component)]
|
|
||||||
sys.argv[1:] = args
|
|
||||||
main_res = tgh.main()
|
|
||||||
self.assertEqual(
|
|
||||||
"https://trunk.rdoproject.org/centos8-master/component"
|
|
||||||
"/{}/current-tripleo/commit.yaml".format(
|
|
||||||
component
|
|
||||||
),
|
|
||||||
main_res.dlrn_url,
|
|
||||||
)
|
|
||||||
self.assertEqual("{}".format(component), main_res.component)
|
|
||||||
|
|
||||||
def test_invalid_component_centos7(self, mock_config):
|
|
||||||
args = ['--os-version', 'centos7', '--component', 'tripleo']
|
|
||||||
sys.argv[1:] = args
|
|
||||||
self.assertRaises(exc.TripleOHashInvalidParameter, lambda: tgh.main())
|
|
||||||
|
|
||||||
def test_valid_os_version(self, mock_config):
|
|
||||||
config_file = open("fake_config_file") # open is mocked at class level
|
|
||||||
config_yaml = yaml.safe_load(config_file.read())
|
|
||||||
config_file.close()
|
|
||||||
# interate for each supported os_version
|
|
||||||
for os_v in config_yaml['os_versions']:
|
|
||||||
if '7' in os_v:
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_COMMIT_YAML_CENTOS_7, 200))
|
|
||||||
expected_url = (
|
|
||||||
"https://trunk.rdoproject.org/{}-master/"
|
|
||||||
"current-tripleo/commit.yaml".format(os_v)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_REPO_MD5, 200))
|
|
||||||
expected_url = (
|
|
||||||
"https://trunk.rdoproject.org/{}-master/"
|
|
||||||
"current-tripleo/delorean.repo.md5".format(os_v)
|
|
||||||
)
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get',
|
|
||||||
mocked):
|
|
||||||
args = ['--os-version', "{}".format(os_v)]
|
|
||||||
sys.argv[1:] = args
|
|
||||||
main_res = tgh.main()
|
|
||||||
self.assertEqual(main_res.dlrn_url, expected_url)
|
|
||||||
self.assertEqual("{}".format(os_v), main_res.os_version)
|
|
||||||
|
|
||||||
def test_invalid_os_version(self, mock_config):
|
|
||||||
args = ['--os-version', 'rhelos99', '--component', 'tripleo']
|
|
||||||
sys.argv[1:] = args
|
|
||||||
self.assertRaises(SystemExit, lambda: tgh.main())
|
|
||||||
|
|
||||||
def test_invalid_unknown_tag(self, mock_config):
|
|
||||||
args = ['--tag', 'nosuchtag']
|
|
||||||
sys.argv[1:] = args
|
|
||||||
self.assertRaises(SystemExit, lambda: tgh.main())
|
|
||||||
|
|
||||||
def test_valid_rdo_named_tags(self, mock_config):
|
|
||||||
config_file = open("fake_config_file") # open is mocked at class level
|
|
||||||
config_yaml = yaml.safe_load(config_file.read())
|
|
||||||
config_file.close()
|
|
||||||
# iterate for each of config named tags
|
|
||||||
for tag in config_yaml['rdo_named_tags']:
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_REPO_MD5, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get',
|
|
||||||
mocked):
|
|
||||||
|
|
||||||
args = ['--tag', "{}".format(tag)]
|
|
||||||
sys.argv[1:] = args
|
|
||||||
main_res = tgh.main()
|
|
||||||
self.assertEqual(
|
|
||||||
"https://trunk.rdoproject.org/centos8-master"
|
|
||||||
"/{}/delorean.repo.md5".format(
|
|
||||||
tag
|
|
||||||
),
|
|
||||||
main_res.dlrn_url,
|
|
||||||
)
|
|
||||||
self.assertEqual(tag, main_res.tag)
|
|
||||||
|
|
||||||
def test_override_dlrn_url(self, mock_config):
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_REPO_MD5, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get',
|
|
||||||
mocked):
|
|
||||||
|
|
||||||
args = ['--dlrn-url', 'https://awoo.com/awoo']
|
|
||||||
sys.argv[1:] = args
|
|
||||||
main_res = tgh.main()
|
|
||||||
self.assertEqual(
|
|
||||||
"https://awoo.com/awoo/centos8-master/current-tripleo"
|
|
||||||
"/delorean.repo.md5",
|
|
||||||
main_res.dlrn_url,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_override_os_version_release_rhel8(self, mock_config):
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_REPO_MD5, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get',
|
|
||||||
mocked):
|
|
||||||
args = [
|
|
||||||
'--dlrn-url',
|
|
||||||
'https://awoo.com/awoo',
|
|
||||||
'--os-version',
|
|
||||||
'rhel8',
|
|
||||||
'--release',
|
|
||||||
'osp16-2',
|
|
||||||
]
|
|
||||||
sys.argv[1:] = args
|
|
||||||
main_res = tgh.main()
|
|
||||||
self.assertEqual('rhel8', main_res.os_version)
|
|
||||||
self.assertEqual('osp16-2', main_res.release)
|
|
||||||
self.assertEqual(
|
|
||||||
"https://awoo.com/awoo/rhel8-osp16-2/current-tripleo"
|
|
||||||
"/delorean.repo.md5", main_res.dlrn_url,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
@ -1,227 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
import tripleo_repos.get_hash.tripleo_hash_info as thi
|
|
||||||
import tripleo_repos.get_hash.exceptions as exc
|
|
||||||
from . import fakes as test_fakes
|
|
||||||
from unittest import mock
|
|
||||||
from unittest.mock import mock_open, MagicMock, patch
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch(
|
|
||||||
'builtins.open', new_callable=mock_open, read_data=test_fakes.CONFIG_FILE
|
|
||||||
)
|
|
||||||
class TestGetHashInfo(unittest.TestCase):
|
|
||||||
"""In this class we test the functions and instantiation of the
|
|
||||||
TripleOHashInfo class. The builtin 'open' function is mocked at a
|
|
||||||
class level so we can mock the config.yaml with the contents of the
|
|
||||||
fakes.CONFIG_FILE
|
|
||||||
"""
|
|
||||||
|
|
||||||
def test_hashes_from_commit_yaml(self, mock_config):
|
|
||||||
sample_commit_yaml = test_fakes.TEST_COMMIT_YAML_COMPONENT
|
|
||||||
expected_result = (
|
|
||||||
'476a52df13202a44336c8b01419f8b73b93d93eb_1f5a41f3',
|
|
||||||
'476a52df13202a44336c8b01419f8b73b93d93eb',
|
|
||||||
'1f5a41f31db8e3eb51caa9c0e201ab0583747be8',
|
|
||||||
'None',
|
|
||||||
)
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_COMMIT_YAML_COMPONENT, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
mock_hash_info = thi.TripleOHashInfo(
|
|
||||||
'centos8', 'master', 'common', 'current-tripleo'
|
|
||||||
)
|
|
||||||
actual_result = mock_hash_info._hashes_from_commit_yaml(
|
|
||||||
sample_commit_yaml
|
|
||||||
)
|
|
||||||
self.assertEqual(expected_result, actual_result)
|
|
||||||
|
|
||||||
def test_resolve_repo_url_component_commit_yaml(self, mock_config):
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_COMMIT_YAML_COMPONENT, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
c8_component_hash_info = thi.TripleOHashInfo(
|
|
||||||
'centos8', 'master', 'common', 'current-tripleo'
|
|
||||||
)
|
|
||||||
repo_url = c8_component_hash_info._resolve_repo_url("https://woo")
|
|
||||||
self.assertEqual(
|
|
||||||
repo_url,
|
|
||||||
'https://woo/centos8-master/component/common/current-tripleo/commit.yaml', # noqa
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_resolve_repo_url_centos8_repo_md5(self, mock_config):
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_REPO_MD5, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
c8_hash_info = thi.TripleOHashInfo(
|
|
||||||
'centos8', 'master', None, 'current-tripleo'
|
|
||||||
)
|
|
||||||
repo_url = c8_hash_info._resolve_repo_url("https://woo")
|
|
||||||
self.assertEqual(
|
|
||||||
repo_url, 'https://woo/centos8-master/current-tripleo/delorean.repo.md5' # noqa
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_resolve_repo_url_centos7_commit_yaml(self, mock_config):
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_COMMIT_YAML_CENTOS_7, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
c7_hash_info = thi.TripleOHashInfo(
|
|
||||||
'centos7', 'master', None, 'current-tripleo'
|
|
||||||
)
|
|
||||||
repo_url = c7_hash_info._resolve_repo_url("https://woo")
|
|
||||||
self.assertEqual(
|
|
||||||
repo_url, 'https://woo/centos7-master/current-tripleo/commit.yaml' # noqa
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_tripleo_hash_info_centos8_md5(self, mock_config):
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_REPO_MD5, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
created_hash_info = thi.TripleOHashInfo(
|
|
||||||
'centos8', 'master', None, 'current-tripleo'
|
|
||||||
)
|
|
||||||
self.assertIsInstance(created_hash_info, thi.TripleOHashInfo)
|
|
||||||
self.assertEqual(
|
|
||||||
created_hash_info.full_hash, test_fakes.TEST_REPO_MD5
|
|
||||||
)
|
|
||||||
self.assertEqual(created_hash_info.tag, 'current-tripleo')
|
|
||||||
self.assertEqual(created_hash_info.os_version, 'centos8')
|
|
||||||
self.assertEqual(created_hash_info.release, 'master')
|
|
||||||
|
|
||||||
def test_get_tripleo_hash_info_component(self, mock_config):
|
|
||||||
expected_commit_hash = '476a52df13202a44336c8b01419f8b73b93d93eb'
|
|
||||||
expected_distro_hash = '1f5a41f31db8e3eb51caa9c0e201ab0583747be8'
|
|
||||||
expected_full_hash = '476a52df13202a44336c8b01419f8b73b93d93eb_1f5a41f3' # noqa
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_COMMIT_YAML_COMPONENT, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
created_hash_info = thi.TripleOHashInfo(
|
|
||||||
'centos8', 'victoria', 'common', 'tripleo-ci-testing'
|
|
||||||
)
|
|
||||||
self.assertIsInstance(created_hash_info, thi.TripleOHashInfo)
|
|
||||||
self.assertEqual(created_hash_info.full_hash, expected_full_hash)
|
|
||||||
self.assertEqual(
|
|
||||||
created_hash_info.distro_hash, expected_distro_hash
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
created_hash_info.commit_hash, expected_commit_hash
|
|
||||||
)
|
|
||||||
self.assertEqual(created_hash_info.component, 'common')
|
|
||||||
self.assertEqual(created_hash_info.tag, 'tripleo-ci-testing')
|
|
||||||
self.assertEqual(created_hash_info.release, 'victoria')
|
|
||||||
|
|
||||||
def test_get_tripleo_hash_info_centos7_commit_yaml(self, mock_config):
|
|
||||||
expected_commit_hash = 'b5ef03c9c939db551b03e9490edc6981ff582035'
|
|
||||||
expected_distro_hash = '76ebc4655502820b7677579349fd500eeca292e6'
|
|
||||||
expected_full_hash = 'b5ef03c9c939db551b03e9490edc6981ff582035_76ebc465' # noqa
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_COMMIT_YAML_CENTOS_7, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
created_hash_info = thi.TripleOHashInfo(
|
|
||||||
'centos7', 'master', None, 'tripleo-ci-testing'
|
|
||||||
)
|
|
||||||
self.assertIsInstance(created_hash_info, thi.TripleOHashInfo)
|
|
||||||
self.assertEqual(created_hash_info.full_hash, expected_full_hash)
|
|
||||||
self.assertEqual(
|
|
||||||
created_hash_info.distro_hash, expected_distro_hash
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
created_hash_info.commit_hash, expected_commit_hash
|
|
||||||
)
|
|
||||||
self.assertEqual(created_hash_info.os_version, 'centos7')
|
|
||||||
|
|
||||||
def test_bad_config_file(self, mock_config):
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=test_fakes.TEST_COMMIT_YAML_CENTOS_7)
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
with mock.patch(
|
|
||||||
'builtins.open',
|
|
||||||
new_callable=mock_open,
|
|
||||||
read_data=test_fakes.BAD_CONFIG_FILE,
|
|
||||||
):
|
|
||||||
self.assertRaises(
|
|
||||||
exc.TripleOHashInvalidConfig,
|
|
||||||
thi.TripleOHashInfo,
|
|
||||||
'centos7',
|
|
||||||
'master',
|
|
||||||
None,
|
|
||||||
'tripleo-ci-testing',
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_override_config_dlrn_url(self, mock_config):
|
|
||||||
expected_dlrn_url = 'https://foo.bar.baz/centos8-master/component/common/current-tripleo/commit.yaml' # noqa
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_COMMIT_YAML_COMPONENT, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
mock_hash_info = thi.TripleOHashInfo(
|
|
||||||
'centos8', 'master', 'common', 'current-tripleo',
|
|
||||||
{'dlrn_url': 'https://foo.bar.baz'}
|
|
||||||
)
|
|
||||||
self.assertEqual(expected_dlrn_url, mock_hash_info.dlrn_url)
|
|
||||||
|
|
||||||
def test_override_config_dlrn_url_empty_ignored(self, mock_config):
|
|
||||||
expected_dlrn_url = 'https://trunk.rdoproject.org/centos8-master/component/common/current-tripleo/commit.yaml' # noqa
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(test_fakes.TEST_COMMIT_YAML_COMPONENT, 200))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
mock_hash_info = thi.TripleOHashInfo(
|
|
||||||
'centos8', 'master', 'common', 'current-tripleo',
|
|
||||||
{'dlrn_url': ''}
|
|
||||||
)
|
|
||||||
self.assertEqual(expected_dlrn_url, mock_hash_info.dlrn_url)
|
|
||||||
|
|
||||||
def test_404_dlrn_http_status_code(self, mock_config):
|
|
||||||
bad_dlrn_url = 'https://server.ok/centos8-master/component/common/current-tripleo/commit.yaml' # noqa
|
|
||||||
response_text_404 = "Some kind of 404 text NOT FOUND!"
|
|
||||||
mocked = MagicMock(
|
|
||||||
return_value=(response_text_404, 404))
|
|
||||||
with patch(
|
|
||||||
'tripleo_repos.get_hash.tripleo_hash_info.http_get', mocked):
|
|
||||||
with self.assertLogs() as captured:
|
|
||||||
self.assertRaises(
|
|
||||||
exc.TripleOHashInvalidDLRNResponse,
|
|
||||||
thi.TripleOHashInfo,
|
|
||||||
'centos8',
|
|
||||||
'master',
|
|
||||||
'common',
|
|
||||||
'current-tripleo',
|
|
||||||
{'dlrn_url': 'https://server.ok'},
|
|
||||||
)
|
|
||||||
debug_msgs = [
|
|
||||||
record.message
|
|
||||||
for record in captured.records
|
|
||||||
if record.levelname == 'ERROR'
|
|
||||||
]
|
|
||||||
error_str = (
|
|
||||||
"Invalid response received from the delorean server. Queried "
|
|
||||||
"URL: {0}. Response code: {1}. Response text: {2}. Failed to "
|
|
||||||
"create TripleOHashInfo object."
|
|
||||||
).format(bad_dlrn_url, '404', response_text_404)
|
|
||||||
self.assertIn(error_str, debug_msgs)
|
|
@ -1,773 +0,0 @@
|
|||||||
# Copyright 2016 Red Hat, Inc.
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
import ddt
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
from tripleo_repos import main
|
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
|
||||||
class TestTripleORepos(testtools.TestCase):
|
|
||||||
@mock.patch('tripleo_repos.main._get_distro')
|
|
||||||
@mock.patch('sys.argv', ['tripleo-repos', 'current', '-d', 'centos8'])
|
|
||||||
@mock.patch('tripleo_repos.main._run_pkg_clean')
|
|
||||||
@mock.patch('tripleo_repos.main._validate_args')
|
|
||||||
@mock.patch('tripleo_repos.main._get_base_path')
|
|
||||||
@mock.patch('tripleo_repos.main._remove_existing')
|
|
||||||
@mock.patch('tripleo_repos.main._install_repos')
|
|
||||||
def test_main_centos8(self, mock_install, mock_remove, mock_gbp,
|
|
||||||
mock_validate, mock_clean, mock_distro):
|
|
||||||
mock_distro.return_value = ('centos', '8', 'CentOS 8')
|
|
||||||
args = main._parse_args('centos', '8')
|
|
||||||
mock_path = mock.Mock()
|
|
||||||
mock_gbp.return_value = mock_path
|
|
||||||
main.main()
|
|
||||||
mock_validate.assert_called_once_with(args, 'CentOS 8', '8')
|
|
||||||
mock_gbp.assert_called_once_with(args)
|
|
||||||
mock_remove.assert_called_once_with(args)
|
|
||||||
mock_clean.assert_called_once_with('centos8')
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_distro')
|
|
||||||
@mock.patch('sys.argv', ['tripleo-repos', 'current', '-d', 'fedora'])
|
|
||||||
@mock.patch('tripleo_repos.main._run_pkg_clean')
|
|
||||||
@mock.patch('tripleo_repos.main._validate_args')
|
|
||||||
@mock.patch('tripleo_repos.main._get_base_path')
|
|
||||||
@mock.patch('tripleo_repos.main._install_priorities')
|
|
||||||
@mock.patch('tripleo_repos.main._remove_existing')
|
|
||||||
@mock.patch('tripleo_repos.main._install_repos')
|
|
||||||
def test_main_fedora(self, mock_install, mock_remove, mock_ip, mock_gbp,
|
|
||||||
mock_validate, mock_clean, mock_distro):
|
|
||||||
mock_distro.return_value = ('centos', '8', 'CentOS 8')
|
|
||||||
args = main._parse_args('centos', '8')
|
|
||||||
mock_path = mock.Mock()
|
|
||||||
mock_gbp.return_value = mock_path
|
|
||||||
main.main()
|
|
||||||
mock_validate.assert_called_once_with(args, 'CentOS 8', '8')
|
|
||||||
mock_gbp.assert_called_once_with(args)
|
|
||||||
assert not mock_ip.called, '_install_priorities should no tbe called'
|
|
||||||
mock_remove.assert_called_once_with(args)
|
|
||||||
mock_install.assert_called_once_with(args, mock_path)
|
|
||||||
mock_clean.assert_called_once_with('fedora')
|
|
||||||
|
|
||||||
@mock.patch('requests.get')
|
|
||||||
def test_get_repo(self, mock_get):
|
|
||||||
mock_response = mock.Mock()
|
|
||||||
mock_response.status_code = 200
|
|
||||||
mock_response.text = '88MPH'
|
|
||||||
mock_get.return_value = mock_response
|
|
||||||
fake_addr = 'http://lone/pine/mall'
|
|
||||||
args = mock.Mock()
|
|
||||||
args.distro = 'centos'
|
|
||||||
content = main._get_repo(fake_addr, args)
|
|
||||||
self.assertEqual('88MPH', content)
|
|
||||||
mock_get.assert_called_once_with(fake_addr)
|
|
||||||
|
|
||||||
@mock.patch('requests.get')
|
|
||||||
def test_get_repo_404(self, mock_get):
|
|
||||||
mock_response = mock.Mock()
|
|
||||||
mock_response.status_code = 404
|
|
||||||
mock_get.return_value = mock_response
|
|
||||||
fake_addr = 'http://twin/pines/mall'
|
|
||||||
main._get_repo(fake_addr, mock.Mock())
|
|
||||||
mock_get.assert_called_once_with(fake_addr)
|
|
||||||
mock_response.raise_for_status.assert_called_once_with()
|
|
||||||
|
|
||||||
@mock.patch('os.listdir')
|
|
||||||
@mock.patch('os.remove')
|
|
||||||
@mock.patch('os.path.exists')
|
|
||||||
def test_remove_existing(self, mock_exists, mock_remove, mock_listdir):
|
|
||||||
fake_list = ['foo.repo', 'delorean.repo',
|
|
||||||
'delorean-current-tripleo.repo',
|
|
||||||
'tripleo-centos-opstools.repo',
|
|
||||||
'tripleo-centos-highavailability.repo']
|
|
||||||
mock_exists.return_value = [True, False, True, False, True,
|
|
||||||
False, False, True]
|
|
||||||
mock_listdir.return_value = fake_list
|
|
||||||
mock_args = mock.Mock()
|
|
||||||
mock_args.output_path = '/etc/yum.repos.d'
|
|
||||||
main._remove_existing(mock_args)
|
|
||||||
self.assertIn(mock.call('/etc/yum.repos.d/delorean.repo'),
|
|
||||||
mock_remove.mock_calls)
|
|
||||||
self.assertIn(mock.call('/etc/yum.repos.d/'
|
|
||||||
'delorean-current-tripleo.repo'),
|
|
||||||
mock_remove.mock_calls)
|
|
||||||
self.assertIn(
|
|
||||||
mock.call('/etc/yum.repos.d/tripleo-centos-opstools.repo'),
|
|
||||||
mock_remove.mock_calls)
|
|
||||||
self.assertIn(
|
|
||||||
mock.call('/etc/distro.repos.d/'
|
|
||||||
'tripleo-centos-highavailability.repo'),
|
|
||||||
mock_remove.mock_calls)
|
|
||||||
self.assertNotIn(mock.call('/etc/yum.repos.d/foo.repo'),
|
|
||||||
mock_remove.mock_calls)
|
|
||||||
|
|
||||||
# There is no $DISTRO single path anymore, every path has branch
|
|
||||||
# specification, even master
|
|
||||||
def test_get_base_path(self):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.branch = 'master'
|
|
||||||
args.distro = 'centos7'
|
|
||||||
args.rdo_mirror = 'http://trunk.rdoproject.org'
|
|
||||||
path = main._get_base_path(args)
|
|
||||||
self.assertEqual('http://trunk.rdoproject.org/centos7-master/', path)
|
|
||||||
|
|
||||||
def test_get_base_path_fedora(self):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.branch = 'master'
|
|
||||||
args.distro = 'fedora'
|
|
||||||
args.rdo_mirror = 'http://trunk.rdoproject.org'
|
|
||||||
path = main._get_base_path(args)
|
|
||||||
self.assertEqual('http://trunk.rdoproject.org/fedora-master/', path)
|
|
||||||
|
|
||||||
@mock.patch('subprocess.check_call')
|
|
||||||
def test_install_priorities(self, mock_check_call):
|
|
||||||
main._install_priorities()
|
|
||||||
mock_check_call.assert_called_once_with(['yum', 'install', '-y',
|
|
||||||
'yum-plugin-priorities'])
|
|
||||||
|
|
||||||
@mock.patch('subprocess.check_call')
|
|
||||||
def test_install_priorities_fails(self, mock_check_call):
|
|
||||||
mock_check_call.side_effect = subprocess.CalledProcessError(88, '88')
|
|
||||||
self.assertRaises(subprocess.CalledProcessError,
|
|
||||||
main._install_priorities)
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_current(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['current']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'fake'
|
|
||||||
mock_get.return_value = '[delorean]\nMr. Fusion'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
self.assertEqual([mock.call('roads/current/delorean.repo', args),
|
|
||||||
mock.call('roads/delorean-deps.repo', args),
|
|
||||||
],
|
|
||||||
mock_get.mock_calls)
|
|
||||||
self.assertEqual([mock.call('[delorean]\nMr. Fusion', 'test',
|
|
||||||
name='delorean'),
|
|
||||||
mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
],
|
|
||||||
mock_write.mock_calls)
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_current_mitaka(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['current']
|
|
||||||
args.branch = 'mitaka'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'fake'
|
|
||||||
mock_get.return_value = '[delorean]\nMr. Fusion'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
self.assertEqual([mock.call('roads/current/delorean.repo', args),
|
|
||||||
mock.call('roads/delorean-deps.repo', args),
|
|
||||||
],
|
|
||||||
mock_get.mock_calls)
|
|
||||||
self.assertEqual([mock.call('[delorean]\nMr. Fusion', 'test',
|
|
||||||
name='delorean'),
|
|
||||||
mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
],
|
|
||||||
mock_write.mock_calls)
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_deps(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['deps']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'fake'
|
|
||||||
mock_get.return_value = '[delorean-deps]\nMr. Fusion'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
mock_get.assert_called_once_with('roads/delorean-deps.repo', args)
|
|
||||||
mock_write.assert_called_once_with('[delorean-deps]\nMr. Fusion',
|
|
||||||
'test')
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_current_tripleo(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['current-tripleo']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'fake'
|
|
||||||
mock_get.return_value = '[delorean]\nMr. Fusion'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
self.assertEqual([mock.call('roads/current-tripleo/delorean.repo',
|
|
||||||
args),
|
|
||||||
mock.call('roads/delorean-deps.repo', args),
|
|
||||||
],
|
|
||||||
mock_get.mock_calls)
|
|
||||||
self.assertEqual([mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
],
|
|
||||||
mock_write.mock_calls)
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_current_tripleo_dev(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['current-tripleo-dev']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'fake'
|
|
||||||
mock_get.return_value = '[delorean]\nMr. Fusion'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
mock_get.assert_any_call('roads/delorean-deps.repo', args)
|
|
||||||
# This is the wrong name for the deps repo, but I'm not bothered
|
|
||||||
# enough by that to mess with mocking multiple different calls.
|
|
||||||
mock_write.assert_any_call('[delorean]\nMr. Fusion', 'test')
|
|
||||||
mock_get.assert_any_call('roads/current-tripleo/delorean.repo', args)
|
|
||||||
mock_write.assert_any_call('[delorean-current-tripleo]\n'
|
|
||||||
'priority=20\nMr. Fusion', 'test',
|
|
||||||
name='delorean-current-tripleo')
|
|
||||||
mock_get.assert_called_with('roads/current/delorean.repo', args)
|
|
||||||
mock_write.assert_called_with('[delorean]\npriority=10\n%s\n'
|
|
||||||
'Mr. Fusion' %
|
|
||||||
main.INCLUDE_PKGS, 'test',
|
|
||||||
name='delorean')
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_tripleo_ci_testing(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['tripleo-ci-testing']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'fake'
|
|
||||||
mock_get.return_value = '[delorean]\nMr. Fusion'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
self.assertEqual([mock.call('roads/tripleo-ci-testing/delorean.repo',
|
|
||||||
args),
|
|
||||||
mock.call('roads/delorean-deps.repo', args),
|
|
||||||
],
|
|
||||||
mock_get.mock_calls)
|
|
||||||
self.assertEqual([mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
],
|
|
||||||
mock_write.mock_calls)
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_current_tripleo_rdo(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['current-tripleo-rdo']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'fake'
|
|
||||||
mock_get.return_value = '[delorean]\nMr. Fusion'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
self.assertEqual([mock.call('roads/current-tripleo-rdo/delorean.repo',
|
|
||||||
args),
|
|
||||||
mock.call('roads/delorean-deps.repo', args),
|
|
||||||
],
|
|
||||||
mock_get.mock_calls)
|
|
||||||
self.assertEqual([mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
],
|
|
||||||
mock_write.mock_calls)
|
|
||||||
|
|
||||||
@ddt.data('liberty', 'mitaka', 'newton', 'ocata', 'pike', 'queens',
|
|
||||||
'rocky', 'stein', 'master')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._create_ceph')
|
|
||||||
def test_install_repos_ceph(self,
|
|
||||||
branch,
|
|
||||||
mock_create_ceph,
|
|
||||||
mock_write_repo):
|
|
||||||
ceph_release = {
|
|
||||||
'liberty': 'hammer',
|
|
||||||
'mitaka': 'hammer',
|
|
||||||
'newton': 'jewel',
|
|
||||||
'ocata': 'jewel',
|
|
||||||
'pike': 'jewel',
|
|
||||||
'queens': 'luminous',
|
|
||||||
'rocky': 'luminous',
|
|
||||||
'stein': 'nautilus',
|
|
||||||
'train': 'nautilus',
|
|
||||||
'ussuri': 'nautilus',
|
|
||||||
'victoria': 'nautilus',
|
|
||||||
'master': 'pacific',
|
|
||||||
}
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['ceph']
|
|
||||||
args.branch = branch
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'fake'
|
|
||||||
mock_repo = '[centos-ceph-luminous]\nMr. Fusion'
|
|
||||||
mock_create_ceph.return_value = mock_repo
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
mock_create_ceph.assert_called_once_with(args, ceph_release[branch])
|
|
||||||
mock_write_repo.assert_called_once_with(mock_repo, 'test')
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_opstools(self, mock_write):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['opstools']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.mirror = 'http://foo'
|
|
||||||
args.distro = 'fake'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
expected_repo = ('\n[tripleo-centos-opstools]\n'
|
|
||||||
'name=tripleo-centos-opstools\n'
|
|
||||||
'baseurl=http://foo/centos/7/opstools/$basearch/\n'
|
|
||||||
'gpgcheck=0\n'
|
|
||||||
'enabled=1\n')
|
|
||||||
mock_write.assert_called_once_with(expected_repo,
|
|
||||||
'test')
|
|
||||||
|
|
||||||
@mock.patch('requests.get')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_deps_mirror(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['deps']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.old_mirror = 'http://mirror.centos.org'
|
|
||||||
args.mirror = 'http://foo'
|
|
||||||
args.distro = 'centos7'
|
|
||||||
args.rdo_mirror = 'http://bar'
|
|
||||||
# Abbreviated repos to verify the regex works
|
|
||||||
fake_repo = '''
|
|
||||||
[delorean-current-tripleo]
|
|
||||||
name=test repo
|
|
||||||
baseurl=https://trunk.rdoproject.org/centos7/some-repo-hash
|
|
||||||
enabled=1
|
|
||||||
|
|
||||||
[rdo-qemu-ev]
|
|
||||||
name=test qemu-ev
|
|
||||||
baseurl=http://mirror.centos.org/centos/7/virt/$basearch/kvm-common
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
expected_repo = '''
|
|
||||||
[delorean-current-tripleo]
|
|
||||||
name=test repo
|
|
||||||
baseurl=http://bar/centos7/some-repo-hash
|
|
||||||
enabled=1
|
|
||||||
|
|
||||||
[rdo-qemu-ev]
|
|
||||||
name=test qemu-ev
|
|
||||||
baseurl=http://foo/centos/7/virt/$basearch/kvm-common
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
mock_get.return_value = mock.Mock(text=fake_repo,
|
|
||||||
status_code=200)
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
mock_write.assert_called_once_with(expected_repo,
|
|
||||||
'test')
|
|
||||||
|
|
||||||
def test_install_repos_invalid(self):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['roads?']
|
|
||||||
self.assertRaises(main.InvalidArguments, main._install_repos, args,
|
|
||||||
'roads/')
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_centos8(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['current']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'centos8'
|
|
||||||
args.stream = False
|
|
||||||
args.mirror = 'mirror'
|
|
||||||
mock_get.return_value = '[delorean]\nMr. Fusion'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
self.assertEqual([mock.call('roads/current/delorean.repo', args),
|
|
||||||
mock.call('roads/delorean-deps.repo', args),
|
|
||||||
],
|
|
||||||
mock_get.mock_calls)
|
|
||||||
self.assertEqual([mock.call('[delorean]\nMr. Fusion', 'test',
|
|
||||||
name='delorean'),
|
|
||||||
mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
mock.call((
|
|
||||||
'\n[tripleo-centos-highavailability]\n'
|
|
||||||
'name=tripleo-centos-highavailability\n'
|
|
||||||
'baseurl=mirror/centos/8/HighAvailability'
|
|
||||||
'/$basearch/os/\ngpgcheck=0\nenabled=1\n'),
|
|
||||||
'test'),
|
|
||||||
mock.call((
|
|
||||||
'\n[tripleo-centos-powertools]\n'
|
|
||||||
'name=tripleo-centos-powertools\n'
|
|
||||||
'baseurl=mirror/centos/8/PowerTools'
|
|
||||||
'/$basearch/os/\ngpgcheck=0\nenabled=1\n'),
|
|
||||||
'test')
|
|
||||||
],
|
|
||||||
mock_write.mock_calls)
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_centos8_stream(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['current']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'centos8'
|
|
||||||
args.stream = True
|
|
||||||
args.no_stream = False
|
|
||||||
args.mirror = 'mirror'
|
|
||||||
mock_get.return_value = '[delorean]\nMr. Fusion'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
self.assertEqual([mock.call('roads/current/delorean.repo', args),
|
|
||||||
mock.call('roads/delorean-deps.repo', args),
|
|
||||||
],
|
|
||||||
mock_get.mock_calls)
|
|
||||||
self.assertEqual([mock.call('[delorean]\nMr. Fusion', 'test',
|
|
||||||
name='delorean'),
|
|
||||||
mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
mock.call((
|
|
||||||
'\n[tripleo-centos-highavailability]\n'
|
|
||||||
'name=tripleo-centos-highavailability\n'
|
|
||||||
'baseurl=mirror/centos/8-stream/HighAvailability'
|
|
||||||
'/$basearch/os/\ngpgcheck=0\nenabled=1\n'),
|
|
||||||
'test'),
|
|
||||||
mock.call((
|
|
||||||
'\n[tripleo-centos-powertools]\n'
|
|
||||||
'name=tripleo-centos-powertools\n'
|
|
||||||
'baseurl=mirror/centos/8-stream/PowerTools'
|
|
||||||
'/$basearch/os/\ngpgcheck=0\nenabled=1\n'),
|
|
||||||
'test')
|
|
||||||
],
|
|
||||||
mock_write.mock_calls)
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_centos9_stream(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['current']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'centos9'
|
|
||||||
args.stream = True
|
|
||||||
args.no_stream = False
|
|
||||||
args.mirror = 'mirror'
|
|
||||||
mock_get.return_value = '[delorean]\nMr. Fusion'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
self.assertEqual([mock.call('roads/current/delorean.repo', args),
|
|
||||||
mock.call('roads/delorean-deps.repo', args),
|
|
||||||
],
|
|
||||||
mock_get.mock_calls)
|
|
||||||
self.assertEqual([mock.call('[delorean]\nMr. Fusion', 'test',
|
|
||||||
name='delorean'),
|
|
||||||
mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
mock.call((
|
|
||||||
'\n[tripleo-centos-highavailability]\n'
|
|
||||||
'name=tripleo-centos-highavailability\n'
|
|
||||||
'baseurl=mirror/9-stream/HighAvailability'
|
|
||||||
'/$basearch/os/\ngpgcheck=0\nenabled=1\n'),
|
|
||||||
'test'),
|
|
||||||
mock.call((
|
|
||||||
'\n[tripleo-centos-powertools]\n'
|
|
||||||
'name=tripleo-centos-powertools\n'
|
|
||||||
'baseurl=mirror/9-stream/CRB'
|
|
||||||
'/$basearch/os/\ngpgcheck=0\nenabled=1\n'),
|
|
||||||
'test'),
|
|
||||||
mock.call((
|
|
||||||
'\n[tripleo-centos-appstream]\n'
|
|
||||||
'name=tripleo-centos-appstream\n'
|
|
||||||
'baseurl=mirror/9-stream/AppStream'
|
|
||||||
'/$basearch/os/\ngpgcheck=0\nenabled=1\n\n'),
|
|
||||||
'test'),
|
|
||||||
mock.call((
|
|
||||||
'\n[tripleo-centos-baseos]\n'
|
|
||||||
'name=tripleo-centos-baseos\n'
|
|
||||||
'baseurl=mirror/9-stream/BaseOS'
|
|
||||||
'/$basearch/os/\ngpgcheck=0\nenabled=1\n'),
|
|
||||||
'test')
|
|
||||||
],
|
|
||||||
mock_write.mock_calls)
|
|
||||||
|
|
||||||
@mock.patch('tripleo_repos.main._get_repo')
|
|
||||||
@mock.patch('tripleo_repos.main._write_repo')
|
|
||||||
def test_install_repos_centos8_no_stream(self, mock_write, mock_get):
|
|
||||||
args = mock.Mock()
|
|
||||||
args.repos = ['current']
|
|
||||||
args.branch = 'master'
|
|
||||||
args.output_path = 'test'
|
|
||||||
args.distro = 'centos8'
|
|
||||||
args.stream = False
|
|
||||||
args.no_stream = True
|
|
||||||
args.mirror = 'mirror'
|
|
||||||
mock_get.return_value = '[delorean]\nMr. Fusion'
|
|
||||||
main._install_repos(args, 'roads/')
|
|
||||||
self.assertEqual([mock.call('roads/current/delorean.repo', args),
|
|
||||||
mock.call('roads/delorean-deps.repo', args),
|
|
||||||
],
|
|
||||||
mock_get.mock_calls)
|
|
||||||
self.assertEqual([mock.call('[delorean]\nMr. Fusion', 'test',
|
|
||||||
name='delorean'),
|
|
||||||
mock.call('[delorean]\nMr. Fusion', 'test'),
|
|
||||||
mock.call((
|
|
||||||
'\n[tripleo-centos-highavailability]\n'
|
|
||||||
'name=tripleo-centos-highavailability\n'
|
|
||||||
'baseurl=mirror/centos/8/HighAvailability'
|
|
||||||
'/$basearch/os/\ngpgcheck=0\nenabled=1\n'),
|
|
||||||
'test'),
|
|
||||||
mock.call((
|
|
||||||
'\n[tripleo-centos-powertools]\n'
|
|
||||||
'name=tripleo-centos-powertools\n'
|
|
||||||
'baseurl=mirror/centos/8/PowerTools'
|
|
||||||
'/$basearch/os/\ngpgcheck=0\nenabled=1\n'),
|
|
||||||
'test')
|
|
||||||
],
|
|
||||||
mock_write.mock_calls)
|
|
||||||
|
|
||||||
def test_write_repo(self):
|
|
||||||
m = mock.mock_open()
|
|
||||||
with mock.patch('tripleo_repos.main.open', m, create=True):
|
|
||||||
main._write_repo('#Doc\n[delorean]\nThis=Heavy', 'test')
|
|
||||||
m.assert_called_once_with('test/delorean.repo', 'w')
|
|
||||||
m().write.assert_called_once_with('#Doc\n[delorean]\nThis=Heavy')
|
|
||||||
|
|
||||||
def test_write_repo_invalid(self):
|
|
||||||
self.assertRaises(main.NoRepoTitle, main._write_repo, 'Great Scot!',
|
|
||||||
'test')
|
|
||||||
|
|
||||||
def test_parse_args(self):
|
|
||||||
with mock.patch.object(sys, 'argv', ['', 'current', 'deps', '-d',
|
|
||||||
'centos7', '-b', 'liberty',
|
|
||||||
'-o', 'test']):
|
|
||||||
args = main._parse_args('centos', '8')
|
|
||||||
self.assertEqual(['current', 'deps'], args.repos)
|
|
||||||
self.assertEqual('centos7', args.distro)
|
|
||||||
self.assertEqual('liberty', args.branch)
|
|
||||||
self.assertEqual('test', args.output_path)
|
|
||||||
|
|
||||||
def test_parse_args_long(self):
|
|
||||||
with mock.patch.object(sys, 'argv', ['', 'current', '--distro',
|
|
||||||
'centos7', '--branch',
|
|
||||||
'mitaka', '--output-path',
|
|
||||||
'test']):
|
|
||||||
args = main._parse_args('centos', '8')
|
|
||||||
self.assertEqual(['current'], args.repos)
|
|
||||||
self.assertEqual('centos7', args.distro)
|
|
||||||
self.assertEqual('mitaka', args.branch)
|
|
||||||
self.assertEqual('test', args.output_path)
|
|
||||||
|
|
||||||
def test_change_priority(self):
|
|
||||||
result = main._change_priority('[delorean]\npriority=1', 10)
|
|
||||||
self.assertEqual('[delorean]\npriority=10', result)
|
|
||||||
|
|
||||||
def test_change_priority_none(self):
|
|
||||||
result = main._change_priority('[delorean]', 10)
|
|
||||||
self.assertEqual('[delorean]\npriority=10', result)
|
|
||||||
|
|
||||||
def test_change_priority_none_muilti(self):
|
|
||||||
data = "[repo1]\n[repo2]\n"
|
|
||||||
expected = "[repo1]\n{0}\n[repo2]\n{0}\n".format("priority=10")
|
|
||||||
result = main._change_priority(data, 10)
|
|
||||||
self.assertEqual(expected, result)
|
|
||||||
|
|
||||||
def test_add_includepkgs(self):
|
|
||||||
data = "[repo1]\n[repo2]"
|
|
||||||
expected = "[repo1]\n{0}\n[repo2]\n{0}".format(main.INCLUDE_PKGS)
|
|
||||||
result = main._add_includepkgs(data)
|
|
||||||
self.assertEqual(expected, result)
|
|
||||||
|
|
||||||
def test_create_ceph(self):
|
|
||||||
mock_args = mock.Mock(mirror='http://foo')
|
|
||||||
result = main._create_ceph(mock_args, 'jewel')
|
|
||||||
expected_repo = '''
|
|
||||||
[tripleo-centos-ceph-jewel]
|
|
||||||
name=tripleo-centos-ceph-jewel
|
|
||||||
baseurl=http://foo/SIGs/9-stream/storage/$basearch/ceph-jewel/
|
|
||||||
gpgcheck=0
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
self.assertEqual(expected_repo, result)
|
|
||||||
|
|
||||||
mock_args = mock.Mock(mirror='http://foo', distro='centos8')
|
|
||||||
result = main._create_ceph(mock_args, 'jewel')
|
|
||||||
expected_repo = '''
|
|
||||||
[tripleo-centos-ceph-jewel]
|
|
||||||
name=tripleo-centos-ceph-jewel
|
|
||||||
baseurl=http://foo/centos/8-stream/storage/$basearch/ceph-jewel/
|
|
||||||
gpgcheck=0
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
self.assertEqual(expected_repo, result)
|
|
||||||
|
|
||||||
def test_inject_mirrors_centos(self):
|
|
||||||
start_repo = '''
|
|
||||||
[delorean]
|
|
||||||
name=delorean
|
|
||||||
baseurl=https://trunk.rdoproject.org/centos7/some-repo-hash
|
|
||||||
enabled=1
|
|
||||||
[centos]
|
|
||||||
name=centos
|
|
||||||
baseurl=http://mirror.centos.org/centos/7/virt/$basearch/kvm-common
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
expected = '''
|
|
||||||
[delorean]
|
|
||||||
name=delorean
|
|
||||||
baseurl=http://bar/centos7/some-repo-hash
|
|
||||||
enabled=1
|
|
||||||
[centos]
|
|
||||||
name=centos
|
|
||||||
baseurl=http://foo/centos/7/virt/$basearch/kvm-common
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
mock_args = mock.Mock(mirror='http://foo',
|
|
||||||
rdo_mirror='http://bar',
|
|
||||||
distro='centos',
|
|
||||||
old_mirror='http://mirror.centos.org')
|
|
||||||
result = main._inject_mirrors(start_repo, mock_args)
|
|
||||||
self.assertEqual(expected, result)
|
|
||||||
|
|
||||||
def test_inject_mirrors_rhel(self):
|
|
||||||
start_repo = '''
|
|
||||||
[delorean]
|
|
||||||
name=delorean
|
|
||||||
baseurl=https://trunk.rdoproject.org/centos7/some-repo-hash
|
|
||||||
enabled=1
|
|
||||||
[rhel]
|
|
||||||
name=rhel
|
|
||||||
baseurl=https://some/stuff
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
expected = '''
|
|
||||||
[delorean]
|
|
||||||
name=delorean
|
|
||||||
baseurl=http://bar/centos7/some-repo-hash
|
|
||||||
enabled=1
|
|
||||||
[rhel]
|
|
||||||
name=rhel
|
|
||||||
baseurl=http://foo/stuff
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
mock_args = mock.Mock(mirror='http://foo',
|
|
||||||
rdo_mirror='http://bar',
|
|
||||||
distro='rhel',
|
|
||||||
old_mirror='https://some')
|
|
||||||
result = main._inject_mirrors(start_repo, mock_args)
|
|
||||||
self.assertEqual(expected, result)
|
|
||||||
|
|
||||||
def test_inject_mirrors_no_match(self):
|
|
||||||
start_repo = '''
|
|
||||||
[delorean]
|
|
||||||
name=delorean
|
|
||||||
baseurl=https://some.mirror.com/centos7/some-repo-hash
|
|
||||||
enabled=1
|
|
||||||
'''
|
|
||||||
mock_args = mock.Mock(rdo_mirror='http://some.mirror.com',
|
|
||||||
distro='centos')
|
|
||||||
# If a user has a mirror whose repos already point at itself then
|
|
||||||
# the _inject_mirrors call should be a noop.
|
|
||||||
self.assertEqual(start_repo, main._inject_mirrors(start_repo,
|
|
||||||
mock_args))
|
|
||||||
|
|
||||||
@mock.patch('subprocess.check_call')
|
|
||||||
def test_run_pkg_clean(self, mock_check_call):
|
|
||||||
main._run_pkg_clean('centos7')
|
|
||||||
mock_check_call.assert_called_once_with(['yum', 'clean', 'metadata'])
|
|
||||||
|
|
||||||
@mock.patch('subprocess.check_call')
|
|
||||||
def test_run_pkg_clean_fedora(self, mock_check_call):
|
|
||||||
main._run_pkg_clean('fedora')
|
|
||||||
mock_check_call.assert_called_once_with(['dnf', 'clean', 'metadata'])
|
|
||||||
|
|
||||||
@mock.patch('subprocess.check_call')
|
|
||||||
def test_run_pkg_clean_fails(self, mock_check_call):
|
|
||||||
mock_check_call.side_effect = subprocess.CalledProcessError(88, '88')
|
|
||||||
self.assertRaises(subprocess.CalledProcessError,
|
|
||||||
main._run_pkg_clean, ['centos7'])
|
|
||||||
|
|
||||||
|
|
||||||
class TestValidate(testtools.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
super(TestValidate, self).setUp()
|
|
||||||
self.args = mock.Mock()
|
|
||||||
self.args.repos = ['current']
|
|
||||||
self.args.branch = 'master'
|
|
||||||
self.args.distro = 'centos7'
|
|
||||||
self.distro_major_version_id = "7"
|
|
||||||
self.args.stream = False
|
|
||||||
self.args.no_stream = False
|
|
||||||
|
|
||||||
def test_good(self):
|
|
||||||
main._validate_args(self.args, '', '')
|
|
||||||
|
|
||||||
def test_current_and_tripleo_dev(self):
|
|
||||||
self.args.repos = ['current', 'current-tripleo-dev']
|
|
||||||
self.assertRaises(main.InvalidArguments, main._validate_args,
|
|
||||||
self.args, '', '')
|
|
||||||
|
|
||||||
def test_tripleo_ci_testing_and_current_tripleo(self):
|
|
||||||
self.args.repos = ['current-tripleo', 'tripleo-ci-testing']
|
|
||||||
self.assertRaises(main.InvalidArguments, main._validate_args,
|
|
||||||
self.args, '', '')
|
|
||||||
|
|
||||||
def test_tripleo_ci_testing_and_ceph_opstools_allowed(self):
|
|
||||||
self.args.repos = ['ceph', 'opstools', 'tripleo-ci-testing']
|
|
||||||
main._validate_args(self.args, '', '')
|
|
||||||
|
|
||||||
def test_tripleo_ci_testing_and_deps_allowed(self):
|
|
||||||
self.args.repos = ['deps', 'tripleo-ci-testing']
|
|
||||||
main._validate_args(self.args, '', '')
|
|
||||||
|
|
||||||
def test_ceph_and_tripleo_dev(self):
|
|
||||||
self.args.repos = ['current-tripleo-dev', 'ceph']
|
|
||||||
self.args.output_path = main.DEFAULT_OUTPUT_PATH
|
|
||||||
main._validate_args(self.args, '', '')
|
|
||||||
|
|
||||||
def test_deps_and_tripleo_dev(self):
|
|
||||||
self.args.repos = ['deps', 'current-tripleo-dev']
|
|
||||||
self.assertRaises(main.InvalidArguments, main._validate_args,
|
|
||||||
self.args, '', '')
|
|
||||||
|
|
||||||
def test_current_and_tripleo(self):
|
|
||||||
self.args.repos = ['current', 'current-tripleo']
|
|
||||||
self.assertRaises(main.InvalidArguments, main._validate_args,
|
|
||||||
self.args, '', '')
|
|
||||||
|
|
||||||
def test_deps_and_tripleo_allowed(self):
|
|
||||||
self.args.repos = ['deps', 'current-tripleo']
|
|
||||||
main._validate_args(self.args, '', '')
|
|
||||||
|
|
||||||
def test_invalid_distro(self):
|
|
||||||
self.args.distro = 'Jigawatts 1.21'
|
|
||||||
self.assertRaises(main.InvalidArguments, main._validate_args,
|
|
||||||
self.args, '', '')
|
|
||||||
|
|
||||||
def test_invalid_stream(self):
|
|
||||||
self.args.output_path = main.DEFAULT_OUTPUT_PATH
|
|
||||||
self.args.stream = True
|
|
||||||
self.assertRaises(main.InvalidArguments, main._validate_args,
|
|
||||||
self.args, 'CentOS 8', '8')
|
|
||||||
|
|
||||||
def test_invalid_no_stream(self):
|
|
||||||
self.args.output_path = main.DEFAULT_OUTPUT_PATH
|
|
||||||
self.args.stream = False
|
|
||||||
self.args.no_stream = True
|
|
||||||
self.assertRaises(main.InvalidArguments, main._validate_args,
|
|
||||||
self.args, 'CentOS 8 Stream', '8')
|
|
||||||
|
|
||||||
def test_validate_distro_repos(self):
|
|
||||||
self.assertTrue(main._validate_distro_repos(self.args))
|
|
||||||
|
|
||||||
def test_validate_distro_repos_fedora_tripleo_dev(self):
|
|
||||||
self.args.distro = 'fedora'
|
|
||||||
self.args.repos = ['current-tripleo-dev']
|
|
||||||
self.assertRaises(main.InvalidArguments, main._validate_distro_repos,
|
|
||||||
self.args)
|
|
@ -1,113 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
FAKE_FILE_PATH = '/path/to/file'
|
|
||||||
FAKE_DIR_PATH = '/path/to/dir'
|
|
||||||
FAKE_SUPP_OPTIONS = ['fake_option1', 'fake_option2']
|
|
||||||
FAKE_OPTION1 = 'fake_option1'
|
|
||||||
FAKE_DIR_FILES = ['fake_file1.conf', 'fake_file2.conf', 'fake.md']
|
|
||||||
FAKE_SECTIONS = ['fake_section1', 'fake_section2']
|
|
||||||
FAKE_SECTION1 = 'fake_section1'
|
|
||||||
FAKE_SECTION2 = 'fake_section2'
|
|
||||||
FAKE_SET_DICT = {
|
|
||||||
'key1': 'value1',
|
|
||||||
'key2': 'value2',
|
|
||||||
}
|
|
||||||
FAKE_REPO_DOWN_URL = '/fake/down/url/fake.repo'
|
|
||||||
|
|
||||||
FAKE_COMPOSE_URL = (
|
|
||||||
'https://composes.centos.org/fake-CentOS-Stream/compose/')
|
|
||||||
FAKE_REPO_PATH = '/etc/yum.repos.d/fake.repo'
|
|
||||||
FAKE_RELEASE_NAME = 'fake_release'
|
|
||||||
|
|
||||||
FAKE_COMPOSE_INFO = {
|
|
||||||
"header": {
|
|
||||||
"version": "1.2",
|
|
||||||
},
|
|
||||||
"payload": {
|
|
||||||
"compose": {
|
|
||||||
"id": "fake_compose_id",
|
|
||||||
},
|
|
||||||
"release": {
|
|
||||||
"name": "CentOS Stream",
|
|
||||||
"short": "CentOS-Stream",
|
|
||||||
"version": "8",
|
|
||||||
},
|
|
||||||
"variants": {
|
|
||||||
"AppStream": {
|
|
||||||
"arches": [
|
|
||||||
"aarch64",
|
|
||||||
"ppc64le",
|
|
||||||
"x86_64"
|
|
||||||
],
|
|
||||||
"id": "AppStream",
|
|
||||||
"name": "AppStream",
|
|
||||||
"paths": {
|
|
||||||
"packages": {
|
|
||||||
"aarch64": "AppStream/aarch64/os/Packages",
|
|
||||||
"ppc64le": "AppStream/ppc64le/os/Packages",
|
|
||||||
"x86_64": "AppStream/x86_64/os/Packages",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"BaseOS": {
|
|
||||||
"arches": [
|
|
||||||
"aarch64",
|
|
||||||
"ppc64le",
|
|
||||||
"x86_64",
|
|
||||||
],
|
|
||||||
"id": "BaseOS",
|
|
||||||
"name": "BaseOS",
|
|
||||||
"paths": {
|
|
||||||
"packages": {
|
|
||||||
"aarch64": "BaseOS/aarch64/os/Packages",
|
|
||||||
"ppc64le": "BaseOS/ppc64le/os/Packages",
|
|
||||||
"x86_64": "BaseOS/x86_64/os/Packages",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
FAKE_ENV_OUTPUT = """
|
|
||||||
LANG=C.utf8
|
|
||||||
HOSTNAME=4cb7d7db1907
|
|
||||||
which_declare=declare -f
|
|
||||||
container=oci
|
|
||||||
PWD=/
|
|
||||||
HOME=/root
|
|
||||||
TERM=xterm
|
|
||||||
SHLVL=1
|
|
||||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
||||||
_=/usr/bin/env
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class FakeConfigParser(dict):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(FakeConfigParser, self).__init__(*args, **kwargs)
|
|
||||||
self.__dict__ = self
|
|
||||||
|
|
||||||
def write(self, file, space_around_delimiters=False):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def read(self, file):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def add_section(self, section):
|
|
||||||
self[section] = {}
|
|
||||||
|
|
||||||
def sections(self):
|
|
||||||
return self.keys()
|
|
@ -1,18 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
sys.modules["dnf"] = mock.Mock()
|
|
@ -1,119 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import copy
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
from unittest import mock
|
|
||||||
import urllib.request
|
|
||||||
|
|
||||||
from . import fakes
|
|
||||||
from . import test_main
|
|
||||||
import tripleo_repos.yum_config.constants as const
|
|
||||||
import tripleo_repos.yum_config.exceptions as exc
|
|
||||||
import tripleo_repos.yum_config.compose_repos as repos
|
|
||||||
import tripleo_repos.yum_config.yum_config as yum_config
|
|
||||||
|
|
||||||
|
|
||||||
class TestTripleOComposeRepos(test_main.TestTripleoYumConfigBase):
|
|
||||||
"""Tests for TripleComposeRepos class and its methods."""
|
|
||||||
def setUp(self):
|
|
||||||
super(TestTripleOComposeRepos, self).setUp()
|
|
||||||
self.repos = self._create_compose_repos_obj(
|
|
||||||
dir_path='/tmp'
|
|
||||||
)
|
|
||||||
|
|
||||||
def _create_compose_repos_obj(
|
|
||||||
self,
|
|
||||||
compose_url=fakes.FAKE_COMPOSE_URL,
|
|
||||||
release=const.COMPOSE_REPOS_RELEASES[0],
|
|
||||||
dir_path=None,
|
|
||||||
arch=const.COMPOSE_REPOS_SUPPORTED_ARCHS[0]):
|
|
||||||
|
|
||||||
url_res = mock.Mock()
|
|
||||||
json_data = json.dumps(fakes.FAKE_COMPOSE_INFO)
|
|
||||||
self.mock_object(urllib.request, "urlopen",
|
|
||||||
mock.Mock(return_value=url_res))
|
|
||||||
self.mock_object(url_res, 'read',
|
|
||||||
mock.Mock(return_value=json_data))
|
|
||||||
|
|
||||||
return repos.TripleOYumComposeRepoConfig(
|
|
||||||
compose_url, release, dir_path=dir_path, arch=arch)
|
|
||||||
|
|
||||||
def test_tripleo_compose_repos_invalid_release(self):
|
|
||||||
self.assertRaises(exc.TripleOYumConfigComposeError,
|
|
||||||
repos.TripleOYumComposeRepoConfig,
|
|
||||||
fakes.FAKE_COMPOSE_URL,
|
|
||||||
'invalid_release')
|
|
||||||
|
|
||||||
def test_tripleo_compose_repos_invalid_url(self):
|
|
||||||
self.assertRaises(exc.TripleOYumConfigComposeError,
|
|
||||||
repos.TripleOYumComposeRepoConfig,
|
|
||||||
"http://invalid_url.org",
|
|
||||||
const.COMPOSE_REPOS_RELEASES[0])
|
|
||||||
|
|
||||||
def test__get_compose_info_exc(self):
|
|
||||||
self.mock_object(urllib.request, "urlopen",
|
|
||||||
mock.Mock(side_effect=Exception))
|
|
||||||
|
|
||||||
self.assertRaises(exc.TripleOYumConfigComposeError,
|
|
||||||
self.repos._get_compose_info)
|
|
||||||
|
|
||||||
def test_enable_compose_repos(self):
|
|
||||||
self.mock_object(self.repos, 'add_section')
|
|
||||||
self.repos.enable_compose_repos(
|
|
||||||
variants=fakes.FAKE_COMPOSE_INFO['payload']['variants'].keys(),
|
|
||||||
override_repos=False
|
|
||||||
)
|
|
||||||
|
|
||||||
@mock.patch('builtins.open')
|
|
||||||
def test_add_section(self, open):
|
|
||||||
self.mock_object(os.path, 'isfile', mock.Mock(return_value=False))
|
|
||||||
mock_add_section = self.mock_object(yum_config.TripleOYumConfig,
|
|
||||||
"add_section")
|
|
||||||
|
|
||||||
self.repos.add_section(fakes.FAKE_SECTION1, fakes.FAKE_SET_DICT,
|
|
||||||
fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
mock_add_section.assert_called_once_with(
|
|
||||||
fakes.FAKE_SECTION1, fakes.FAKE_SET_DICT, fakes.FAKE_FILE_PATH
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_section(self):
|
|
||||||
mock_update = self.mock_object(yum_config.TripleOYumConfig,
|
|
||||||
"update_section")
|
|
||||||
expected_set_dict = copy.deepcopy(fakes.FAKE_SET_DICT)
|
|
||||||
expected_set_dict['enabled'] = '1'
|
|
||||||
|
|
||||||
self.repos.update_section(fakes.FAKE_SECTION1,
|
|
||||||
set_dict=fakes.FAKE_SET_DICT,
|
|
||||||
enabled=True,
|
|
||||||
file_path=fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
mock_update.assert_called_once_with(fakes.FAKE_SECTION1,
|
|
||||||
expected_set_dict,
|
|
||||||
file_path=fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
def test_update_all_sections(self):
|
|
||||||
mock_update = self.mock_object(yum_config.TripleOYumConfig,
|
|
||||||
"update_all_sections")
|
|
||||||
expected_set_dict = copy.deepcopy(fakes.FAKE_SET_DICT)
|
|
||||||
expected_set_dict['enabled'] = '0'
|
|
||||||
|
|
||||||
self.repos.update_all_sections(fakes.FAKE_FILE_PATH,
|
|
||||||
set_dict=fakes.FAKE_SET_DICT,
|
|
||||||
enabled=False)
|
|
||||||
|
|
||||||
mock_update.assert_called_once_with(expected_set_dict,
|
|
||||||
fakes.FAKE_FILE_PATH)
|
|
@ -1,71 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
import ddt
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
from . import test_main
|
|
||||||
import tripleo_repos.yum_config.dnf_manager as dnf_mgr
|
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
|
||||||
class TestTripleODnfManager(test_main.TestTripleoYumConfigBase):
|
|
||||||
"""Tests for DnfModuleManager class and its methods."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestTripleODnfManager, self).setUp()
|
|
||||||
self.dnf = dnf_mgr.DnfModuleManager()
|
|
||||||
|
|
||||||
@ddt.data(
|
|
||||||
{'module': 'fake', 'stream': None, 'profile': None},
|
|
||||||
{'module': 'fake', 'stream': 'fake_stream', 'profile': None},
|
|
||||||
{'module': 'fake', 'stream': None, 'profile': 'fake_prof'},
|
|
||||||
{'module': 'fake', 'stream': 'fake_stream', 'profile': 'fake_prof'},
|
|
||||||
)
|
|
||||||
@ddt.unpack
|
|
||||||
def test__get_module_spec(self, module, stream, profile):
|
|
||||||
exp_str = module
|
|
||||||
exp_str += ':' + stream if stream else ''
|
|
||||||
exp_str += '/' + profile if profile else ''
|
|
||||||
|
|
||||||
result = self.dnf._get_module_spec(module, stream=stream,
|
|
||||||
profile=profile)
|
|
||||||
|
|
||||||
self.assertEqual(exp_str, result)
|
|
||||||
|
|
||||||
@ddt.data(Exception, RuntimeError)
|
|
||||||
def test__do_transaction_failure(self, exc):
|
|
||||||
mock_transaction = self.mock_object(
|
|
||||||
self.dnf.base, 'do_transaction',
|
|
||||||
mock.Mock(side_effect=exc))
|
|
||||||
|
|
||||||
self.assertRaises(exc, self.dnf._do_transaction)
|
|
||||||
|
|
||||||
mock_transaction.assert_called_once()
|
|
||||||
|
|
||||||
@ddt.data('enable', 'disable', 'reset', 'install', 'remove')
|
|
||||||
def test_module_operations(self, operation):
|
|
||||||
fake_module = 'fake_module'
|
|
||||||
fake_stream = 'fake_stream'
|
|
||||||
fake_profile = 'fake_profile'
|
|
||||||
mock_get_mod_spec = self.mock_object(self.dnf, '_get_module_spec')
|
|
||||||
mock_op = self.mock_object(self.dnf.module_base, operation)
|
|
||||||
mock_transaction = self.mock_object(self.dnf, '_do_transaction')
|
|
||||||
|
|
||||||
dnf_method = getattr(self.dnf, operation + "_module")
|
|
||||||
dnf_method(fake_module, stream=fake_stream, profile=fake_profile)
|
|
||||||
|
|
||||||
mock_get_mod_spec.assert_called_once_with(
|
|
||||||
fake_module, stream=fake_stream, profile=fake_profile)
|
|
||||||
mock_op.assert_called_once()
|
|
||||||
mock_transaction.assert_called_once()
|
|
@ -1,209 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import ddt
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
from . import fakes
|
|
||||||
from . import mock_modules # noqa: F401
|
|
||||||
import tripleo_repos.yum_config.__main__ as main
|
|
||||||
import tripleo_repos.yum_config.compose_repos as repos
|
|
||||||
import tripleo_repos.yum_config.constants as const
|
|
||||||
import tripleo_repos.yum_config.dnf_manager as dnf_mgr
|
|
||||||
import tripleo_repos.yum_config.utils as utils
|
|
||||||
import tripleo_repos.yum_config.yum_config as yum_cfg
|
|
||||||
|
|
||||||
|
|
||||||
class TestTripleoYumConfigBase(unittest.TestCase):
|
|
||||||
"""Base test class for tripleo yum config module."""
|
|
||||||
|
|
||||||
def mock_object(self, obj, attr, new_attr=None):
|
|
||||||
if not new_attr:
|
|
||||||
new_attr = mock.Mock()
|
|
||||||
|
|
||||||
patcher = mock.patch.object(obj, attr, new_attr)
|
|
||||||
patcher.start()
|
|
||||||
# stop patcher at the end of the test
|
|
||||||
self.addCleanup(patcher.stop)
|
|
||||||
|
|
||||||
return new_attr
|
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
|
||||||
class TestTripleoYumConfigMain(TestTripleoYumConfigBase):
|
|
||||||
"""Test class for main method operations."""
|
|
||||||
def setUp(self):
|
|
||||||
super(TestTripleoYumConfigMain, self).setUp()
|
|
||||||
self.mock_object(utils, 'get_distro_info',
|
|
||||||
mock.Mock(return_value=("centos", "8", None)))
|
|
||||||
|
|
||||||
def test_main_repo(self):
|
|
||||||
sys.argv[1:] = ['repo', '--name', 'fake_repo', '--enable',
|
|
||||||
'--set-opts', 'key1=value1', 'key2=value2',
|
|
||||||
'--config-file-path', fakes.FAKE_FILE_PATH,
|
|
||||||
'--down-url', fakes.FAKE_REPO_DOWN_URL]
|
|
||||||
|
|
||||||
yum_repo_obj = mock.Mock()
|
|
||||||
mock_update_section = self.mock_object(yum_repo_obj,
|
|
||||||
'add_or_update_section')
|
|
||||||
mock_yum_repo_obj = self.mock_object(
|
|
||||||
yum_cfg, 'TripleOYumRepoConfig',
|
|
||||||
mock.Mock(return_value=yum_repo_obj))
|
|
||||||
|
|
||||||
main.main()
|
|
||||||
expected_dict = {'key1': 'value1', 'key2': 'value2'}
|
|
||||||
|
|
||||||
mock_yum_repo_obj.assert_called_once_with(dir_path=const.YUM_REPO_DIR,
|
|
||||||
environment_file=None)
|
|
||||||
mock_update_section.assert_called_once_with(
|
|
||||||
'fake_repo', set_dict=expected_dict,
|
|
||||||
file_path=fakes.FAKE_FILE_PATH, enabled=True,
|
|
||||||
from_url=fakes.FAKE_REPO_DOWN_URL)
|
|
||||||
|
|
||||||
def test_main_repo_from_url(self):
|
|
||||||
sys.argv[1:] = ['repo', '--enable',
|
|
||||||
'--set-opts', 'key1=value1', 'key2=value2',
|
|
||||||
'--config-file-path', fakes.FAKE_FILE_PATH,
|
|
||||||
'--down-url', fakes.FAKE_REPO_DOWN_URL]
|
|
||||||
|
|
||||||
yum_repo_obj = mock.Mock()
|
|
||||||
mock_update_all_sections = self.mock_object(
|
|
||||||
yum_repo_obj, 'add_or_update_all_sections_from_url')
|
|
||||||
mock_yum_repo_obj = self.mock_object(
|
|
||||||
yum_cfg, 'TripleOYumRepoConfig',
|
|
||||||
mock.Mock(return_value=yum_repo_obj))
|
|
||||||
|
|
||||||
main.main()
|
|
||||||
expected_dict = {'key1': 'value1', 'key2': 'value2'}
|
|
||||||
|
|
||||||
mock_yum_repo_obj.assert_called_once_with(dir_path=const.YUM_REPO_DIR,
|
|
||||||
environment_file=None)
|
|
||||||
mock_update_all_sections.assert_called_once_with(
|
|
||||||
fakes.FAKE_REPO_DOWN_URL, file_path=fakes.FAKE_FILE_PATH,
|
|
||||||
set_dict=expected_dict, enabled=True)
|
|
||||||
|
|
||||||
@ddt.data('enable', 'disable', 'reset', 'install', 'remove')
|
|
||||||
def test_main_module(self, operation):
|
|
||||||
sys.argv[1:] = ['module', operation, 'fake_module', '--stream',
|
|
||||||
'fake_stream', '--profile', 'fake_profile']
|
|
||||||
|
|
||||||
mock_dnf_mod = mock.Mock()
|
|
||||||
mock_op = self.mock_object(mock_dnf_mod, operation + '_module')
|
|
||||||
mock_dnf_mod_obj = self.mock_object(
|
|
||||||
dnf_mgr, 'DnfModuleManager',
|
|
||||||
mock.Mock(return_value=mock_dnf_mod))
|
|
||||||
|
|
||||||
main.main()
|
|
||||||
|
|
||||||
mock_dnf_mod_obj.assert_called_once()
|
|
||||||
mock_op.assert_called_once_with(
|
|
||||||
'fake_module', stream='fake_stream', profile='fake_profile')
|
|
||||||
|
|
||||||
def test_main_global_conf(self):
|
|
||||||
sys.argv[1:] = ['global', '--set-opts', 'key1=value1', 'key2=value2']
|
|
||||||
yum_global_obj = mock.Mock()
|
|
||||||
mock_update_section = self.mock_object(
|
|
||||||
yum_global_obj, 'update_section')
|
|
||||||
mock_yum_global_obj = self.mock_object(
|
|
||||||
yum_cfg, 'TripleOYumGlobalConfig',
|
|
||||||
mock.Mock(return_value=yum_global_obj))
|
|
||||||
|
|
||||||
main.main()
|
|
||||||
expected_dict = {'key1': 'value1', 'key2': 'value2'}
|
|
||||||
|
|
||||||
mock_yum_global_obj.assert_called_once_with(file_path=None,
|
|
||||||
environment_file=None)
|
|
||||||
mock_update_section.assert_called_once_with('main', expected_dict)
|
|
||||||
|
|
||||||
def test_main_no_command(self):
|
|
||||||
sys.argv[1:] = []
|
|
||||||
with self.assertRaises(SystemExit) as command:
|
|
||||||
main.main()
|
|
||||||
|
|
||||||
self.assertEqual(2, command.exception.code)
|
|
||||||
|
|
||||||
@ddt.data('repo')
|
|
||||||
def test_main_repo_mod_without_name(self, command):
|
|
||||||
sys.argv[1:] = [command, '--set-opts', 'key1=value1',
|
|
||||||
'--config-dir-path', '/tmp']
|
|
||||||
|
|
||||||
with self.assertRaises(SystemExit) as command:
|
|
||||||
main.main()
|
|
||||||
|
|
||||||
self.assertEqual(2, command.exception.code)
|
|
||||||
|
|
||||||
def test_main_repo_without_name_and_url(self):
|
|
||||||
sys.argv[1:] = ['repo', '--enable',
|
|
||||||
'--set-opts', 'key1=value1', 'key2=value2',
|
|
||||||
'--config-file-path', fakes.FAKE_FILE_PATH,
|
|
||||||
'--config-dir-path', '/tmp']
|
|
||||||
|
|
||||||
with self.assertRaises(SystemExit) as command:
|
|
||||||
main.main()
|
|
||||||
|
|
||||||
self.assertEqual(2, command.exception.code)
|
|
||||||
|
|
||||||
@ddt.data('key:value', 'value', 'key value')
|
|
||||||
def test_main_invalid_options_format(self, option):
|
|
||||||
sys.argv[1:] = ['global', '--set-opts', option]
|
|
||||||
|
|
||||||
with self.assertRaises(SystemExit) as command:
|
|
||||||
main.main()
|
|
||||||
|
|
||||||
self.assertEqual(2, command.exception.code)
|
|
||||||
|
|
||||||
def test_main_enable_compose_repos(self):
|
|
||||||
sys.argv[1:] = [
|
|
||||||
'enable-compose-repos', '--compose-url', fakes.FAKE_COMPOSE_URL,
|
|
||||||
'--release', const.COMPOSE_REPOS_RELEASES[0],
|
|
||||||
'--variants', 'fake_variant',
|
|
||||||
'--disable-repos', fakes.FAKE_REPO_PATH,
|
|
||||||
'--arch', const.COMPOSE_REPOS_SUPPORTED_ARCHS[0],
|
|
||||||
]
|
|
||||||
repos_obj = mock.Mock()
|
|
||||||
mock_yum_global_obj = self.mock_object(
|
|
||||||
repos, 'TripleOYumComposeRepoConfig',
|
|
||||||
mock.Mock(return_value=repos_obj))
|
|
||||||
mock_enable_composes = self.mock_object(
|
|
||||||
repos_obj, 'enable_compose_repos')
|
|
||||||
mock_update_all = self.mock_object(
|
|
||||||
repos_obj, 'update_all_sections')
|
|
||||||
self.mock_object(yum_cfg, 'validated_file_path',
|
|
||||||
mock.Mock(return_value=True))
|
|
||||||
|
|
||||||
main.main()
|
|
||||||
|
|
||||||
mock_yum_global_obj.assert_called_once_with(
|
|
||||||
fakes.FAKE_COMPOSE_URL,
|
|
||||||
const.COMPOSE_REPOS_RELEASES[0],
|
|
||||||
dir_path=const.YUM_REPO_DIR,
|
|
||||||
arch=const.COMPOSE_REPOS_SUPPORTED_ARCHS[0],
|
|
||||||
environment_file=None)
|
|
||||||
mock_enable_composes.assert_called_once_with(
|
|
||||||
variants=['fake_variant'], override_repos=False)
|
|
||||||
mock_update_all.assert_called_once_with(
|
|
||||||
fakes.FAKE_REPO_PATH, enabled=False
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_main_invalid_release_for_dnf_module(self):
|
|
||||||
self.mock_object(utils, 'get_distro_info',
|
|
||||||
mock.Mock(return_value=("centos", "7", None)))
|
|
||||||
sys.argv[1:] = ['module', 'enable', 'fake_module']
|
|
||||||
|
|
||||||
with self.assertRaises(SystemExit) as command:
|
|
||||||
main.main()
|
|
||||||
|
|
||||||
self.assertEqual(2, command.exception.code)
|
|
@ -1,462 +0,0 @@
|
|||||||
# Copyright 2021 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import configparser
|
|
||||||
import copy
|
|
||||||
import ddt
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
from . import fakes
|
|
||||||
from . import test_main
|
|
||||||
import tripleo_repos.yum_config.constants as const
|
|
||||||
import tripleo_repos.yum_config.exceptions as exc
|
|
||||||
import tripleo_repos.yum_config.yum_config as yum_cfg
|
|
||||||
import tripleo_repos.utils as repos_utils
|
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
|
||||||
class TestTripleOYumConfig(test_main.TestTripleoYumConfigBase):
|
|
||||||
"""Tests for TripleYumConfig class and its methods."""
|
|
||||||
|
|
||||||
def _create_yum_config_obj(self, dir_path=None, valid_options=None,
|
|
||||||
file_extension=None):
|
|
||||||
self.mock_object(os.path, 'isfile')
|
|
||||||
self.mock_object(os, 'access')
|
|
||||||
self.mock_object(os.path, 'isdir')
|
|
||||||
return yum_cfg.TripleOYumConfig(dir_path=dir_path,
|
|
||||||
valid_options=valid_options,
|
|
||||||
file_extension=file_extension)
|
|
||||||
|
|
||||||
def test_tripleo_yum_config_invalid_dir_path(self):
|
|
||||||
self.mock_object(os.path, 'isdir',
|
|
||||||
mock.Mock(return_value=False))
|
|
||||||
|
|
||||||
self.assertRaises(exc.TripleOYumConfigNotFound,
|
|
||||||
yum_cfg.TripleOYumConfig,
|
|
||||||
dir_path='fake_dir_path')
|
|
||||||
|
|
||||||
def test_read_config_file_path(self):
|
|
||||||
yum_config = self._create_yum_config_obj()
|
|
||||||
|
|
||||||
parser_mock = mock.Mock()
|
|
||||||
self.mock_object(configparser, 'ConfigParser',
|
|
||||||
mock.Mock(return_value=parser_mock))
|
|
||||||
read_mock = self.mock_object(parser_mock, 'read')
|
|
||||||
self.mock_object(parser_mock, 'sections',
|
|
||||||
mock.Mock(return_value=fakes.FAKE_SECTIONS))
|
|
||||||
|
|
||||||
config_parser, file_path = yum_config._read_config_file(
|
|
||||||
fakes.FAKE_FILE_PATH,
|
|
||||||
section=fakes.FAKE_SECTION1
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(parser_mock, config_parser)
|
|
||||||
self.assertEqual(fakes.FAKE_FILE_PATH, file_path)
|
|
||||||
read_mock.assert_called_once_with(fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
def test_read_config_file_path_parse_error(self):
|
|
||||||
yum_config = self._create_yum_config_obj()
|
|
||||||
|
|
||||||
parser_mock = mock.Mock()
|
|
||||||
self.mock_object(configparser, 'ConfigParser',
|
|
||||||
mock.Mock(return_value=parser_mock))
|
|
||||||
read_mock = self.mock_object(parser_mock, 'read',
|
|
||||||
mock.Mock(side_effect=configparser.Error))
|
|
||||||
|
|
||||||
self.assertRaises(exc.TripleOYumConfigFileParseError,
|
|
||||||
yum_config._read_config_file,
|
|
||||||
fakes.FAKE_FILE_PATH,
|
|
||||||
section=fakes.FAKE_SECTION1)
|
|
||||||
|
|
||||||
read_mock.assert_called_once_with(fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
def test_read_config_file_path_invalid_section(self):
|
|
||||||
yum_config = self._create_yum_config_obj()
|
|
||||||
|
|
||||||
parser_mock = mock.Mock()
|
|
||||||
self.mock_object(configparser, 'ConfigParser',
|
|
||||||
mock.Mock(return_value=parser_mock))
|
|
||||||
read_mock = self.mock_object(parser_mock, 'read')
|
|
||||||
self.mock_object(parser_mock, 'sections',
|
|
||||||
mock.Mock(return_value=fakes.FAKE_SECTIONS))
|
|
||||||
|
|
||||||
self.assertRaises(exc.TripleOYumConfigInvalidSection,
|
|
||||||
yum_config._read_config_file,
|
|
||||||
fakes.FAKE_FILE_PATH,
|
|
||||||
section='invalid_section')
|
|
||||||
|
|
||||||
read_mock.assert_called_once_with(fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
def test_get_config_files(self):
|
|
||||||
yum_config = self._create_yum_config_obj(
|
|
||||||
dir_path=fakes.FAKE_DIR_PATH,
|
|
||||||
file_extension='.conf')
|
|
||||||
parser_mocks = []
|
|
||||||
for i in range(3):
|
|
||||||
parser_mock = mock.Mock()
|
|
||||||
parser_mocks.append(parser_mock)
|
|
||||||
self.mock_object(parser_mock, 'read')
|
|
||||||
|
|
||||||
self.mock_object(parser_mocks[0], 'sections',
|
|
||||||
mock.Mock(return_value=[]))
|
|
||||||
# second file inside dir will have the expected sections
|
|
||||||
self.mock_object(parser_mocks[1], 'sections',
|
|
||||||
mock.Mock(return_value=fakes.FAKE_SECTIONS))
|
|
||||||
self.mock_object(parser_mocks[2], 'sections',
|
|
||||||
mock.Mock(return_value=[]))
|
|
||||||
self.mock_object(os, 'listdir',
|
|
||||||
mock.Mock(return_value=fakes.FAKE_DIR_FILES))
|
|
||||||
self.mock_object(os, 'access', mock.Mock(return_value=True))
|
|
||||||
self.mock_object(configparser, 'ConfigParser',
|
|
||||||
mock.Mock(side_effect=parser_mocks))
|
|
||||||
|
|
||||||
result = yum_config._get_config_files(fakes.FAKE_SECTION1)
|
|
||||||
expected_dir_path = [os.path.join(fakes.FAKE_DIR_PATH,
|
|
||||||
fakes.FAKE_DIR_FILES[1])]
|
|
||||||
|
|
||||||
self.assertEqual(expected_dir_path, result)
|
|
||||||
|
|
||||||
@mock.patch('builtins.open')
|
|
||||||
def test_update_section(self, open):
|
|
||||||
yum_config = self._create_yum_config_obj(
|
|
||||||
valid_options=fakes.FAKE_SUPP_OPTIONS)
|
|
||||||
config_parser = fakes.FakeConfigParser({fakes.FAKE_SECTION1: {}})
|
|
||||||
|
|
||||||
mock_read_config = self.mock_object(
|
|
||||||
yum_config, '_read_config_file',
|
|
||||||
mock.Mock(return_value=(config_parser, fakes.FAKE_FILE_PATH)))
|
|
||||||
|
|
||||||
updates = {fakes.FAKE_OPTION1: 'new_fake_value'}
|
|
||||||
|
|
||||||
yum_config.update_section(fakes.FAKE_SECTION1, updates,
|
|
||||||
file_path=fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
mock_read_config.assert_called_once_with(fakes.FAKE_FILE_PATH,
|
|
||||||
section=fakes.FAKE_SECTION1)
|
|
||||||
|
|
||||||
def test_update_section_invalid_options(self):
|
|
||||||
yum_config = self._create_yum_config_obj(
|
|
||||||
valid_options=fakes.FAKE_SUPP_OPTIONS)
|
|
||||||
|
|
||||||
updates = {'invalid_option': 'new_fake_value'}
|
|
||||||
|
|
||||||
self.assertRaises(exc.TripleOYumConfigInvalidOption,
|
|
||||||
yum_config.update_section,
|
|
||||||
fakes.FAKE_SECTION1,
|
|
||||||
updates,
|
|
||||||
file_path=fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
def test_update_section_file_not_found(self):
|
|
||||||
yum_config = self._create_yum_config_obj(
|
|
||||||
valid_options=fakes.FAKE_SUPP_OPTIONS)
|
|
||||||
mock_get_configs = self.mock_object(
|
|
||||||
yum_config, '_get_config_files',
|
|
||||||
mock.Mock(return_value=[]))
|
|
||||||
|
|
||||||
updates = {fakes.FAKE_OPTION1: 'new_fake_value'}
|
|
||||||
|
|
||||||
self.assertRaises(exc.TripleOYumConfigNotFound,
|
|
||||||
yum_config.update_section,
|
|
||||||
fakes.FAKE_SECTION1,
|
|
||||||
updates)
|
|
||||||
mock_get_configs.assert_called_once_with(fakes.FAKE_SECTION1)
|
|
||||||
|
|
||||||
@mock.patch('builtins.open')
|
|
||||||
def test_add_section(self, open):
|
|
||||||
yum_config = self._create_yum_config_obj(
|
|
||||||
valid_options=fakes.FAKE_SUPP_OPTIONS)
|
|
||||||
config_parser = fakes.FakeConfigParser({fakes.FAKE_SECTION1: {}})
|
|
||||||
|
|
||||||
mock_read_config = self.mock_object(
|
|
||||||
yum_config, '_read_config_file',
|
|
||||||
mock.Mock(return_value=(config_parser, fakes.FAKE_FILE_PATH)))
|
|
||||||
|
|
||||||
updates = {fakes.FAKE_OPTION1: 'new_fake_value'}
|
|
||||||
|
|
||||||
yum_config.add_section(fakes.FAKE_SECTION2, updates,
|
|
||||||
file_path=fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
mock_read_config.assert_called_once_with(
|
|
||||||
file_path=fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
@mock.patch('builtins.open')
|
|
||||||
def test_add_section_already_exists(self, open):
|
|
||||||
yum_config = self._create_yum_config_obj(
|
|
||||||
valid_options=fakes.FAKE_SUPP_OPTIONS)
|
|
||||||
config_parser = fakes.FakeConfigParser({fakes.FAKE_SECTION1: {}})
|
|
||||||
|
|
||||||
mock_read_config = self.mock_object(
|
|
||||||
yum_config, '_read_config_file',
|
|
||||||
mock.Mock(return_value=(config_parser, fakes.FAKE_FILE_PATH)))
|
|
||||||
|
|
||||||
updates = {fakes.FAKE_OPTION1: 'new_fake_value'}
|
|
||||||
|
|
||||||
self.assertRaises(exc.TripleOYumConfigInvalidSection,
|
|
||||||
yum_config.add_section,
|
|
||||||
fakes.FAKE_SECTION1, updates,
|
|
||||||
file_path=fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
mock_read_config.assert_called_once_with(
|
|
||||||
file_path=fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
@mock.patch('builtins.open')
|
|
||||||
def test_add_update_all_sections(self, open):
|
|
||||||
yum_config = self._create_yum_config_obj(
|
|
||||||
valid_options=fakes.FAKE_SUPP_OPTIONS)
|
|
||||||
config_parser = fakes.FakeConfigParser({fakes.FAKE_SECTION1: {}})
|
|
||||||
|
|
||||||
mock_read_config = self.mock_object(
|
|
||||||
yum_config, '_read_config_file',
|
|
||||||
mock.Mock(return_value=(config_parser, fakes.FAKE_FILE_PATH)))
|
|
||||||
|
|
||||||
updates = {fakes.FAKE_OPTION1: 'new_fake_value'}
|
|
||||||
|
|
||||||
yum_config.update_all_sections(updates, fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
mock_read_config.assert_called_once_with(fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
def test_source_env_file(self):
|
|
||||||
p_open_mock = mock.Mock()
|
|
||||||
mock_open = self.mock_object(subprocess, 'Popen',
|
|
||||||
mock.Mock(return_value=p_open_mock))
|
|
||||||
data_mock = mock.Mock()
|
|
||||||
self.mock_object(data_mock, 'decode',
|
|
||||||
mock.Mock(return_value=fakes.FAKE_ENV_OUTPUT))
|
|
||||||
self.mock_object(p_open_mock, 'communicate',
|
|
||||||
mock.Mock(return_value=[data_mock]))
|
|
||||||
env_update_mock = self.mock_object(os.environ, 'update')
|
|
||||||
|
|
||||||
yum_cfg.source_env_file('fake_source_file', update=True)
|
|
||||||
|
|
||||||
exp_env_dict = dict(
|
|
||||||
line.split("=", 1) for line in fakes.FAKE_ENV_OUTPUT.splitlines()
|
|
||||||
if len(line.split("=", 1)) > 1)
|
|
||||||
|
|
||||||
mock_open.assert_called_once_with(". fake_source_file; env",
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
shell=True)
|
|
||||||
env_update_mock.assert_called_once_with(exp_env_dict)
|
|
||||||
|
|
||||||
def test_get_config_from_url_invalid_url(self):
|
|
||||||
yum_config = self._create_yum_config_obj(
|
|
||||||
valid_options=fakes.FAKE_SUPP_OPTIONS)
|
|
||||||
fake_context = mock.Mock()
|
|
||||||
self.mock_object(repos_utils, 'http_get',
|
|
||||||
mock.Mock(return_value=(fake_context, 404)))
|
|
||||||
|
|
||||||
self.assertRaises(exc.TripleOYumConfigUrlError,
|
|
||||||
yum_config.get_config_from_url,
|
|
||||||
fakes.FAKE_REPO_DOWN_URL)
|
|
||||||
|
|
||||||
def test_get_config_from_url(self):
|
|
||||||
yum_config = self._create_yum_config_obj(
|
|
||||||
valid_options=fakes.FAKE_SUPP_OPTIONS)
|
|
||||||
fake_context = mock.Mock()
|
|
||||||
self.mock_object(repos_utils, 'http_get',
|
|
||||||
mock.Mock(return_value=(fake_context, 200)))
|
|
||||||
parser_mock = mock.Mock()
|
|
||||||
self.mock_object(configparser, 'ConfigParser',
|
|
||||||
mock.Mock(return_value=parser_mock))
|
|
||||||
|
|
||||||
result = yum_config.get_config_from_url(fakes.FAKE_REPO_DOWN_URL)
|
|
||||||
|
|
||||||
self.assertEqual(parser_mock, result)
|
|
||||||
|
|
||||||
def test_get_options_from_url_section_not_found(self):
|
|
||||||
yum_config = self._create_yum_config_obj(
|
|
||||||
valid_options=fakes.FAKE_SUPP_OPTIONS)
|
|
||||||
fake_config = mock.Mock()
|
|
||||||
self.mock_object(fake_config, 'sections',
|
|
||||||
mock.Mock(return_value=[]))
|
|
||||||
mock_get_from_url = self.mock_object(
|
|
||||||
yum_config, 'get_config_from_url',
|
|
||||||
mock.Mock(return_value=fake_config))
|
|
||||||
|
|
||||||
self.assertRaises(exc.TripleOYumConfigInvalidSection,
|
|
||||||
yum_config.get_options_from_url,
|
|
||||||
fakes.FAKE_REPO_DOWN_URL,
|
|
||||||
fakes.FAKE_SECTION1)
|
|
||||||
|
|
||||||
mock_get_from_url.assert_called_once_with(fakes.FAKE_REPO_DOWN_URL)
|
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
|
||||||
class TestTripleOYumRepoConfig(test_main.TestTripleoYumConfigBase):
|
|
||||||
"""Tests for TripleOYumRepoConfig class and its methods."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestTripleOYumRepoConfig, self).setUp()
|
|
||||||
self.config_obj = yum_cfg.TripleOYumRepoConfig(
|
|
||||||
dir_path='/tmp'
|
|
||||||
)
|
|
||||||
|
|
||||||
@ddt.data(True, False, None)
|
|
||||||
def test_yum_repo_config_update_section(self, enable):
|
|
||||||
self.mock_object(os.path, 'isfile')
|
|
||||||
self.mock_object(os, 'access')
|
|
||||||
self.mock_object(os.path, 'isdir')
|
|
||||||
|
|
||||||
mock_update = self.mock_object(yum_cfg.TripleOYumConfig,
|
|
||||||
'update_section')
|
|
||||||
|
|
||||||
updates = {fakes.FAKE_OPTION1: 'new_fake_value'}
|
|
||||||
expected_updates = copy.copy(updates)
|
|
||||||
if enable is not None:
|
|
||||||
expected_updates['enabled'] = '1' if enable else '0'
|
|
||||||
|
|
||||||
self.config_obj.update_section(
|
|
||||||
fakes.FAKE_SECTION1, set_dict=updates,
|
|
||||||
file_path=fakes.FAKE_FILE_PATH, enabled=enable)
|
|
||||||
|
|
||||||
mock_update.assert_called_once_with(fakes.FAKE_SECTION1,
|
|
||||||
expected_updates,
|
|
||||||
file_path=fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
@mock.patch('builtins.open')
|
|
||||||
@ddt.data(None, fakes.FAKE_REPO_DOWN_URL)
|
|
||||||
def test_add_or_update_section(self, open, down_url):
|
|
||||||
mock_update = self.mock_object(
|
|
||||||
self.config_obj, 'update_section',
|
|
||||||
mock.Mock(side_effect=exc.TripleOYumConfigNotFound(
|
|
||||||
error_msg='error')))
|
|
||||||
mock_add_section = self.mock_object(self.config_obj, 'add_section')
|
|
||||||
extra_opt = {'key1': 'new value 1'}
|
|
||||||
mock_get_from_url = self.mock_object(
|
|
||||||
self.config_obj, 'get_options_from_url',
|
|
||||||
mock.Mock(return_value=extra_opt))
|
|
||||||
|
|
||||||
self.config_obj.add_or_update_section(
|
|
||||||
fakes.FAKE_SECTION1,
|
|
||||||
set_dict=fakes.FAKE_SET_DICT,
|
|
||||||
file_path=fakes.FAKE_FILE_PATH,
|
|
||||||
enabled=True,
|
|
||||||
create_if_not_exists=True,
|
|
||||||
from_url=down_url)
|
|
||||||
|
|
||||||
fake_set_dict = copy.deepcopy(fakes.FAKE_SET_DICT)
|
|
||||||
fake_set_dict['name'] = fakes.FAKE_SECTION1
|
|
||||||
if down_url:
|
|
||||||
fake_set_dict.update(extra_opt)
|
|
||||||
mock_get_from_url.assert_called_once_with(down_url,
|
|
||||||
fakes.FAKE_SECTION1)
|
|
||||||
|
|
||||||
mock_update.assert_called_once_with(fakes.FAKE_SECTION1,
|
|
||||||
set_dict=fake_set_dict,
|
|
||||||
file_path=fakes.FAKE_FILE_PATH,
|
|
||||||
enabled=True)
|
|
||||||
mock_add_section.assert_called_once_with(
|
|
||||||
fakes.FAKE_SECTION1,
|
|
||||||
fake_set_dict,
|
|
||||||
fakes.FAKE_FILE_PATH,
|
|
||||||
enabled=True)
|
|
||||||
|
|
||||||
@ddt.data((fakes.FAKE_FILE_PATH, False), (None, True))
|
|
||||||
@ddt.unpack
|
|
||||||
def test_add_or_update_section_file_not_found(self, fake_path,
|
|
||||||
create_if_not_exists):
|
|
||||||
mock_update = self.mock_object(
|
|
||||||
self.config_obj, 'update_section',
|
|
||||||
mock.Mock(side_effect=exc.TripleOYumConfigNotFound(
|
|
||||||
error_msg='error')))
|
|
||||||
|
|
||||||
self.assertRaises(
|
|
||||||
exc.TripleOYumConfigNotFound,
|
|
||||||
self.config_obj.add_or_update_section,
|
|
||||||
fakes.FAKE_SECTION1,
|
|
||||||
set_dict=fakes.FAKE_SET_DICT,
|
|
||||||
file_path=fake_path,
|
|
||||||
enabled=True,
|
|
||||||
create_if_not_exists=create_if_not_exists)
|
|
||||||
|
|
||||||
fake_set_dict = copy.deepcopy(fakes.FAKE_SET_DICT)
|
|
||||||
fake_set_dict['name'] = fakes.FAKE_SECTION1
|
|
||||||
mock_update.assert_called_once_with(fakes.FAKE_SECTION1,
|
|
||||||
set_dict=fake_set_dict,
|
|
||||||
file_path=fake_path,
|
|
||||||
enabled=True)
|
|
||||||
|
|
||||||
@ddt.data(None, False, True)
|
|
||||||
def test_add_section(self, enabled):
|
|
||||||
mock_add = self.mock_object(yum_cfg.TripleOYumConfig, 'add_section')
|
|
||||||
|
|
||||||
self.config_obj.add_section(
|
|
||||||
fakes.FAKE_SECTION1, fakes.FAKE_SET_DICT,
|
|
||||||
fakes.FAKE_FILE_PATH, enabled=enabled)
|
|
||||||
|
|
||||||
updated_dict = copy.deepcopy(fakes.FAKE_SET_DICT)
|
|
||||||
if enabled is not None:
|
|
||||||
updated_dict['enabled'] = '1' if enabled else '0'
|
|
||||||
mock_add.assert_called_once_with(fakes.FAKE_SECTION1, updated_dict,
|
|
||||||
fakes.FAKE_FILE_PATH)
|
|
||||||
|
|
||||||
@ddt.data(fakes.FAKE_FILE_PATH, None)
|
|
||||||
def test_add_or_update_all_sections_from_url(self, file_path):
|
|
||||||
add_or_update_section = self.mock_object(
|
|
||||||
self.config_obj, 'add_or_update_section')
|
|
||||||
fake_config = mock.Mock()
|
|
||||||
self.mock_object(fake_config, 'sections',
|
|
||||||
mock.Mock(return_value=[fakes.FAKE_SECTION1]))
|
|
||||||
options_from_url = {'key3': 'value3'}
|
|
||||||
self.mock_object(fake_config, 'items',
|
|
||||||
mock.Mock(return_value=options_from_url))
|
|
||||||
mock_get_from_url = self.mock_object(
|
|
||||||
self.config_obj, 'get_config_from_url',
|
|
||||||
mock.Mock(return_value=fake_config))
|
|
||||||
exp_file_path = (
|
|
||||||
file_path or os.path.join(
|
|
||||||
'/tmp', fakes.FAKE_REPO_DOWN_URL.split('/')[-1])
|
|
||||||
)
|
|
||||||
|
|
||||||
self.config_obj.add_or_update_all_sections_from_url(
|
|
||||||
fakes.FAKE_REPO_DOWN_URL,
|
|
||||||
file_path=file_path,
|
|
||||||
set_dict=fakes.FAKE_SET_DICT,
|
|
||||||
enabled=True,
|
|
||||||
create_if_not_exists=True)
|
|
||||||
|
|
||||||
mock_get_from_url.assert_called_once_with(fakes.FAKE_REPO_DOWN_URL)
|
|
||||||
expected_update_dict = copy.deepcopy(fakes.FAKE_SET_DICT)
|
|
||||||
expected_update_dict.update(options_from_url)
|
|
||||||
add_or_update_section.assert_called_once_with(
|
|
||||||
fakes.FAKE_SECTION1, set_dict=expected_update_dict,
|
|
||||||
file_path=exp_file_path, enabled=True,
|
|
||||||
create_if_not_exists=True)
|
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
|
||||||
class TestTripleOYumGlobalConfig(test_main.TestTripleoYumConfigBase):
|
|
||||||
"""Tests for TripleOYumGlobalConfig class and its methods."""
|
|
||||||
|
|
||||||
@mock.patch('builtins.open')
|
|
||||||
def test_create_yum_global_config_create_yum_conf(self, open):
|
|
||||||
self.mock_object(os, 'access')
|
|
||||||
self.mock_object(os.path, 'isdir')
|
|
||||||
self.mock_object(os.path, 'isfile',
|
|
||||||
mock.Mock(side_effect=[False, True]))
|
|
||||||
|
|
||||||
fake_cfg_parser = mock.Mock()
|
|
||||||
mock_read = self.mock_object(fake_cfg_parser, 'read')
|
|
||||||
mock_add = self.mock_object(fake_cfg_parser, 'add_section')
|
|
||||||
mock_write = self.mock_object(fake_cfg_parser, 'write')
|
|
||||||
self.mock_object(configparser, 'ConfigParser',
|
|
||||||
mock.Mock(return_value=fake_cfg_parser))
|
|
||||||
|
|
||||||
cfg_obj = yum_cfg.TripleOYumGlobalConfig()
|
|
||||||
|
|
||||||
self.assertIsNotNone(cfg_obj)
|
|
||||||
mock_read.assert_called_once_with(const.YUM_GLOBAL_CONFIG_FILE_PATH)
|
|
||||||
mock_add.assert_called_once_with('main')
|
|
||||||
mock_write.assert_called_once()
|
|
108
tox.ini
108
tox.ini
@ -1,108 +0,0 @@
|
|||||||
[tox]
|
|
||||||
minversion = 3.18.0
|
|
||||||
skipsdist = True
|
|
||||||
envlist = py,pep8,packaging,sanity
|
|
||||||
requires =
|
|
||||||
tox-ansible>=1.5.3
|
|
||||||
|
|
||||||
[testenv]
|
|
||||||
usedevelop = True
|
|
||||||
setenv =
|
|
||||||
ANSIBLE_FORCE_COLOR={tty:1:0}
|
|
||||||
VIRTUAL_ENV={envdir}
|
|
||||||
passenv =
|
|
||||||
HOME
|
|
||||||
deps =
|
|
||||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
|
||||||
-r{toxinidir}/test-requirements.txt
|
|
||||||
-r{toxinidir}/requirements.txt
|
|
||||||
commands =
|
|
||||||
stestr run --slowest {posargs}
|
|
||||||
|
|
||||||
[testenv:venv]
|
|
||||||
commands = {posargs}
|
|
||||||
|
|
||||||
[testenv:docs]
|
|
||||||
deps =
|
|
||||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
|
||||||
-r{toxinidir}/doc/requirements.txt
|
|
||||||
allowlist_externals =
|
|
||||||
rm
|
|
||||||
commands =
|
|
||||||
rm -rf doc/build
|
|
||||||
sphinx-build -W --keep-going -b html doc/source doc/build/html
|
|
||||||
|
|
||||||
[testenv:pep8]
|
|
||||||
deps = flake8
|
|
||||||
commands = flake8
|
|
||||||
|
|
||||||
[testenv:cover]
|
|
||||||
setenv =
|
|
||||||
PYTHON=coverage run --source tripleo_repos --parallel-mode
|
|
||||||
commands =
|
|
||||||
stestr run {posargs}
|
|
||||||
coverage combine
|
|
||||||
coverage html -d cover
|
|
||||||
coverage xml -o cover/coverage.xml
|
|
||||||
|
|
||||||
[testenv:packaging]
|
|
||||||
description =
|
|
||||||
Build package, verify metadata, install package and assert basic behavior
|
|
||||||
deps =
|
|
||||||
build
|
|
||||||
twine
|
|
||||||
ansible-core # used for ansible-galaxy command
|
|
||||||
skip_install = true
|
|
||||||
commands =
|
|
||||||
# build wheel and sdist using PEP-517
|
|
||||||
{envpython} -c 'import os.path, shutil, sys; \
|
|
||||||
dist_dir = os.path.join("{toxinidir}", "dist"); \
|
|
||||||
os.path.isdir(dist_dir) or sys.exit(0); \
|
|
||||||
print("Removing \{!s\} contents...".format(dist_dir), file=sys.stderr); \
|
|
||||||
shutil.rmtree(dist_dir)'
|
|
||||||
{envpython} -m build \
|
|
||||||
--sdist \
|
|
||||||
--wheel \
|
|
||||||
--outdir {toxinidir}/dist/ \
|
|
||||||
{toxinidir}
|
|
||||||
# Validate metadata using twine
|
|
||||||
twine check {toxinidir}/dist/*
|
|
||||||
# Install the wheel
|
|
||||||
sh -c "python3 -m pip install {toxinidir}/dist/*.whl"
|
|
||||||
# Assure that CLIs were installed
|
|
||||||
tripleo-repos --help
|
|
||||||
tripleo-get-hash --help
|
|
||||||
tripleo-yum-config --help
|
|
||||||
# Validate collection installation
|
|
||||||
ansible-galaxy collection install --force .
|
|
||||||
# Ensure that ansible is able to load the modules, as syntax check will fail
|
|
||||||
# if modules cannot be loaded.
|
|
||||||
sh -c "ansible-playbook --syntax-check playbooks/*.yaml"
|
|
||||||
ansible localhost -m tripleo.repos.get_hash -a "release=master os_version=centos8"
|
|
||||||
|
|
||||||
allowlist_externals =
|
|
||||||
sh
|
|
||||||
|
|
||||||
# https://github.com/ansible-community/tox-ansible/issues/96
|
|
||||||
# Override ansible version coming from ansible-test package.
|
|
||||||
[testenv:sanity]
|
|
||||||
deps =
|
|
||||||
ansible-core>=2.11,<2.12
|
|
||||||
|
|
||||||
[testenv:molecule]
|
|
||||||
description = Used by all molecule jobs (tox-ansible)
|
|
||||||
deps =
|
|
||||||
ansible-core
|
|
||||||
molecule>=3.3.0,<3.4.0 # bug with collection install
|
|
||||||
molecule-docker
|
|
||||||
usedevelop = False
|
|
||||||
skip_install = true
|
|
||||||
commands =
|
|
||||||
ansible-galaxy collection install -v 'community.docker:>=1.8.0'
|
|
||||||
ansible-galaxy collection install -v --force .
|
|
||||||
molecule test
|
|
||||||
|
|
||||||
[flake8]
|
|
||||||
ignore = H803
|
|
||||||
show-source = True
|
|
||||||
exclude = .tox,dist,doc,*.egg,build
|
|
@ -1 +0,0 @@
|
|||||||
plugins/module_utils/tripleo_repos
|
|
@ -1,40 +0,0 @@
|
|||||||
- job:
|
|
||||||
name: tox-sanity-py39
|
|
||||||
description: Run ansible-test sanity tests on a collection
|
|
||||||
parent: openstack-tox-py39
|
|
||||||
vars:
|
|
||||||
tox_envlist: sanity
|
|
||||||
- project:
|
|
||||||
templates:
|
|
||||||
- check-requirements
|
|
||||||
- openstack-cover-jobs
|
|
||||||
- openstack-python3-zed-jobs
|
|
||||||
check:
|
|
||||||
jobs:
|
|
||||||
- openstack-tox-pep8:
|
|
||||||
vars:
|
|
||||||
tox_envlist: pep8,packaging
|
|
||||||
- openstack-tox-py39
|
|
||||||
- tripleo-tox-molecule
|
|
||||||
- tripleo-buildimage-overcloud-hardened-uefi-full-centos-9:
|
|
||||||
dependencies: &deps_unit_lint_cprovider
|
|
||||||
- openstack-tox-pep8
|
|
||||||
- openstack-tox-py38
|
|
||||||
- openstack-tox-py39
|
|
||||||
- tripleo-ci-centos-9-content-provider:
|
|
||||||
dependencies: *deps_unit_lint_cprovider
|
|
||||||
# TODO(marios) re-enable when https://bugs.launchpad.net/tripleo/+bug/2002112
|
|
||||||
# - tox-sanity-py39
|
|
||||||
gate:
|
|
||||||
jobs:
|
|
||||||
- openstack-tox-pep8:
|
|
||||||
vars:
|
|
||||||
tox_envlist: pep8,packaging
|
|
||||||
- openstack-tox-py39
|
|
||||||
- tripleo-buildimage-overcloud-hardened-uefi-full-centos-9:
|
|
||||||
dependencies: *deps_unit_lint_cprovider
|
|
||||||
- tripleo-ci-centos-9-content-provider:
|
|
||||||
dependencies: *deps_unit_lint_cprovider
|
|
||||||
post:
|
|
||||||
jobs:
|
|
||||||
- publish-openstack-python-branch-tarball
|
|
Loading…
Reference in New Issue
Block a user