cdf1a8
From 005d0a98c69d154a00e9fd599c7fbe5aef73c933 Mon Sep 17 00:00:00 2001
cdf1a8
From: Amy Chen <xiachen@redhat.com>
cdf1a8
Date: Thu, 25 Nov 2021 18:30:48 +0800
cdf1a8
Subject: [PATCH] fix error on upgrade caused by new vendordata2 attributes
cdf1a8
cdf1a8
RH-Author: xiachen <None>
cdf1a8
RH-MergeRequest: 35: fix error on upgrade caused by new vendordata2 attributes
cdf1a8
RH-Commit: [1/1] 9e00a7744838afbbdc5eb14628b7f572beba9f19
cdf1a8
RH-Bugzilla: 2021538
cdf1a8
RH-Acked-by: Mohamed Gamal Morsy <mmorsy@redhat.com>
cdf1a8
RH-Acked-by: Eduardo Otubo <otubo@redhat.com>
cdf1a8
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
cdf1a8
cdf1a8
commit d132356cc361abef2d90d4073438f3ab759d5964
cdf1a8
Author: James Falcon <TheRealFalcon@users.noreply.github.com>
cdf1a8
Date:   Mon Apr 19 11:31:28 2021 -0500
cdf1a8
cdf1a8
    fix error on upgrade caused by new vendordata2 attributes (#869)
cdf1a8
cdf1a8
    In #777, we added 'vendordata2' and 'vendordata2_raw' attributes to
cdf1a8
    the DataSource class, but didn't use the upgrade framework to deal
cdf1a8
    with an unpickle after upgrade. This commit adds the necessary
cdf1a8
    upgrade code.
cdf1a8
cdf1a8
    Additionally, added a smaller-scope upgrade test to our integration
cdf1a8
    tests that will be run on every CI run so we catch these issues
cdf1a8
    immediately in the future.
cdf1a8
cdf1a8
    LP: #1922739
cdf1a8
cdf1a8
Signed-off-by: Amy Chen <xiachen@redhat.com>
cdf1a8
---
cdf1a8
 cloudinit/sources/__init__.py           | 12 +++++++++++-
cdf1a8
 cloudinit/tests/test_upgrade.py         |  4 ++++
cdf1a8
 tests/integration_tests/clouds.py       |  4 ++--
cdf1a8
 tests/integration_tests/test_upgrade.py | 25 ++++++++++++++++++++++++-
cdf1a8
 4 files changed, 41 insertions(+), 4 deletions(-)
cdf1a8
cdf1a8
diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py
cdf1a8
index 1ad1880d..7d74f8d9 100644
cdf1a8
--- a/cloudinit/sources/__init__.py
cdf1a8
+++ b/cloudinit/sources/__init__.py
cdf1a8
@@ -24,6 +24,7 @@ from cloudinit import util
cdf1a8
 from cloudinit.atomic_helper import write_json
cdf1a8
 from cloudinit.event import EventType
cdf1a8
 from cloudinit.filters import launch_index
cdf1a8
+from cloudinit.persistence import CloudInitPickleMixin
cdf1a8
 from cloudinit.reporting import events
cdf1a8
 
cdf1a8
 DSMODE_DISABLED = "disabled"
cdf1a8
@@ -134,7 +135,7 @@ URLParams = namedtuple(
cdf1a8
     'URLParms', ['max_wait_seconds', 'timeout_seconds', 'num_retries'])
cdf1a8
 
cdf1a8
 
cdf1a8
-class DataSource(metaclass=abc.ABCMeta):
cdf1a8
+class DataSource(CloudInitPickleMixin, metaclass=abc.ABCMeta):
cdf1a8
 
cdf1a8
     dsmode = DSMODE_NETWORK
cdf1a8
     default_locale = 'en_US.UTF-8'
cdf1a8
@@ -196,6 +197,8 @@ class DataSource(metaclass=abc.ABCMeta):
cdf1a8
     # non-root users
cdf1a8
     sensitive_metadata_keys = ('merged_cfg', 'security-credentials',)
cdf1a8
 
cdf1a8
+    _ci_pkl_version = 1
cdf1a8
+
cdf1a8
     def __init__(self, sys_cfg, distro, paths, ud_proc=None):
cdf1a8
         self.sys_cfg = sys_cfg
cdf1a8
         self.distro = distro
cdf1a8
@@ -218,6 +221,13 @@ class DataSource(metaclass=abc.ABCMeta):
cdf1a8
         else:
cdf1a8
             self.ud_proc = ud_proc
cdf1a8
 
cdf1a8
+    def _unpickle(self, ci_pkl_version: int) -> None:
cdf1a8
+        """Perform deserialization fixes for Paths."""
cdf1a8
+        if not hasattr(self, 'vendordata2'):
cdf1a8
+            self.vendordata2 = None
cdf1a8
+        if not hasattr(self, 'vendordata2_raw'):
cdf1a8
+            self.vendordata2_raw = None
cdf1a8
+
cdf1a8
     def __str__(self):
cdf1a8
         return type_utils.obj_name(self)
cdf1a8
 
cdf1a8
diff --git a/cloudinit/tests/test_upgrade.py b/cloudinit/tests/test_upgrade.py
cdf1a8
index f79a2536..fd3c5812 100644
cdf1a8
--- a/cloudinit/tests/test_upgrade.py
cdf1a8
+++ b/cloudinit/tests/test_upgrade.py
cdf1a8
@@ -43,3 +43,7 @@ class TestUpgrade:
cdf1a8
     def test_blacklist_drivers_set_on_networking(self, previous_obj_pkl):
cdf1a8
         """We always expect Networking.blacklist_drivers to be initialised."""
cdf1a8
         assert previous_obj_pkl.distro.networking.blacklist_drivers is None
cdf1a8
+
cdf1a8
+    def test_vendordata_exists(self, previous_obj_pkl):
cdf1a8
+        assert previous_obj_pkl.vendordata2 is None
cdf1a8
+        assert previous_obj_pkl.vendordata2_raw is None
cdf1a8
diff --git a/tests/integration_tests/clouds.py b/tests/integration_tests/clouds.py
cdf1a8
index 9527a413..1d0b9d83 100644
cdf1a8
--- a/tests/integration_tests/clouds.py
cdf1a8
+++ b/tests/integration_tests/clouds.py
cdf1a8
@@ -100,14 +100,14 @@ class IntegrationCloud(ABC):
cdf1a8
             # Even if we're using the default key, it may still have a
cdf1a8
             # different name in the clouds, so we need to set it separately.
cdf1a8
             self.cloud_instance.key_pair.name = settings.KEYPAIR_NAME
cdf1a8
-        self._released_image_id = self._get_initial_image()
cdf1a8
+        self.released_image_id = self._get_initial_image()
cdf1a8
         self.snapshot_id = None
cdf1a8
 
cdf1a8
     @property
cdf1a8
     def image_id(self):
cdf1a8
         if self.snapshot_id:
cdf1a8
             return self.snapshot_id
cdf1a8
-        return self._released_image_id
cdf1a8
+        return self.released_image_id
cdf1a8
 
cdf1a8
     def emit_settings_to_log(self) -> None:
cdf1a8
         log.info(
cdf1a8
diff --git a/tests/integration_tests/test_upgrade.py b/tests/integration_tests/test_upgrade.py
cdf1a8
index c20cb3c1..48e0691b 100644
cdf1a8
--- a/tests/integration_tests/test_upgrade.py
cdf1a8
+++ b/tests/integration_tests/test_upgrade.py
cdf1a8
@@ -1,4 +1,5 @@
cdf1a8
 import logging
cdf1a8
+import os
cdf1a8
 import pytest
cdf1a8
 import time
cdf1a8
 from pathlib import Path
cdf1a8
@@ -8,6 +9,8 @@ from tests.integration_tests.conftest import (
cdf1a8
     get_validated_source,
cdf1a8
     session_start_time,
cdf1a8
 )
cdf1a8
+from tests.integration_tests.instances import CloudInitSource
cdf1a8
+
cdf1a8
 
cdf1a8
 log = logging.getLogger('integration_testing')
cdf1a8
 
cdf1a8
@@ -63,7 +66,7 @@ def test_upgrade(session_cloud: IntegrationCloud):
cdf1a8
         return  # type checking doesn't understand that skip raises
cdf1a8
 
cdf1a8
     launch_kwargs = {
cdf1a8
-        'image_id': session_cloud._get_initial_image(),
cdf1a8
+        'image_id': session_cloud.released_image_id,
cdf1a8
     }
cdf1a8
 
cdf1a8
     image = ImageSpecification.from_os_image()
cdf1a8
@@ -93,6 +96,26 @@ def test_upgrade(session_cloud: IntegrationCloud):
cdf1a8
         instance.install_new_cloud_init(source, take_snapshot=False)
cdf1a8
         instance.execute('hostname something-else')
cdf1a8
         _restart(instance)
cdf1a8
+        assert instance.execute('cloud-init status --wait --long').ok
cdf1a8
         _output_to_compare(instance, after_path, netcfg_path)
cdf1a8
 
cdf1a8
     log.info('Wrote upgrade test logs to %s and %s', before_path, after_path)
cdf1a8
+
cdf1a8
+
cdf1a8
+@pytest.mark.ci
cdf1a8
+@pytest.mark.ubuntu
cdf1a8
+def test_upgrade_package(session_cloud: IntegrationCloud):
cdf1a8
+    if get_validated_source(session_cloud) != CloudInitSource.DEB_PACKAGE:
cdf1a8
+        not_run_message = 'Test only supports upgrading to build deb'
cdf1a8
+        if os.environ.get('TRAVIS'):
cdf1a8
+            # If this isn't running on CI, we should know
cdf1a8
+            pytest.fail(not_run_message)
cdf1a8
+        else:
cdf1a8
+            pytest.skip(not_run_message)
cdf1a8
+
cdf1a8
+    launch_kwargs = {'image_id': session_cloud.released_image_id}
cdf1a8
+
cdf1a8
+    with session_cloud.launch(launch_kwargs=launch_kwargs) as instance:
cdf1a8
+        instance.install_deb()
cdf1a8
+        instance.restart()
cdf1a8
+        assert instance.execute('cloud-init status --wait --long').ok
cdf1a8
-- 
cdf1a8
2.27.0
cdf1a8