Merge "Improvements to yum-config module"

This commit is contained in:
Zuul 2021-10-04 19:59:31 +00:00 committed by Gerrit Code Review
commit de3736a739
11 changed files with 483 additions and 112 deletions

View File

@ -13,34 +13,47 @@
register: result
failed_when: result is success
- name: "Disable one of the system repos"
- name: "Test disable system repo"
become: true
tripleo.repos.yum_config:
type: repo
name: rt
file_path: /etc/yum.repos.d/CentOS-Stream-RealTime.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 idemptotence test.
# the line below which disables molecule idempotence test.
- molecule-idempotence-notest
# TODO: code needs a fix to be compatible with CentOS-7
when: ansible_distribution_major_version is version(8, '>=')
- name: "Update yum_config global config"
- 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
file_path: "{{ '/etc/dnf/dnf.conf' if (ansible_distribution_major_version is version(8, '>=')) else '/etc/yum.conf' }}"
set_options:
skip_if_unavailable: "False"
keepcache: "0"
fake_conf: "True"
tags:
# TODO: fix yum_config to correctly report changed state and uncomment
# the line below which disables molecule idemptotence test.
# the line below which disables molecule idempotence test.
- molecule-idempotence-notest
# TODO: code needs a fix to be compatible with CentOS-7
when: ansible_distribution_major_version is version(8, '>=')
- name: "Test yum_config enable-compose-repos"
become: true

View File

@ -2,32 +2,33 @@
- name: Verify
hosts: all
tasks:
- name: Validate if RealTime repo is disabled
- 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: RealTime
path: /etc/yum.repos.d/CentOS-Stream-RealTime.repo
section: rt
- name: "{{ section_name|upper }}"
path: "{{ repo_path }}"
section: "{{ section_name }}"
key: enabled
value: "0"
# TODO: code needs a fix to be compatible with CentOS-7
when: ansible_distribution_major_version is version(8, '>=')
- name: Validate yum/dnf conf file
- 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: dnf.conf
path: /etc/dnf/dnf.conf
- name: global_conf
path: "{{ conf_file_path }}"
section: main
key: skip_if_unavailable
value: "False"
- name: dnf.conf
path: /etc/dnf/dnf.conf
- name: global_conf
path: "{{ conf_file_path }}"
section: main
key: keepcache
value: "0"
# TODO: code needs a fix to be compatible with CentOS-7
when: ansible_distribution_major_version is version(8, '>=')
key: fake_conf
value: "True"
- name: Validate compose repos outputs
include_tasks: verify_compose_repos.yml

View File

@ -17,10 +17,9 @@ import argparse
import logging
import sys
import tripleo_repos.yum_config.compose_repos as compose_repos
import tripleo_repos.yum_config.constants as const
import tripleo_repos.yum_config.dnf_manager as dnf_mgr
import tripleo_repos.yum_config.yum_config as cfg
import tripleo_repos.yum_config.utils as utils
def options_to_dict(options):
@ -39,6 +38,12 @@ def options_to_dict(options):
def main():
cfg.TripleOYumConfig.load_logging()
# 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)
@ -47,6 +52,15 @@ def main():
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',
@ -171,24 +185,32 @@ def main():
# Subcommands
subparsers.add_parser(
'repo',
parents=[common_parse, repo_args_parser, options_parse],
parents=[common_parse, environment_parse, repo_args_parser,
options_parse],
help='updates a yum repository options'
)
subparsers.add_parser(
'module',
parents=[dnf_module_parser],
help='updates yum module options'
)
subparsers.add_parser(
'global',
parents=[common_parse, options_parse],
parents=[common_parse, environment_parse, options_parse],
help='updates global yum configuration options'
)
subparsers.add_parser(
'enable-compose-repos',
parents=[compose_args_parser],
help='enable CentOS compose repos based on an compose url.'
)
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:
@ -202,13 +224,14 @@ def main():
if args.command == 'repo':
set_dict = options_to_dict(args.set_opts)
config_obj = cfg.TripleOYumRepoConfig(
dir_path=args.config_dir_path)
config_obj.update_section(args.name, set_dict,
file_path=args.config_file_path,
enabled=args.enable)
dir_path=args.config_dir_path,
environment_file=args.env_file)
config_obj.add_or_update_section(args.name, set_dict=set_dict,
file_path=args.config_file_path,
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)
@ -216,16 +239,19 @@ def main():
elif args.command == 'global':
set_dict = options_to_dict(args.set_opts)
config_obj = cfg.TripleOYumGlobalConfig(
file_path=args.config_file_path)
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)
arch=args.arch,
environment_file=args.env_file)
repo_obj.enable_compose_repos(variants=args.variants,
override_repos=args.disable_conflicting)

