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

Save sharedconfig to disk #2649

Merged
merged 3 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions azurelinuxagent/common/protocol/goal_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from azurelinuxagent.common.protocol.hostplugin import VmSettingsNotSupported, VmSettingsSupportStopped
from azurelinuxagent.common.protocol.restapi import Cert, CertList, RemoteAccessUser, RemoteAccessUsersList
from azurelinuxagent.common.utils import fileutil
from azurelinuxagent.common.utils.archive import GoalStateHistory
from azurelinuxagent.common.utils.archive import GoalStateHistory, SHARED_CONF_FILE_NAME
from azurelinuxagent.common.utils.cryptutil import CryptUtil
from azurelinuxagent.common.utils.textutil import parse_doc, findall, find, findtext, getattrib

Expand Down Expand Up @@ -345,8 +345,14 @@ def _fetch_full_wire_server_goal_state(self, incarnation, xml_doc):

shared_conf_uri = findtext(xml_doc, "SharedConfig")
xml_text = self._wire_client.fetch_config(shared_conf_uri, self._wire_client.get_header())
shared_conf = SharedConfig(xml_text)
shared_config = SharedConfig(xml_text)
self._history.save_shared_conf(xml_text)
# SharedConfig.xml is used by other components (Azsec and Singularity/HPC Infiniband), so save it to the agent's root directory as well
shared_config_file = os.path.join(conf.get_lib_dir(), SHARED_CONF_FILE_NAME)
try:
fileutil.write_file(shared_config_file, xml_text)
except Exception as e:
logger.warn("Failed to save {0}: {1}".format(shared_config, e))

certs = EmptyCertificates()
certs_uri = findtext(xml_doc, "Certificates")
Expand All @@ -372,7 +378,7 @@ def _fetch_full_wire_server_goal_state(self, incarnation, xml_doc):
self._role_config_name = role_config_name
self._container_id = container_id
self._hosting_env = hosting_env
self._shared_conf = shared_conf
self._shared_conf = shared_config
self._certs = certs
self._remote_access = remote_access

Expand Down
11 changes: 7 additions & 4 deletions azurelinuxagent/common/utils/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@
_MAX_ARCHIVED_STATES = 50

