File: test_schema.py

package info (click to toggle)
cloud-init 25.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 12,412 kB
  • sloc: python: 135,894; sh: 3,883; makefile: 141; javascript: 30; xml: 22
file content (192 lines) | stat: -rw-r--r-- 6,641 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
"""Tests for `cloud-init status`"""

from textwrap import dedent

import pytest

from cloudinit import lifecycle
from tests.integration_tests.instances import IntegrationInstance
from tests.integration_tests.util import (
    get_feature_flag_value,
    has_netplanlib,
    verify_clean_boot,
    verify_clean_log,
)

USER_DATA = """\
#cloud-config
apt_update: false
apt_upgrade: false
apt_reboot_if_required: false
"""

NET_CFG_V1 = """\
network:
  version: 1
  config:
  - type: physical
    name: eth0
    subnets:
      - type: dhcp
"""
NET_CFG_V1_INVALID = NET_CFG_V1.replace("config", "junk")
NET_V1_ANNOTATED = """\
network:		# E1,E2
  version: 1
  junk:
  - type: physical
    name: eth0
    subnets:
      - type: dhcp

# Errors: -------------
# E1: 'config' is a required property
# E2: Additional properties are not allowed ('junk' was unexpected)"""

NET_CFG_V2 = """\
version: 2
ethernets:
  eth0:
    dhcp4: true
"""
NET_CFG_V2_INVALID = NET_CFG_V2.replace("true", "bogus")
NET_V2_ANNOTATED = """\
---
network:
    ethernets:
        eth0:
            dhcp4: bogus		# E1
    version: 2
...

# Errors: -------------
# E1: Invalid netplan schema. Error in network definition: invalid boolean value 'bogus'"""  # noqa: E501


@pytest.mark.user_data(USER_DATA)
class TestSchemaDeprecations:
    def test_clean_log(self, class_client: IntegrationInstance):
        log = class_client.read_from_file("/var/log/cloud-init.log")
        verify_clean_log(log, ignore_deprecations=True)
        version_boundary = get_feature_flag_value(
            class_client, "DEPRECATION_INFO_BOUNDARY"
        )
        boundary_message = "Deprecated cloud-config provided:"
        messages = [
            "apt_reboot_if_required:  Deprecated ",
            "apt_update:  Deprecated in version",
            "apt_upgrade:  Deprecated in version",
        ]
        # the deprecation_version is 22.2 in schema for apt_* keys in
        # user-data. Pass 22.2 in against the client's version_boundary.
        if lifecycle.should_log_deprecation("22.2", version_boundary):
            messages += boundary_message
            verify_clean_boot(class_client, require_deprecations=messages)
        else:
            verify_clean_boot(class_client)
            assert f"INFO]: {boundary_message}" in log
            assert "apt_reboot_if_required:  Deprecated " in log
            assert "apt_update:  Deprecated in version" in log
            assert "apt_upgrade:  Deprecated in version" in log

    def test_network_config_schema_validation(
        self, class_client: IntegrationInstance
    ):
        content_responses = {
            NET_CFG_V1: {"out": "Valid schema /root/net.yaml"},
            NET_CFG_V1_INVALID: {
                "out": "Invalid network-config /root/net.yaml",
                "err": (
                    "network: Additional properties are not allowed"
                    " ('junk' was unexpected)"
                ),
                "annotate": NET_V1_ANNOTATED,
            },
        }
        if has_netplanlib(class_client):
            # Support for netplan API available
            content_responses[NET_CFG_V2] = {
                "out": "Valid schema /root/net.yaml"
            }
            content_responses[NET_CFG_V2_INVALID] = {
                "out": "Invalid network-config /root/net.yaml",
                "err": (
                    "Cloud config schema errors: format-l5.c20:"
                    " Invalid netplan schema. Error in network definition:"
                    " invalid boolean value 'bogus'"
                ),
                "annotate": NET_V2_ANNOTATED,
            }
        else:
            # No netplan API available skips validation
            content_responses[NET_CFG_V2] = {
                "out": (
                    "Skipping network-config schema validation for version: 2."
                    " No netplan API available."
                )
            }
            content_responses[NET_CFG_V2_INVALID] = {
                "out": (
                    "Skipping network-config schema validation for version: 2."
                    " No netplan API available."
                )
            }

        for content, responses in content_responses.items():
            class_client.write_to_file("/root/net.yaml", content)
            result = class_client.execute(
                "cloud-init schema --schema-type network-config"
                " --config-file /root/net.yaml"
            )
            assert responses["out"] == result.stdout
            if responses.get("err"):
                assert responses["err"] in result.stderr
            if responses.get("annotate"):
                result = class_client.execute(
                    "cloud-init schema --schema-type network-config"
                    " --config-file /root/net.yaml --annotate"
                )
                assert responses["annotate"] in result.stdout

    def test_schema_deprecations(self, class_client: IntegrationInstance):
        """Test schema behavior with deprecated configs."""
        user_data_fn = "/root/user-data"
        class_client.write_to_file(user_data_fn, USER_DATA)

        result = class_client.execute(
            f"cloud-init schema --config-file {user_data_fn}"
        )
        assert (
            result.ok
        ), "`schema` cmd must return 0 even with deprecated configs"
        assert not result.stderr
        assert "Cloud config schema deprecations:" in result.stdout
        assert "apt_update:  Deprecated in version" in result.stdout
        assert "apt_upgrade:  Deprecated in version" in result.stdout
        assert (
            "apt_reboot_if_required:  Deprecated in version" in result.stdout
        )

        annotated_result = class_client.execute(
            f"cloud-init schema --annotate --config-file {user_data_fn}"
        )
        assert (
            annotated_result.ok
        ), "`schema` cmd must return 0 even with deprecated configs"
        assert not annotated_result.stderr
        expected_output = dedent(
            """\
            #cloud-config
            apt_update: false\t\t# D1
            apt_upgrade: false\t\t# D2
            apt_reboot_if_required: false\t\t# D3

            # Deprecations: -------------
            # D1:  Deprecated in version 22.2. Use **package_update** instead.
            # D2:  Deprecated in version 22.2. Use **package_upgrade** instead.
            # D3:  Deprecated in version 22.2. Use **package_reboot_if_required** instead.


            Valid schema /root/user-data"""  # noqa: E501
        )
        assert expected_output in annotated_result.stdout