View File

@ -18,8 +18,6 @@ import logging
import json
import os
import re
import urllib.parse
import urllib.request
from .constants import (
YUM_REPO_DIR,
@ -44,7 +42,8 @@ __metaclass__ = type
class TripleOYumComposeRepoConfig(TripleOYumConfig):
"""Manages yum repo configuration files for CentOS Compose."""
def __init__(self, compose_url, release, dir_path=None, arch=None):
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'
@ -80,11 +79,13 @@ class TripleOYumComposeRepoConfig(TripleOYumConfig):
super(TripleOYumComposeRepoConfig, self).__init__(
valid_options=YUM_REPO_SUPPORTED_OPTIONS,
dir_path=conf_dir_path,
file_extension=YUM_REPO_FILE_EXTENSION)
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)
@ -179,7 +180,7 @@ class TripleOYumComposeRepoConfig(TripleOYumConfig):
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'):
with open(file_path, 'w+'):
pass
super(TripleOYumComposeRepoConfig, self).add_section(
section, add_dict, file_path)

View File

@ -19,14 +19,21 @@ List of options that can be updated for yum repo files.
"""
__metaclass__ = type
YUM_REPO_SUPPORTED_OPTIONS = [
'name',
'baseurl',
'cost',
'enabled',
'exclude',
'excludepkgs',
'gpgcheck',
'gpgkey',
'priority',
'exclude',
'includepkgs',
'metalink',
'mirrorlist',
'module_hotfixes',
'name',
'priority'
]
"""
@ -68,3 +75,12 @@ 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},
]

View File

@ -0,0 +1,50 @@
# 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

View File