_CACHE_PATTERNS = [
#
# Note that SharedConfig.xml is not included here; this file is used by other components (Azsec and Singularity/HPC Infiniband)
#
re.compile(r"^VmSettings\.\d+\.json$"),
re.compile(r"^(.*)\.(\d+)\.(agentsManifest)$", re.IGNORECASE),
re.compile(r"^(.*)\.(\d+)\.(manifest\.xml)$", re.IGNORECASE),
re.compile(r"^(.*)\.(\d+)\.(xml)$", re.IGNORECASE),
re.compile(r"^SharedConfig\.xml$", re.IGNORECASE),
re.compile(r"^HostingEnvironmentConfig\.xml$", re.IGNORECASE),
re.compile(r"^RemoteAccess\.xml$", re.IGNORECASE),
re.compile(r"^waagent_status\.\d+\.json$"),
Expand Down Expand Up @@ -78,12 +80,12 @@
_VM_SETTINGS_FILE_NAME = "VmSettings.json"
_CERTIFICATES_FILE_NAME = "Certificates.json"
_HOSTING_ENV_FILE_NAME = "HostingEnvironmentConfig.xml"
_SHARED_CONF_FILE_NAME = "SharedConfig.xml"
_REMOTE_ACCESS_FILE_NAME = "RemoteAccess.xml"
_EXT_CONF_FILE_NAME = "ExtensionsConfig.xml"
_MANIFEST_FILE_NAME = "{0}.manifest.xml"

AGENT_STATUS_FILE = "waagent_status.json"
SHARED_CONF_FILE_NAME = "SharedConfig.xml"

# TODO: use @total_ordering once RHEL/CentOS and SLES 11 are EOL.
# @total_ordering first appeared in Python 2.7 and 3.2
Expand Down Expand Up @@ -166,9 +168,10 @@ def __init__(self, lib_dir):
def purge_legacy_goal_state_history():
lib_dir = conf.get_lib_dir()
for current_file in os.listdir(lib_dir):
# Don't remove the placeholder goal state file.
# TODO: See comment in GoalStateHistory._save_placeholder and remove this code when no longer needed
if current_file == _PLACEHOLDER_FILE_NAME:
return
continue
# END TODO
full_path = os.path.join(lib_dir, current_file)
for pattern in _CACHE_PATTERNS:
Expand Down Expand Up @@ -302,4 +305,4 @@ def save_hosting_env(self, text):
self.save(text, _HOSTING_ENV_FILE_NAME)

def save_shared_conf(self, text):
self.save(text, _SHARED_CONF_FILE_NAME)
self.save(text, SHARED_CONF_FILE_NAME)
3 changes: 1 addition & 2 deletions azurelinuxagent/ga/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ def run(self, debug=False):
self._ensure_firewall_rules_persisted(dst_ip=protocol.get_endpoint())
self._add_accept_tcp_firewall_rule_if_not_enabled(dst_ip=protocol.get_endpoint())
self._reset_legacy_blacklisted_agents()
self._cleanup_legacy_goal_state_history()

# Get all thread handlers
telemetry_handler = get_send_telemetry_events_handler(self.protocol_util)
Expand All @@ -396,8 +397,6 @@ def run(self, debug=False):

logger.info("Goal State Period: {0} sec. This indicates how often the agent checks for new goal states and reports status.", self._goal_state_period)

self._cleanup_legacy_goal_state_history()

while self.is_running:
self._check_daemon_running(debug)
self._check_threads_running(all_thread_handlers)
Expand Down
11 changes: 10 additions & 1 deletion tests/protocol/test_goal_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import re
import time

from azurelinuxagent.common import conf
from azurelinuxagent.common.future import httpclient
from azurelinuxagent.common.protocol.extensions_goal_state import GoalStateSource, GoalStateChannel
from azurelinuxagent.common.protocol.extensions_goal_state_from_extensions_config import ExtensionsGoalStateFromExtensionsConfig
Expand Down Expand Up @@ -96,7 +97,15 @@ def test_fetch_goal_state_should_raise_on_incomplete_goal_state(self):
GoalState(protocol.client)
self.assertEqual(_GET_GOAL_STATE_MAX_ATTEMPTS, mock_sleep.call_count, "Unexpected number of retries")

def test_instantiating_goal_state_should_save_the_goal_state_to_the_history_directory(self):
def test_fetching_the_goal_state_should_save_the_shared_config(self):
# SharedConfig.xml is used by other components (Azsec and Singularity/HPC Infiniband); verify that we do not delete it
with mock_wire_protocol(mockwiredata.DATA_FILE_VM_SETTINGS) as protocol:
_ = GoalState(protocol.client)

shared_config = os.path.join(conf.get_lib_dir(), 'SharedConfig.xml')
self.assertTrue(os.path.exists(shared_config), "{0} should have been created".format(shared_config))

def test_fetching_the_goal_state_should_save_the_goal_state_to_the_history_directory(self):
with mock_wire_protocol(mockwiredata.DATA_FILE_VM_SETTINGS) as protocol:
protocol.mock_wire_data.set_incarnation(999)
protocol.mock_wire_data.set_etag(888)
Expand Down
9 changes: 7 additions & 2 deletions tests/utils/test_archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,27 +133,32 @@ def test_goal_state_history_init_should_purge_old_items(self):

def test_purge_legacy_goal_state_history(self):
with patch("azurelinuxagent.common.conf.get_lib_dir", return_value=self.tmp_dir):
# SharedConfig.xml is used by other components (Azsec and Singularity/HPC Infiniband); verify that we do not delete it
shared_config = os.path.join(self.tmp_dir, 'SharedConfig.xml')

legacy_files = [
'GoalState.2.xml',
'VmSettings.2.json',
'Prod.2.manifest.xml',
'ExtensionsConfig.2.xml',
'Microsoft.Azure.Extensions.CustomScript.1.xml',
'SharedConfig.xml',
'HostingEnvironmentConfig.xml',
'RemoteAccess.xml',
'waagent_status.1.json'
]
legacy_files = [os.path.join(self.tmp_dir, f) for f in legacy_files]

self._write_file(shared_config)
for f in legacy_files:
self._write_file(f)

StateArchiver.purge_legacy_goal_state_history()

self.assertTrue(os.path.exists(shared_config), "{0} should not have been removed".format(shared_config))

for f in legacy_files:
self.assertFalse(os.path.exists(f), "Legacy file {0} was not removed".format(f))


@staticmethod
def parse_isoformat(timestamp_str):
return datetime.strptime(timestamp_str, '%Y-%m-%dT%H:%M:%S.%f')
Expand Down