Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support systems with transactional updates #156

Merged
merged 7 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions integration_test-process.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ After installing the test package with "zypper in"
+ no error no message
+ zypper lr has no repos
+ nothing in /etc/zypp/credentials.d
+ nothing in /etc/zypp/services.d/
+ no pkl files in /var/cache/cloudregister/
- registercloudguest
+ no error
+ sucees message on stdout
Expand Down Expand Up @@ -75,6 +77,7 @@ After installing the test package with "zypper in"
+ no error no message
+ zypper lr has no repos
+ nothing in /etc/zypp/credentials.d
+ nothing in /etc/zypp/services.d/
- registercloudguest -r XXX
+ no error
+ sucees message on stdout
Expand Down
23 changes: 21 additions & 2 deletions lib/cloudregister/registerutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,13 @@ def clear_rmt_as_scc_proxy_flag():
def credentials_files_are_equal(repo_credentials):
"""Compare the base credentials files the the repo header and make
sure they have the same values."""
credentials_location = '/etc/zypp/credentials.d/'

if not repo_credentials or not isinstance(repo_credentials, str):
return False

base_credentials_location = '/etc/zypp/credentials.d/'
target_root = get_zypper_target_root()
credentials_location = target_root + base_credentials_location
rjschwei marked this conversation as resolved.
Show resolved Hide resolved
credentials_base = os.path.join(credentials_location, 'SCCcredentials')
credentials_header = os.path.join(credentials_location, repo_credentials)
ref_user, ref_pass = get_credentials(credentials_base)
Expand Down Expand Up @@ -522,7 +525,8 @@ def get_credentials_file(update_server, service_name=None):
after the system is properly registered.
"""
credentials_file = ''
credentials_loc = '/etc/zypp/credentials.d/'
target_root = get_zypper_target_root()
credentials_loc = target_root + '/etc/zypp/credentials.d/'
credential_names = [
'*' + update_server.get_FQDN().replace('.', '_'),
'SCC*'
Expand Down Expand Up @@ -874,6 +878,21 @@ def get_zypper_pid_cache():
return zypper_state_file.read()


# ----------------------------------------------------------------------------
def get_zypper_target_root():
"""Return the target root if zypper has the --root argument to
specify a target directory in which to operate.
"""
zypper_cmd = get_zypper_command()
target_root = ''
for root_arg in ('-R', '--root'):
if zypper_cmd and root_arg in zypper_cmd:
rjschwei marked this conversation as resolved.
Show resolved Hide resolved
target_root = zypper_cmd.split(root_arg)[-1].split()[0].strip()
break

return target_root


# ----------------------------------------------------------------------------
def has_ipv6_access(smt):
"""IPv6 access is possible if we have an SMT server that has an IPv6
Expand Down
5 changes: 4 additions & 1 deletion tests/test_cloudguestregistryauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from lxml import etree

from cloudregister.smt import SMT
from importlib.machinery import SourceFileLoader

from pytest import raises
Expand All @@ -18,6 +17,10 @@

sys.path.insert(0, code_path)

from cloudregister.smt import SMT # noqa

sys.path.insert(0, code_path)