@ -15,9 +15,9 @@
from __future__ import (absolute_import, division, print_function)
import configparser
import logging
import os
import subprocess
import sys
from .constants import (
@ -33,6 +33,54 @@ from .exceptions import (
TripleOYumConfigNotFound,
)
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
@ -43,6 +91,21 @@ def validated_file_path(file_path):
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
@ -79,7 +142,8 @@ class TripleOYumConfig:
logger.addHandler(handler)
logger.setLevel(logging.INFO)
def __init__(self, valid_options=None, dir_path=None, file_extension=None):
def __init__(self, valid_options=None, dir_path=None, file_extension=None,
environment_file=None):
"""
Creates a TripleOYumConfig object that holds configuration file
information.
@ -90,10 +154,13 @@ class TripleOYumConfig:
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:
@ -102,6 +169,9 @@ class TripleOYumConfig:
'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.
@ -109,7 +179,7 @@ class TripleOYumConfig:
to fail earlier if the section is not found.
:return: a config parser object and the full file path.
"""
config = configparser.ConfigParser()
config = cfg_parser.ConfigParser()
file_paths = [file_path]
if self.dir_path:
# if dir_path is configured, we can search for filename there
@ -127,7 +197,7 @@ class TripleOYumConfig:
try:
config.read(valid_file_path)
except configparser.Error:
except cfg_parser.Error:
msg = 'Unable to parse configuration file {0}.'.format(
valid_file_path)
raise TripleOYumConfigFileParseError(error_msg=msg)
@ -161,10 +231,10 @@ class TripleOYumConfig:
if not os.access(os.path.join(self.dir_path, file), os.W_OK):
continue
tmp_config = configparser.ConfigParser()
tmp_config = cfg_parser.ConfigParser()
try:
tmp_config.read(os.path.join(self.dir_path, file))
except configparser.Error:
except cfg_parser.Error:
continue
if section in tmp_config.sections():
config_files_path.append(os.path.join(self.dir_path, file))
@ -195,12 +265,12 @@ class TripleOYumConfig:
'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
config[section].update(set_dict)
with open(file, 'w') as f:
config.write(f)
save_section_to_file(file, config, section, set_dict)
logging.info("Section '%s' was successfully "
"updated.", section)
@ -213,6 +283,11 @@ class TripleOYumConfig:
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():
@ -220,13 +295,12 @@ class TripleOYumConfig:
"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
config[section].update(add_dict)
with open(file_path, '+w') as file:
config.write(file)
save_section_to_file(file_path, config, section, add_dict)
logging.info("Section '%s' was successfully "
"added.", section)
@ -245,10 +319,7 @@ class TripleOYumConfig:
config, file_path = self._read_config_file(file_path)
for section in config.sections():
config[section].update(set_dict)
with open(file_path, '+w') as file:
config.write(file)
save_section_to_file(file_path, config, section, set_dict)
logging.info("All sections for '%s' were successfully "
"updated.", file_path)
@ -257,13 +328,14 @@ class TripleOYumConfig:
class TripleOYumRepoConfig(TripleOYumConfig):
"""Manages yum repo configuration files."""
def __init__(self, dir_path=None):
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)
file_extension=YUM_REPO_FILE_EXTENSION,
environment_file=environment_file)
def update_section(
self, section, set_dict=None, file_path=None, enabled=None):
@ -274,11 +346,36 @@ class TripleOYumRepoConfig(TripleOYumConfig):
super(TripleOYumRepoConfig, self).update_section(
section, update_dict, file_path=file_path)
def add_section(self, section, add_dict, file_path, enabled=None):
update_dict = add_dict or {}
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):
try:
self.update_section(
section, set_dict=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
# When creating a new repo file, make sure that it has a name
if 'name' not in set_dict.keys():
set_dict['name'] = section
self.add_section(section, set_dict, file_path, enabled=enabled)
class TripleOYumGlobalConfig(TripleOYumConfig):
"""Manages yum global configuration file."""
def __init__(self, file_path=None):
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)
@ -290,13 +387,14 @@ class TripleOYumGlobalConfig(TripleOYumConfig):
# 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 = configparser.ConfigParser()
config = cfg_parser.ConfigParser()
config.read(self.conf_file_path)
config.add_section('main')
with open(self.conf_file_path, '+w') as file:
with open(self.conf_file_path, 'w+') as file:
config.write(file)
super(TripleOYumGlobalConfig, self).__init__()
super(TripleOYumGlobalConfig, self).__init__(
environment_file=environment_file)
def update_section(self, section, set_dict, file_path=None):
super(TripleOYumGlobalConfig, self).update_section(

View File

@ -66,6 +66,11 @@ options:
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.
@ -151,7 +156,7 @@ EXAMPLES = r'''
- name: Configure a set of repos based on latest CentOS Stream 8 compose
become: true
become_user: root
tripleo_yup_config:
tripleo_yum_config:
compose_url: https://composes.centos.org/latest-CentOS-Stream-8/compose/
centos_release: centos-stream-8
variants:
@ -165,6 +170,7 @@ EXAMPLES = r'''
RETURN = r''' # '''
from ansible.module_utils import six # noqa: E402
from ansible.module_utils.basic import AnsibleModule # noqa: E402
@ -172,10 +178,13 @@ def run_module():
try:
import ansible_collections.tripleo.repos.plugins.module_utils. \
tripleo_repos.yum_config.constants as const
import ansible_collections.tripleo.repos.plugins.module_utils. \
tripleo_repos.yum_config.utils as utils
except ImportError:
import tripleo_repos.yum_config.constants as const
# define available arguments/parameters a user can pass to the module
supported_config_types = ['repo', 'module', 'global',
import tripleo_repos.yum_config.utils as utils
supported_config_types = ['repo', 'global', 'module',
'enable-compose-repos']
supported_module_operations = ['install', 'remove', 'reset']
module_args = dict(
@ -188,6 +197,7 @@ def run_module():
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),
@ -199,17 +209,37 @@ def run_module():
disable_repos=dict(type='list', default=[],
elements='str'),
)
required_if_params = [
["type", "repo", ["name"]],
["type", "module", ["name"]],
["type", "enable-compose-repos", ["compose_url"]]
]
module = AnsibleModule(
argument_spec=module_args,
required_if=[
["type", "repo", ["name"]],
["type", "module", ["name"]],
["type", "enable-compose-repos", ["compose_url"]],
],
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)
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')
@ -223,33 +253,41 @@ def run_module():
# Module execution
try:
try:
import ansible_collections.tripleo.repos.plugins.module_utils.\
tripleo_repos.yum_config.dnf_manager as dnf_mgr
import ansible_collections.tripleo.repos.plugins.module_utils.\
tripleo_repos.yum_config.yum_config as cfg
import ansible_collections.tripleo.repos.plugins.module_utils. \
tripleo_repos.yum_config.compose_repos as repos
except ImportError:
import tripleo_repos.yum_config.dnf_manager as dnf_mgr
import tripleo_repos.yum_config.yum_config as cfg
import tripleo_repos.yum_config.compose_repos as repos
if module.params['type'] == 'repo':
config_obj = cfg.TripleOYumRepoConfig(
dir_path=module.params['dir_path'])
config_obj.update_section(
dir_path=module.params['dir_path'],
environment_file=module.params['environment_file'])
config_obj.add_or_update_section(
module.params['name'],
m_set_opts,
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'])
arch=module.params['arch'],
environment_file=module.params['environment_file'])
# 2. enable CentOS compose repos
repo_obj.enable_compose_repos(
variants=module.params['variants'],
@ -259,6 +297,12 @@ def run_module():
repo_obj.update_all_sections(file, 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'],
@ -275,11 +319,6 @@ def run_module():
stream=module.params['stream'],
profile=module.params['profile'])
elif module.params['type'] == 'global':
config_obj = cfg.TripleOYumGlobalConfig(
file_path=module.params['file_path'])
config_obj.update_section('main', m_set_opts)
except Exception as exc:
module.fail_json(msg=str(exc))

View File

@ -28,6 +28,7 @@ FAKE_SET_DICT = {
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": {
@ -79,13 +80,26 @@ FAKE_COMPOSE_INFO = {
},
}
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):
def write(self, file, space_around_delimiters=False):
pass
def read(self, file):

View File

@ -23,6 +23,7 @@ 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
@ -44,13 +45,19 @@ class TestTripleoYumConfigBase(unittest.TestCase):
@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', 'fake_repo', '--enable',
'--set-opts', 'key1=value1', 'key2=value2',
'--config-file-path', fakes.FAKE_FILE_PATH]
yum_repo_obj = mock.Mock()
mock_update_section = self.mock_object(yum_repo_obj, 'update_section')
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))
@ -58,10 +65,11 @@ class TestTripleoYumConfigMain(TestTripleoYumConfigBase):
main.main()
expected_dict = {'key1': 'value1', 'key2': 'value2'}
mock_yum_repo_obj.assert_called_once_with(dir_path=const.YUM_REPO_DIR)
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', expected_dict, file_path=fakes.FAKE_FILE_PATH,
enabled=True)
'fake_repo', set_dict=expected_dict,
file_path=fakes.FAKE_FILE_PATH, enabled=True)
@ddt.data('enable', 'disable', 'reset', 'install', 'remove')
def test_main_module(self, operation):
@ -92,7 +100,8 @@ class TestTripleoYumConfigMain(TestTripleoYumConfigBase):
main.main()
expected_dict = {'key1': 'value1', 'key2': 'value2'}
mock_yum_global_obj.assert_called_once_with(file_path=None)
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):
@ -143,9 +152,20 @@ class TestTripleoYumConfigMain(TestTripleoYumConfigBase):
fakes.FAKE_COMPOSE_URL,
const.COMPOSE_REPOS_RELEASES[0],
dir_path=const.YUM_REPO_DIR,
arch=const.COMPOSE_REPOS_SUPPORTED_ARCHS[0])
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)

