summaryrefslogtreecommitdiff
path: root/cloudinit/cmd/query.py
diff options
context:
space:
mode:
authorChad Smith <chad.smith@canonical.com>2020-08-20 15:51:35 -0600
committerGitHub <noreply@github.com>2020-08-20 15:51:35 -0600
commit747723a42c98fa13080ea31127e289e7b826046f (patch)
treefdfece56467d4a75f6266ffa5c628777863fe1cf /cloudinit/cmd/query.py
parentd941c7f7846e0216873384044d20f4b6723697c5 (diff)
downloadvyos-cloud-init-747723a42c98fa13080ea31127e289e7b826046f.tar.gz
vyos-cloud-init-747723a42c98fa13080ea31127e289e7b826046f.zip
cmd: cloud-init query to handle compressed userdata (#516)
cloud-init query tries to directly load and decode raw user-data from /var/lib/cloud/instance/user-data.txt. This results in UnicodeDecodeErrors on some platforms which provide compressed content. Avoid UnicodeDecoderErrors when parsing compressed user-data at /var/lib/cloud/instance/user-data.txt. LP: #1889938
Diffstat (limited to 'cloudinit/cmd/query.py')
-rw-r--r--cloudinit/cmd/query.py43
1 files changed, 37 insertions, 6 deletions
diff --git a/cloudinit/cmd/query.py b/cloudinit/cmd/query.py
index 0fb48ebd..03b77c17 100644
--- a/cloudinit/cmd/query.py
+++ b/cloudinit/cmd/query.py
@@ -1,6 +1,17 @@
# This file is part of cloud-init. See LICENSE file for license information.
-"""Query standardized instance metadata from the command line."""
+"""Query standardized instance metadata provided to machine, returning a JSON
+structure.
+
+Some instance-data values may be binary on some platforms, such as userdata and
+vendordata. Attempt to decompress and decode UTF-8 any binary values.
+
+Any binary values in the instance metadata will be base64-encoded and prefixed
+with "ci-b64:" in the output. userdata and, where applicable, vendordata may
+be provided to the machine gzip-compressed (and therefore as binary data).
+query will attempt to decompress these to a string before emitting the JSON
+output; if this fails, they are treated as binary.
+"""
import argparse
from errno import EACCES
@@ -30,7 +41,7 @@ def get_parser(parser=None):
"""
if not parser:
parser = argparse.ArgumentParser(
- prog=NAME, description='Query cloud-init instance data')
+ prog=NAME, description=__doc__)
parser.add_argument(
'-d', '--debug', action='store_true', default=False,
help='Add verbose messages during template render')
@@ -52,8 +63,10 @@ def get_parser(parser=None):
' /var/lib/cloud/instance/vendor-data.txt'))
parser.add_argument(
'varname', type=str, nargs='?',
- help=('A dot-delimited instance data variable to query from'
- ' instance-data query. For example: v2.local_hostname'))
+ help=('A dot-delimited specific variable to query from'
+ ' instance-data. For example: v1.local_hostname. If the'
+ ' value is not JSON serializable, it will be base64-encoded and'
+ ' will contain the prefix "ci-b64:". '))
parser.add_argument(
'-a', '--all', action='store_true', default=False, dest='dump_all',
help='Dump all available instance-data')
@@ -65,6 +78,24 @@ def get_parser(parser=None):
return parser
+def load_userdata(ud_file_path):
+ """Attempt to return a string of user-data from ud_file_path
+
+ Attempt to decode or decompress if needed.
+ If unable to decode the content, raw bytes will be returned.
+
+ @returns: String of uncompressed userdata if possible, otherwise bytes.
+ """
+ try:
+ return util.load_file(ud_file_path)
+ except UnicodeDecodeError:
+ encoded_data = util.load_file(ud_file_path, decode=False)
+ try:
+ return util.decomp_gzip(encoded_data, quiet=False)
+ except util.DecompressionError:
+ return encoded_data
+
+
def handle_args(name, args):
"""Handle calls to 'cloud-init query' as a subcommand."""
paths = None
@@ -121,8 +152,8 @@ def handle_args(name, args):
instance_data['vendordata'] = (
'<%s> file:%s' % (REDACT_SENSITIVE_VALUE, vendor_data_fn))
else:
- instance_data['userdata'] = util.load_file(user_data_fn)
- instance_data['vendordata'] = util.load_file(vendor_data_fn)
+ instance_data['userdata'] = load_userdata(user_data_fn)
+ instance_data['vendordata'] = load_userdata(vendor_data_fn)
if args.format:
payload = '## template: jinja\n{fmt}'.format(fmt=args.format)
rendered_payload = render_jinja_payload(