# Hack to get the script without the .py imported for testing
cloudguestregistryauth = SourceFileLoader(
'cloudguestregistryauth',
Expand Down
39 changes: 39 additions & 0 deletions tests/test_registerutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,45 @@ def test_get_zypper_pid_cache_no_cache(path_exists):
assert utils.get_zypper_pid_cache() == 0


@patch('cloudregister.registerutils.get_zypper_command')
def test_get_zypper_target_root_no_zypper(zypp_cmd):
"""Test behavior when zypper is not running"""
zypp_cmd.return_value = None
assert utils.get_zypper_target_root() == ''


@patch('cloudregister.registerutils.get_zypper_command')
def test_get_zypper_target_root_set_R_short(zypp_cmd):
"""Test behavior when zypper is "running" and has root set using -R and no
other args"""
zypp_cmd.return_value = '-R /foobar'
assert utils.get_zypper_target_root() == '/foobar'


@patch('cloudregister.registerutils.get_zypper_command')
def test_get_zypper_target_root_set_R_long(zypp_cmd):
"""Test behavior when zypper is "running" and has root set using -R and
other args"""
zypp_cmd.return_value = '-R /foobar --no-interactive'
assert utils.get_zypper_target_root() == '/foobar'


@patch('cloudregister.registerutils.get_zypper_command')
def test_get_zypper_target_root_set_root_short(zypp_cmd):
"""Test behavior when zypper is "running" and has root set using --root
and no other args"""
zypp_cmd.return_value = '--root /foobar'
assert utils.get_zypper_target_root() == '/foobar'


@patch('cloudregister.registerutils.get_zypper_command')
def test_get_zypper_target_root_set_root_long(zypp_cmd):
"""Test behavior when zypper is "running" and has root set using --root
and other args"""
zypp_cmd.return_value = '--root /foobar --no-interactive'
assert utils.get_zypper_target_root() == '/foobar'


@patch('cloudregister.registerutils.__get_region_server_args')
@patch('cloudregister.registerutils.__get_framework_plugin')
@patch('cloudregister.registerutils.get_framework_identifier_path')
Expand Down
2 changes: 2 additions & 0 deletions usr/lib/zypp/plugins/urlresolver/susecloud
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ class SUSECloudPlugin(Plugin):
if repo_credentials:
repo_url += '?credentials=' + repo_credentials
if verify_credentials:
target_root = utils.get_zypper_target_root()
credentials_file_path = (
target_root +
'/etc/zypp/credentials.d/%s' % repo_credentials
)
user, password = utils.get_credentials(
Expand Down
91 changes: 76 additions & 15 deletions usr/sbin/registercloudguest
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,50 @@ from requests.auth import HTTPBasicAuth
urllib3.disable_warnings()
registration_returncode = 0

# ----------------------------------------------------------------------------
def get_register_cmd():
"""Determine which command we need to use to register the system"""

register_cmd = '/usr/sbin/SUSEConnect'
# Figure out if we are on RO transactional-update system
p = subprocess.Popen(
['findmnt', '--noheadings', '--json', '/'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
res = p.communicate()
# If we get an error from findmnt move forward on a best effort basis
if p.returncode:
logging.warning('Unable to find filesystem information for "/"')
else:
fsinfo = json.loads(res[0])
fsdata = fsinfo.get('filesystems')
if fsdata:
fsoptions = fsdata[0].get('options')
# If we are on a RO system we need to use the
# transactional-update command
if 'ro' in fsoptions.split(','):
cmd_name = 'transactional-update'
for path in ['/sbin/','/usr/sbin/']:
exec_path = path + cmd_name
if os.path.exists(exec_path):
register_cmd = exec_path
break
else:
err_msg = 'transactional-update command not found.'
err_msg += 'But is required on a RO filesystem for '
err_msg += 'registration'
logging.error(err_msg)
print(err_msg, file=sys.stderr)
sys.exit(1)

return register_cmd

# ----------------------------------------------------------------------------
def register_modules(extensions, products, registered=[], failed=[]):
"""Register modules obeying dependencies"""
global registration_returncode
register_cmd = get_register_cmd()
for extension in extensions:
# If the extension is recommended it gets installed with the
# baseproduct registration. No need to run another registration
Expand All @@ -70,13 +110,23 @@ def register_modules(extensions, products, registered=[], failed=[]):
triplet = '/'.join((identifier, version, arch))
if triplet in products and triplet not in registered:
registered.append(triplet)
cmd = [
register_cmd,
'--url',
'https://%s' % registration_target.get_FQDN(),
'--product',
triplet
]
if 'transactional' in register_cmd:
cmd = [
register_cmd,
sub_cmd,
rjschwei marked this conversation as resolved.
Show resolved Hide resolved
'--url',
'https://%s' % registration_target.get_FQDN(),
'--product',
triplet
]
else:
cmd = [
register_cmd,
'--url',
'https://%s' % registration_target.get_FQDN(),
'--product',
triplet
]
if os.path.exists(instance_data_filepath):
cmd.append('--instance-data')
cmd.append(instance_data_filepath)
Expand Down Expand Up @@ -438,10 +488,11 @@ if instance_data:
if not utils.is_registration_supported(cfg):
sys.exit(0)

# Check SUSE/SLES registration command to be present
register_cmd = '/usr/sbin/SUSEConnect'
register_cmd = get_register_cmd()
if not (os.path.exists(register_cmd) and os.access(register_cmd, os.X_OK)):
logging.error('No registration executable found')
err_msg = 'No registration executable found'
logging.error(err_msg)
print(err_msg, file=sys.stderr)
sys.exit(1)

# get product list
Expand All @@ -460,11 +511,20 @@ failed_smts = []

while not base_registered:
utils.add_hosts_entry(registration_target)
cmd = [
register_cmd,
'--url',
'https://%s' % registration_target.get_FQDN()
]
sub_cmd = ''
if 'transactional' in register_cmd:
cmd = [
register_cmd,
'register',
'--url',
'https://%s' % registration_target.get_FQDN()
]
else:
cmd = [
register_cmd,
'--url',
'https://%s' % registration_target.get_FQDN()
]
if os.path.exists(instance_data_filepath):
cmd.append('--instance-data')
cmd.append(instance_data_filepath)
Expand Down Expand Up @@ -503,6 +563,7 @@ while not base_registered:
logging.error('Baseproduct registration failed')
logging.error('\t%s' % error_message)
cleanup()
print(error_message, file=sys.stderr)
sys.exit(1)
for smt_srv in region_smt_servers:
target_smt_ipv4 = registration_target.get_ipv4()
Expand Down