View File

@ -16,6 +16,7 @@ import configparser
import copy
import ddt
import os
import subprocess
from unittest import mock
from . import fakes
@ -226,17 +227,44 @@ class TestTripleOYumConfig(test_main.TestTripleoYumConfigBase):
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)
@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')
cfg_obj = yum_cfg.TripleOYumRepoConfig()
mock_update = self.mock_object(yum_cfg.TripleOYumConfig,
'update_section')
@ -246,13 +274,78 @@ class TestTripleOYumRepoConfig(test_main.TestTripleoYumConfigBase):
if enable is not None:
expected_updates['enabled'] = '1' if enable else '0'
cfg_obj.update_section(fakes.FAKE_SECTION1, set_dict=updates,
file_path=fakes.FAKE_FILE_PATH, enabled=enable)
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')
def test_add_or_update_section(self, open):
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')
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)
mock_update.assert_called_once_with(fakes.FAKE_SECTION1,
set_dict=fakes.FAKE_SET_DICT,
file_path=fakes.FAKE_FILE_PATH,
enabled=True)
fake_set_dict = copy.deepcopy(fakes.FAKE_SET_DICT)
fake_set_dict['name'] = fakes.FAKE_SECTION1
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)
mock_update.assert_called_once_with(fakes.FAKE_SECTION1,
set_dict=fakes.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.ddt
class TestTripleOYumGlobalConfig(test_main.TestTripleoYumConfigBase):