diff options
Diffstat (limited to 'cloudinit/handlers')
-rw-r--r-- | cloudinit/handlers/__init__.py | 1 | ||||
-rw-r--r-- | cloudinit/handlers/cloud_config.py | 35 |
2 files changed, 29 insertions, 7 deletions
diff --git a/cloudinit/handlers/__init__.py b/cloudinit/handlers/__init__.py index 1d450061..4c7c9295 100644 --- a/cloudinit/handlers/__init__.py +++ b/cloudinit/handlers/__init__.py @@ -62,6 +62,7 @@ INCLUSION_TYPES_MAP = { '#part-handler': 'text/part-handler', '#cloud-boothook': 'text/cloud-boothook', '#cloud-config-archive': 'text/cloud-config-archive', + '#json-patch': 'application/json-patch+json', } # Sorted longest first diff --git a/cloudinit/handlers/cloud_config.py b/cloudinit/handlers/cloud_config.py index 730672d7..0f080e66 100644 --- a/cloudinit/handlers/cloud_config.py +++ b/cloudinit/handlers/cloud_config.py @@ -20,6 +20,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import jsonpatch + from cloudinit import handlers from cloudinit import log as logging from cloudinit import mergers @@ -51,6 +53,12 @@ MERGE_HEADER = 'Merge-Type' DEF_MERGERS = mergers.string_extract_mergers('dict(replace)+list()+str()') CLOUD_PREFIX = "#cloud-config" +# The file header -> content types this module will handle. +CC_TYPES = { + '#json-patch': handlers.type_from_starts_with("#json-patch"), + '#cloud-config': handlers.type_from_starts_with("#cloud-config"), +} + class CloudConfigPartHandler(handlers.Handler): def __init__(self, paths, **_kwargs): @@ -60,9 +68,7 @@ class CloudConfigPartHandler(handlers.Handler): self.file_names = [] def list_types(self): - return [ - handlers.type_from_starts_with(CLOUD_PREFIX), - ] + return list(CC_TYPES.values()) def _write_cloud_config(self): if not self.cloud_fn: @@ -108,13 +114,22 @@ class CloudConfigPartHandler(handlers.Handler): all_mergers = DEF_MERGERS return (payload_yaml, all_mergers) + def _merge_patch(self, payload): + payload = payload.lstrip() + if payload.lower().startswith("#json-patch"): + # JSON doesn't handle comments in this manner, so ensure that + # if we started with this 'type' that we remove it before + # attempting to load it as json (which the jsonpatch library will + # attempt to do). + payload = payload[len("#json-patch"):] + patch = jsonpatch.JsonPatch.from_string(payload) + LOG.debug("Merging by applying json patch %s", patch) + self.cloud_buf = patch.apply(self.cloud_buf, in_place=False) + def _merge_part(self, payload, headers): (payload_yaml, my_mergers) = self._extract_mergers(payload, headers) LOG.debug("Merging by applying %s", my_mergers) merger = mergers.construct(my_mergers) - if self.cloud_buf is None: - # First time through, merge with an empty dict... - self.cloud_buf = {} self.cloud_buf = merger.merge(self.cloud_buf, payload_yaml) def _reset(self): @@ -131,7 +146,13 @@ class CloudConfigPartHandler(handlers.Handler): self._reset() return try: - self._merge_part(payload, headers) + # First time through, merge with an empty dict... + if self.cloud_buf is None or not self.file_names: + self.cloud_buf = {} + if ctype == CC_TYPES['#json-patch']: + self._merge_patch(payload) + else: + self._merge_part(payload, headers) # Ensure filename is ok to store for i in ("\n", "\r", "\t"): filename = filename.replace(i, " ") |