cloud-init-dev team mailing list archive
-
cloud-init-dev team
-
Mailing list archive
-
Message #00474
[Merge] lp:~harlowja/cloud-init/py2-3 into lp:cloud-init
Joshua Harlow has proposed merging lp:~harlowja/cloud-init/py2-3 into lp:cloud-init.
Requested reviews:
cloud init development team (cloud-init-dev)
For more details, see:
https://code.launchpad.net/~harlowja/cloud-init/py2-3/+merge/225240
Gets the basic integration of six usage going.
This change does the following:
- Moves to using the new locations of modules (using six as needed)
- urlparse moved, configparser moved, stringio...
- Fixes the octal usage that we had previously (0o644 is the new way that works)
- The utils load_file() now decodes the files by default from binary -> utf-8 (unless decode=False, decode=False seems needed for the configobj module to correctly work)
- The utils write_file() now decodes to binary before writing (from unicode) as needed
- Adjust tests to work correctly using the new way of load/writing files
--
https://code.launchpad.net/~harlowja/cloud-init/py2-3/+merge/225240
Your team cloud init development team is requested to review the proposed merge of lp:~harlowja/cloud-init/py2-3 into lp:cloud-init.
=== modified file 'cloudinit/config/cc_debug.py'
--- cloudinit/config/cc_debug.py 2014-01-23 19:28:59 +0000
+++ cloudinit/config/cc_debug.py 2014-07-01 22:46:34 +0000
@@ -14,10 +14,12 @@
# 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 copy
+
+from six import StringIO
+
from cloudinit import type_utils
from cloudinit import util
-import copy
-from StringIO import StringIO
def _make_header(text):
=== modified file 'cloudinit/config/cc_landscape.py'
--- cloudinit/config/cc_landscape.py 2014-01-27 22:34:35 +0000
+++ cloudinit/config/cc_landscape.py 2014-07-01 22:46:34 +0000
@@ -20,7 +20,7 @@
import os
-from StringIO import StringIO
+from six import StringIO
from configobj import ConfigObj
=== modified file 'cloudinit/config/cc_mcollective.py'
--- cloudinit/config/cc_mcollective.py 2014-01-27 22:34:35 +0000
+++ cloudinit/config/cc_mcollective.py 2014-07-01 22:46:34 +0000
@@ -19,7 +19,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+from six import StringIO
# Used since this can maintain comments
# and doesn't need a top level section
@@ -81,7 +81,7 @@
contents = StringIO()
mcollective_config.write(contents)
contents = contents.getvalue()
- util.write_file(SERVER_CFG, contents, mode=0644)
+ util.write_file(SERVER_CFG, contents, mode=0o644)
# Start mcollective
util.subp(['service', 'mcollective', 'start'], capture=False)
=== modified file 'cloudinit/config/cc_puppet.py'
--- cloudinit/config/cc_puppet.py 2014-02-05 15:36:47 +0000
+++ cloudinit/config/cc_puppet.py 2014-07-01 22:46:34 +0000
@@ -18,7 +18,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+from six import StringIO
import os
import socket
=== modified file 'cloudinit/config/cc_seed_random.py'
--- cloudinit/config/cc_seed_random.py 2014-03-04 19:35:09 +0000
+++ cloudinit/config/cc_seed_random.py 2014-07-01 22:46:34 +0000
@@ -21,7 +21,8 @@
import base64
import os
-from StringIO import StringIO
+
+from six import StringIO
from cloudinit.settings import PER_INSTANCE
from cloudinit import log as logging
=== modified file 'cloudinit/distros/__init__.py'
--- cloudinit/distros/__init__.py 2014-02-12 19:56:55 +0000
+++ cloudinit/distros/__init__.py 2014-07-01 22:46:34 +0000
@@ -21,7 +21,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+from six import StringIO
import abc
import itertools
@@ -268,7 +268,7 @@
if header:
contents.write("%s\n" % (header))
contents.write("%s\n" % (eh))
- util.write_file(self.hosts_fn, contents.getvalue(), mode=0644)
+ util.write_file(self.hosts_fn, contents.getvalue(), mode=0o644)
def _bring_up_interface(self, device_name):
cmd = ['ifup', device_name]
@@ -452,7 +452,7 @@
util.make_header(base="added"),
"#includedir %s" % (path), '']
sudoers_contents = "\n".join(lines)
- util.write_file(sudo_base, sudoers_contents, 0440)
+ util.write_file(sudo_base, sudoers_contents, 0o440)
else:
lines = ['', util.make_header(base="added"),
"#includedir %s" % (path), '']
@@ -462,7 +462,7 @@
except IOError as e:
util.logexc(LOG, "Failed to write %s", sudo_base)
raise e
- util.ensure_dir(path, 0750)
+ util.ensure_dir(path, 0o750)
def write_sudo_rules(self, user, rules, sudo_file=None):
if not sudo_file:
@@ -490,7 +490,7 @@
content,
]
try:
- util.write_file(sudo_file, "\n".join(contents), 0440)
+ util.write_file(sudo_file, "\n".join(contents), 0o440)
except IOError as e:
util.logexc(LOG, "Failed to write sudoers file %s", sudo_file)
raise e
=== modified file 'cloudinit/distros/freebsd.py'
--- cloudinit/distros/freebsd.py 2014-02-28 21:40:08 +0000
+++ cloudinit/distros/freebsd.py 2014-07-01 22:46:34 +0000
@@ -16,10 +16,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
-
import re
+from six import StringIO
+
from cloudinit import distros
from cloudinit import helpers
from cloudinit import log as logging
=== modified file 'cloudinit/distros/parsers/hostname.py'
--- cloudinit/distros/parsers/hostname.py 2012-11-12 22:30:08 +0000
+++ cloudinit/distros/parsers/hostname.py 2014-07-01 22:46:34 +0000
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+from six import StringIO
from cloudinit.distros.parsers import chop_comment
=== modified file 'cloudinit/distros/parsers/hosts.py'
--- cloudinit/distros/parsers/hosts.py 2012-11-13 06:14:31 +0000
+++ cloudinit/distros/parsers/hosts.py 2014-07-01 22:46:34 +0000
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+from six import StringIO
from cloudinit.distros.parsers import chop_comment
=== modified file 'cloudinit/distros/parsers/resolv_conf.py'
--- cloudinit/distros/parsers/resolv_conf.py 2013-03-19 13:32:04 +0000
+++ cloudinit/distros/parsers/resolv_conf.py 2014-07-01 22:46:34 +0000
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+from six import StringIO
from cloudinit import util
=== modified file 'cloudinit/distros/parsers/sys_conf.py'
--- cloudinit/distros/parsers/sys_conf.py 2012-11-12 22:30:08 +0000
+++ cloudinit/distros/parsers/sys_conf.py 2014-07-01 22:46:34 +0000
@@ -16,11 +16,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
-
import pipes
import re
+import six
+
# This library is used to parse/write
# out the various sysconfig files edited (best attempt effort)
#
@@ -61,15 +61,15 @@
def __str__(self):
contents = self.write()
- out_contents = StringIO()
+ out_contents = six.StringIO()
if isinstance(contents, (list, tuple)):
out_contents.write("\n".join(contents))
else:
- out_contents.write(str(contents))
+ out_contents.write(six.text_type(contents))
return out_contents.getvalue()
def _quote(self, value, multiline=False):
- if not isinstance(value, (str, basestring)):
+ if not isinstance(value, six.string_types):
raise ValueError('Value "%s" is not a string' % (value))
if len(value) == 0:
return ''
=== modified file 'cloudinit/ec2_utils.py'
--- cloudinit/ec2_utils.py 2014-02-08 20:20:33 +0000
+++ cloudinit/ec2_utils.py 2014-07-01 22:46:34 +0000
@@ -17,7 +17,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import functools
-import httplib
import json
from cloudinit import log as logging
@@ -25,7 +24,7 @@
from cloudinit import util
LOG = logging.getLogger(__name__)
-SKIP_USERDATA_CODES = frozenset([httplib.NOT_FOUND])
+SKIP_USERDATA_CODES = frozenset([url_helper.NOT_FOUND])
def maybe_json_object(text):
=== modified file 'cloudinit/handlers/__init__.py'
--- cloudinit/handlers/__init__.py 2014-01-16 21:57:21 +0000
+++ cloudinit/handlers/__init__.py 2014-07-01 22:46:34 +0000
@@ -147,7 +147,7 @@
if not modfname.endswith(".py"):
modfname = "%s.py" % (modfname)
# TODO(harlowja): Check if path exists??
- util.write_file(modfname, payload, 0600)
+ util.write_file(modfname, payload, 0o600)
handlers = pdata['handlers']
try:
mod = fixup_handler(importer.import_module(modname))
=== modified file 'cloudinit/handlers/boot_hook.py'
--- cloudinit/handlers/boot_hook.py 2013-07-21 16:34:26 +0000
+++ cloudinit/handlers/boot_hook.py 2014-07-01 22:46:34 +0000
@@ -50,7 +50,7 @@
filepath = os.path.join(self.boothook_dir, filename)
contents = util.strip_prefix_suffix(util.dos2unix(payload),
prefix=BOOTHOOK_PREFIX)
- util.write_file(filepath, contents.lstrip(), 0700)
+ util.write_file(filepath, contents.lstrip(), 0o700)
return filepath
def handle_part(self, _data, ctype, filename, # pylint: disable=W0221
=== modified file 'cloudinit/handlers/cloud_config.py'
--- cloudinit/handlers/cloud_config.py 2014-01-09 00:16:24 +0000
+++ cloudinit/handlers/cloud_config.py 2014-07-01 22:46:34 +0000
@@ -95,7 +95,7 @@
lines.append(util.yaml_dumps(self.cloud_buf))
else:
lines = []
- util.write_file(self.cloud_fn, "\n".join(lines), 0600)
+ util.write_file(self.cloud_fn, "\n".join(lines), 0o600)
def _extract_mergers(self, payload, headers):
merge_header_headers = ''
=== modified file 'cloudinit/handlers/shell_script.py'
--- cloudinit/handlers/shell_script.py 2014-01-09 00:16:24 +0000
+++ cloudinit/handlers/shell_script.py 2014-07-01 22:46:34 +0000
@@ -53,4 +53,4 @@
filename = util.clean_filename(filename)
payload = util.dos2unix(payload)
path = os.path.join(self.script_dir, filename)
- util.write_file(path, payload, 0700)
+ util.write_file(path, payload, 0o700)
=== modified file 'cloudinit/handlers/upstart_job.py'
--- cloudinit/handlers/upstart_job.py 2013-07-21 16:26:44 +0000
+++ cloudinit/handlers/upstart_job.py 2014-07-01 22:46:34 +0000
@@ -66,7 +66,7 @@
payload = util.dos2unix(payload)
path = os.path.join(self.upstart_dir, filename)
- util.write_file(path, payload, 0644)
+ util.write_file(path, payload, 0o644)
if SUITABLE_UPSTART:
util.subp(["initctl", "reload-configuration"], capture=False)
=== modified file 'cloudinit/helpers.py'
--- cloudinit/helpers.py 2014-01-17 20:12:31 +0000
+++ cloudinit/helpers.py 2014-07-01 22:46:34 +0000
@@ -26,7 +26,9 @@
import io
import os
-from ConfigParser import (NoSectionError, NoOptionError, RawConfigParser)
+from six.moves.configparser import (NoSectionError,
+ NoOptionError,
+ RawConfigParser)
from cloudinit.settings import (PER_INSTANCE, PER_ALWAYS, PER_ONCE,
CFG_ENV_NAME)
=== modified file 'cloudinit/log.py'
--- cloudinit/log.py 2013-04-17 16:42:55 +0000
+++ cloudinit/log.py 2014-07-01 22:46:34 +0000
@@ -28,7 +28,7 @@
import os
import sys
-from StringIO import StringIO
+from six import StringIO
# Logging levels for easy access
CRITICAL = logging.CRITICAL
=== modified file 'cloudinit/signal_handler.py'
--- cloudinit/signal_handler.py 2012-09-19 20:33:56 +0000
+++ cloudinit/signal_handler.py 2014-07-01 22:46:34 +0000
@@ -22,7 +22,7 @@
import signal
import sys
-from StringIO import StringIO
+from six import StringIO
from cloudinit import log as logging
from cloudinit import util
=== modified file 'cloudinit/ssh_util.py'
--- cloudinit/ssh_util.py 2013-06-19 06:44:00 +0000
+++ cloudinit/ssh_util.py 2014-07-01 22:46:34 +0000
@@ -239,7 +239,7 @@
# Make sure the users .ssh dir is setup accordingly
(ssh_dir, pwent) = users_ssh_info(username)
if not os.path.isdir(ssh_dir):
- util.ensure_dir(ssh_dir, mode=0700)
+ util.ensure_dir(ssh_dir, mode=0o700)
util.chownbyid(ssh_dir, pwent.pw_uid, pwent.pw_gid)
# Turn the 'update' keys given into actual entries
@@ -252,8 +252,8 @@
(auth_key_fn, auth_key_entries) = extract_authorized_keys(username)
with util.SeLinuxGuard(ssh_dir, recursive=True):
content = update_authorized_keys(auth_key_entries, key_entries)
- util.ensure_dir(os.path.dirname(auth_key_fn), mode=0700)
- util.write_file(auth_key_fn, content, mode=0600)
+ util.ensure_dir(os.path.dirname(auth_key_fn), mode=0o700)
+ util.write_file(auth_key_fn, content, mode=0o600)
util.chownbyid(auth_key_fn, pwent.pw_uid, pwent.pw_gid)
=== modified file 'cloudinit/stages.py'
--- cloudinit/stages.py 2014-02-13 18:53:08 +0000
+++ cloudinit/stages.py 2014-07-01 22:46:34 +0000
@@ -20,10 +20,9 @@
# 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 cPickle as pickle
-
import copy
import os
+import pickle
import sys
from cloudinit.settings import (PER_INSTANCE, FREQUENCIES, CLOUD_CONFIG)
@@ -202,7 +201,7 @@
util.logexc(LOG, "Failed pickling datasource %s", self.datasource)
return False
try:
- util.write_file(pickled_fn, pk_contents, mode=0400)
+ util.write_file(pickled_fn, pk_contents, mode=0o400)
except Exception:
util.logexc(LOG, "Failed pickling datasource to %s", pickled_fn)
return False
@@ -324,15 +323,15 @@
def _store_userdata(self):
raw_ud = "%s" % (self.datasource.get_userdata_raw())
- util.write_file(self._get_ipath('userdata_raw'), raw_ud, 0600)
+ util.write_file(self._get_ipath('userdata_raw'), raw_ud, 0o600)
processed_ud = "%s" % (self.datasource.get_userdata())
- util.write_file(self._get_ipath('userdata'), processed_ud, 0600)
+ util.write_file(self._get_ipath('userdata'), processed_ud, 0o600)
def _store_vendordata(self):
raw_vd = "%s" % (self.datasource.get_vendordata_raw())
- util.write_file(self._get_ipath('vendordata_raw'), raw_vd, 0600)
+ util.write_file(self._get_ipath('vendordata_raw'), raw_vd, 0o600)
processed_vd = "%s" % (self.datasource.get_vendordata())
- util.write_file(self._get_ipath('vendordata'), processed_vd, 0600)
+ util.write_file(self._get_ipath('vendordata'), processed_vd, 0o600)
def _default_handlers(self, opts=None):
if opts is None:
=== modified file 'cloudinit/url_helper.py'
--- cloudinit/url_helper.py 2014-02-13 17:13:42 +0000
+++ cloudinit/url_helper.py 2014-07-01 22:46:34 +0000
@@ -20,21 +20,28 @@
# 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 httplib
+import six
+
import time
import urllib
import requests
from requests import exceptions
-from urlparse import (urlparse, urlunparse)
+from six.moves.urllib.parse import (urlparse, urlunparse)
from cloudinit import log as logging
from cloudinit import version
LOG = logging.getLogger(__name__)
-NOT_FOUND = httplib.NOT_FOUND
+if six.PY2:
+ import httplib
+ NOT_FOUND = httplib.NOT_FOUND
+else:
+ import http.client
+ NOT_FOUND = http.client.NOT_FOUND
+
# Check if requests has ssl support (added in requests >= 0.8.8)
SSL_ENABLED = False
=== modified file 'cloudinit/user_data.py'
--- cloudinit/user_data.py 2014-01-24 20:29:09 +0000
+++ cloudinit/user_data.py 2014-07-01 22:46:34 +0000
@@ -235,7 +235,7 @@
resp = util.read_file_or_url(include_url,
ssl_details=self.ssl_details)
if include_once_on and resp.ok():
- util.write_file(include_once_fn, str(resp), mode=0600)
+ util.write_file(include_once_fn, str(resp), mode=0o600)
if resp.ok():
content = str(resp)
else:
=== modified file 'cloudinit/util.py'
--- cloudinit/util.py 2014-02-24 22:20:12 +0000
+++ cloudinit/util.py 2014-07-01 22:46:34 +0000
@@ -22,8 +22,6 @@
#
# pylint: disable=C0302
-from StringIO import StringIO
-
import contextlib
import copy as obj_copy
import ctypes
@@ -47,8 +45,10 @@
import sys
import tempfile
import time
-import urlparse
-
+
+from six.moves.urllib import parse as urlparse
+
+import six
import yaml
from cloudinit import importer
@@ -71,8 +71,25 @@
}
FN_ALLOWED = ('_-.()' + string.digits + string.ascii_letters)
+TRUE_STRINGS = ('true', '1', 'on', 'yes')
+FALSE_STRINGS = ('off', '0', 'no', 'false')
+
# Helper utils to see if running in a container
-CONTAINER_TESTS = ['running-in-container', 'lxc-is-container']
+CONTAINER_TESTS = ('running-in-container', 'lxc-is-container')
+
+
+def decode_binary(blob, encoding='utf-8'):
+ # Converts a binary type into a text type using given encoding.
+ if isinstance(blob, six.text_type):
+ return blob
+ return blob.decode(encoding)
+
+
+def encode_text(text, encoding='utf-8'):
+ # Converts a text string into a binary type using given encoding.
+ if isinstance(text, six.binary_type):
+ return text
+ return text.encode(encoding)
class ProcessExecutionError(IOError):
@@ -149,7 +166,8 @@
if self.selinux and self.selinux.is_selinux_enabled():
path = os.path.realpath(os.path.expanduser(self.path))
# path should be a string, not unicode
- path = str(path)
+ if six.PY2:
+ path = str(path)
do_restore = False
try:
# See if even worth restoring??
@@ -211,10 +229,10 @@
def is_true(val, addons=None):
if isinstance(val, (bool)):
return val is True
- check_set = ['true', '1', 'on', 'yes']
+ check_set = TRUE_STRINGS
if addons:
- check_set = check_set + addons
- if str(val).lower().strip() in check_set:
+ check_set = list(check_set) + addons
+ if six.text_type(val).lower().strip() in check_set:
return True
return False
@@ -222,10 +240,10 @@
def is_false(val, addons=None):
if isinstance(val, (bool)):
return val is False
- check_set = ['off', '0', 'no', 'false']
+ check_set = FALSE_STRINGS
if addons:
- check_set = check_set + addons
- if str(val).lower().strip() in check_set:
+ check_set = list(check_set) + addons
+ if six.text_type(val).lower().strip() in check_set:
return True
return False
@@ -275,7 +293,7 @@
def uniq_merge(*lists):
combined_list = []
for a_list in lists:
- if isinstance(a_list, (str, basestring)):
+ if isinstance(a_list, six.string_types):
a_list = a_list.strip().split(",")
# Kickout the empty ones
a_list = [a for a in a_list if len(a)]
@@ -298,14 +316,14 @@
def decomp_gzip(data, quiet=True):
try:
- buf = StringIO(str(data))
+ buf = six.BytesIO(encode_text(data))
with contextlib.closing(gzip.GzipFile(None, "rb", 1, buf)) as gh:
- return gh.read()
+ return decode_binary(gh.read())
except Exception as e:
if quiet:
return data
else:
- raise DecompressionError(str(e))
+ raise DecompressionError(six.text_type(e))
def extract_usergroup(ug_pair):
@@ -364,7 +382,7 @@
def load_json(text, root_types=(dict,)):
- decoded = json.loads(text)
+ decoded = json.loads(decode_binary(text))
if not isinstance(decoded, tuple(root_types)):
expected_types = ", ".join([str(t) for t in root_types])
raise TypeError("(%s) root types expected, got %s instead"
@@ -396,7 +414,7 @@
if key not in yobj:
return default
val = yobj[key]
- if not isinstance(val, (str, basestring)):
+ if not isinstance(val, six.string_types):
val = str(val)
return val
@@ -431,7 +449,7 @@
if isinstance(val, (list)):
cval = [v for v in val]
return cval
- if not isinstance(val, (basestring)):
+ if not isinstance(val, six.string_types):
val = str(val)
return [val]
@@ -706,11 +724,11 @@
def load_yaml(blob, default=None, allowed=(dict,)):
loaded = default
+ blob = decode_binary(blob)
try:
- blob = str(blob)
- LOG.debug(("Attempting to load yaml from string "
- "of length %s with allowed root types %s"),
- len(blob), allowed)
+ LOG.debug("Attempting to load yaml from string "
+ "of length %s with allowed root types %s",
+ len(blob), allowed)
converted = safeyaml.load(blob)
if not isinstance(converted, allowed):
# Yes this will just be caught, but thats ok for now...
@@ -782,7 +800,7 @@
if "conf_d" in cfg:
confd = cfg['conf_d']
if confd:
- if not isinstance(confd, (str, basestring)):
+ if not isinstance(confd, six.string_types):
raise TypeError(("Config file %s contains 'conf_d' "
"with non-string type %s") %
(cfgfile, type_utils.obj_name(confd)))
@@ -1074,9 +1092,9 @@
return out_list
-def load_file(fname, read_cb=None, quiet=False):
+def load_file(fname, read_cb=None, quiet=False, decode=True):
LOG.debug("Reading from %s (quiet=%s)", fname, quiet)
- ofh = StringIO()
+ ofh = six.BytesIO()
try:
with open(fname, 'rb') as ifh:
pipe_in_out(ifh, ofh, chunk_cb=read_cb)
@@ -1087,7 +1105,10 @@
raise
contents = ofh.getvalue()
LOG.debug("Read %s bytes from %s", len(contents), fname)
- return contents
+ if decode:
+ return decode_binary(contents)
+ else:
+ return contents
def get_cmdline():
@@ -1217,7 +1238,7 @@
def hash_blob(blob, routine, mlen=None):
hasher = hashlib.new(routine)
- hasher.update(blob)
+ hasher.update(encode_text(blob))
digest = hasher.hexdigest()
# Don't get to long now
if mlen is not None:
@@ -1248,7 +1269,7 @@
os.rename(src, dest)
-def ensure_dirs(dirlist, mode=0755):
+def ensure_dirs(dirlist, mode=0o755):
for d in dirlist:
ensure_dir(d, mode)
@@ -1262,7 +1283,7 @@
return
try:
if key and content:
- write_file(target_fn, content, mode=0600)
+ write_file(target_fn, content, mode=0o600)
LOG.debug(("Wrote to %s with contents of command line"
" url %s (len=%s)"), target_fn, url, len(content))
elif key and not content:
@@ -1454,7 +1475,7 @@
write_file(path, content, omode="ab", mode=None)
-def ensure_file(path, mode=0644):
+def ensure_file(path, mode=0o644):
write_file(path, content='', omode="ab", mode=mode)
@@ -1472,7 +1493,7 @@
os.chmod(path, real_mode)
-def write_file(filename, content, mode=0644, omode="wb"):
+def write_file(filename, content, mode=0o644, omode="wb"):
"""
Writes a file with the given content and sets the file mode as specified.
Resotres the SELinux context if possible.
@@ -1480,11 +1501,17 @@
@param filename: The full path of the file to write.
@param content: The content to write to the file.
@param mode: The filesystem mode to set on the file.
- @param omode: The open mode used when opening the file (r, rb, a, etc.)
+ @param omode: The open mode used when opening the file (w, wb, a, etc.)
"""
ensure_dir(os.path.dirname(filename))
- LOG.debug("Writing to %s - %s: [%s] %s bytes",
- filename, omode, mode, len(content))
+ if 'b' in omode.lower():
+ content = encode_text(content)
+ write_type = 'bytes'
+ else:
+ content = decode_binary(content)
+ write_type = 'characters'
+ LOG.debug("Writing to %s - %s: [%s] %s %s",
+ filename, omode, mode, len(content), write_type)
with SeLinuxGuard(path=filename):
with open(filename, omode) as fh:
fh.write(content)
@@ -1573,10 +1600,10 @@
if isinstance(args, list):
fixed = []
for f in args:
- fixed.append("'%s'" % (str(f).replace("'", escaped)))
+ fixed.append("'%s'" % (six.text_type(f).replace("'", escaped)))
content = "%s%s\n" % (content, ' '.join(fixed))
cmds_made += 1
- elif isinstance(args, (str, basestring)):
+ elif isinstance(args, six.string_types):
content = "%s%s\n" % (content, args)
cmds_made += 1
else:
@@ -1687,7 +1714,7 @@
pkglist = []
for pkg in pkgs:
- if isinstance(pkg, basestring):
+ if isinstance(pkg, six.string_types):
pkglist.append(pkg)
continue
=== modified file 'packages/bddeb'
--- packages/bddeb 2014-01-17 22:08:58 +0000
+++ packages/bddeb 2014-07-01 22:46:34 +0000
@@ -37,6 +37,7 @@
'pyserial': 'python-serial',
'pyyaml': 'python-yaml',
'requests': 'python-requests',
+ 'six': 'python-six',
}
DEBUILD_ARGS = ["-us", "-S", "-uc", "-d"]
=== modified file 'packages/brpm'
--- packages/brpm 2014-01-17 22:08:58 +0000
+++ packages/brpm 2014-07-01 22:46:34 +0000
@@ -44,6 +44,7 @@
'pyserial': 'pyserial',
'pyyaml': 'PyYAML',
'requests': 'python-requests',
+ 'six': 'python-six',
},
'suse': {
'argparse': 'python-argparse',
@@ -55,6 +56,7 @@
'pyserial': 'python-pyserial',
'pyyaml': 'python-yaml',
'requests': 'python-requests',
+ 'six': 'python-six',
}
}
=== modified file 'requirements.txt'
--- requirements.txt 2014-02-12 10:14:49 +0000
+++ requirements.txt 2014-07-01 22:46:34 +0000
@@ -31,3 +31,6 @@
# For patching pieces of cloud-config together
jsonpatch
+
+# For py2/3 simultaneous compatibility
+six
=== modified file 'tests/unittests/test_data.py'
--- tests/unittests/test_data.py 2014-01-17 15:27:09 +0000
+++ tests/unittests/test_data.py 2014-07-01 22:46:34 +0000
@@ -1,11 +1,12 @@
"""Tests for handling of userdata within cloud init."""
-import StringIO
-
import gzip
import logging
import os
+from six import BytesIO
+from six import StringIO
+
from email.mime.application import MIMEApplication
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
@@ -53,7 +54,7 @@
self.patchUtils(root)
def capture_log(self, lvl=logging.DEBUG):
- log_file = StringIO.StringIO()
+ log_file = StringIO()
self._log_handler = logging.StreamHandler(log_file)
self._log_handler.setLevel(lvl)
self._log = log.getLogger()
@@ -352,9 +353,9 @@
"""Tests that individual message gzip encoding works."""
def gzip_part(text):
- contents = StringIO.StringIO()
- f = gzip.GzipFile(fileobj=contents, mode='w')
- f.write(str(text))
+ contents = BytesIO()
+ f = gzip.GzipFile(fileobj=contents, mode='wb')
+ f.write(util.encode_text(text))
f.flush()
f.close()
return MIMEApplication(contents.getvalue(), 'gzip')
=== modified file 'tests/unittests/test_datasource/test_nocloud.py'
--- tests/unittests/test_datasource/test_nocloud.py 2014-02-26 19:28:46 +0000
+++ tests/unittests/test_datasource/test_nocloud.py 2014-07-01 22:46:34 +0000
@@ -86,7 +86,7 @@
data = {
'fs_label': None,
- 'meta-data': {'instance-id': 'IID'},
+ 'meta-data': yaml.safe_dump({'instance-id': 'IID'}),
'user-data': "USER_DATA_RAW",
}
=== modified file 'tests/unittests/test_datasource/test_openstack.py'
--- tests/unittests/test_datasource/test_openstack.py 2014-02-08 20:20:33 +0000
+++ tests/unittests/test_datasource/test_openstack.py 2014-07-01 22:46:34 +0000
@@ -20,9 +20,8 @@
import json
import re
-from StringIO import StringIO
-
-from urlparse import urlparse
+from six import StringIO
+from six.moves.urllib.parse import urlparse
from tests.unittests import helpers as test_helpers
=== modified file 'tests/unittests/test_distros/test_netconfig.py'
--- tests/unittests/test_distros/test_netconfig.py 2012-10-11 19:49:45 +0000
+++ tests/unittests/test_distros/test_netconfig.py 2014-07-01 22:46:34 +0000
@@ -4,6 +4,8 @@
import os
+from six import StringIO
+
from cloudinit import distros
from cloudinit import helpers
from cloudinit import settings
@@ -11,9 +13,6 @@
from cloudinit.distros.parsers.sys_conf import SysConf
-from StringIO import StringIO
-
-
BASE_NET_CFG = '''
auto lo
iface lo inet loopback
=== modified file 'tests/unittests/test_handler/test_handler_locale.py'
--- tests/unittests/test_handler/test_handler_locale.py 2013-06-25 06:57:27 +0000
+++ tests/unittests/test_handler/test_handler_locale.py 2014-07-01 22:46:34 +0000
@@ -29,7 +29,7 @@
from configobj import ConfigObj
-from StringIO import StringIO
+from six import BytesIO
import logging
@@ -59,6 +59,6 @@
cc = self._get_cloud('sles')
cc_locale.handle('cc_locale', cfg, cc, LOG, [])
- contents = util.load_file('/etc/sysconfig/language')
- n_cfg = ConfigObj(StringIO(contents))
+ contents = util.load_file('/etc/sysconfig/language', decode=False)
+ n_cfg = ConfigObj(BytesIO(contents))
self.assertEquals({'RC_LANG': cfg['locale']}, dict(n_cfg))
=== modified file 'tests/unittests/test_handler/test_handler_seed_random.py'
--- tests/unittests/test_handler/test_handler_seed_random.py 2014-03-04 19:35:09 +0000
+++ tests/unittests/test_handler/test_handler_seed_random.py 2014-07-01 22:46:34 +0000
@@ -22,7 +22,7 @@
import gzip
import tempfile
-from StringIO import StringIO
+from six import StringIO
from cloudinit import cloud
from cloudinit import distros
=== modified file 'tests/unittests/test_handler/test_handler_set_hostname.py'
--- tests/unittests/test_handler/test_handler_set_hostname.py 2013-06-25 06:57:27 +0000
+++ tests/unittests/test_handler/test_handler_set_hostname.py 2014-07-01 22:46:34 +0000
@@ -9,7 +9,7 @@
import logging
-from StringIO import StringIO
+from six import BytesIO
from configobj import ConfigObj
@@ -37,8 +37,8 @@
self.patchUtils(self.tmp)
cc_set_hostname.handle('cc_set_hostname',
cfg, cc, LOG, [])
- contents = util.load_file("/etc/sysconfig/network")
- n_cfg = ConfigObj(StringIO(contents))
+ contents = util.load_file("/etc/sysconfig/network", decode=False)
+ n_cfg = ConfigObj(BytesIO(contents))
self.assertEquals({'HOSTNAME': 'blah.blah.blah.yahoo.com'},
dict(n_cfg))
=== modified file 'tests/unittests/test_handler/test_handler_timezone.py'
--- tests/unittests/test_handler/test_handler_timezone.py 2013-06-25 06:57:27 +0000
+++ tests/unittests/test_handler/test_handler_timezone.py 2014-07-01 22:46:34 +0000
@@ -29,7 +29,7 @@
from configobj import ConfigObj
-from StringIO import StringIO
+from six import BytesIO
import logging
@@ -67,8 +67,8 @@
cc_timezone.handle('cc_timezone', cfg, cc, LOG, [])
- contents = util.load_file('/etc/sysconfig/clock')
- n_cfg = ConfigObj(StringIO(contents))
+ contents = util.load_file('/etc/sysconfig/clock', decode=False)
+ n_cfg = ConfigObj(BytesIO(contents))
self.assertEquals({'TIMEZONE': cfg['timezone']}, dict(n_cfg))
contents = util.load_file('/etc/localtime')
=== modified file 'tests/unittests/test_handler/test_handler_yum_add_repo.py'
--- tests/unittests/test_handler/test_handler_yum_add_repo.py 2014-04-01 18:20:57 +0000
+++ tests/unittests/test_handler/test_handler_yum_add_repo.py 2014-07-01 22:46:34 +0000
@@ -6,7 +6,7 @@
import logging
-from StringIO import StringIO
+from six import BytesIO
import configobj
@@ -52,8 +52,9 @@
}
self.patchUtils(self.tmp)
cc_yum_add_repo.handle('yum_add_repo', cfg, None, LOG, [])
- contents = util.load_file("/etc/yum.repos.d/epel_testing.repo")
- contents = configobj.ConfigObj(StringIO(contents))
+ contents = util.load_file("/etc/yum.repos.d/epel_testing.repo",
+ decode=False)
+ contents = configobj.ConfigObj(BytesIO(contents))
expected = {
'epel_testing': {
'name': 'Extra Packages for Enterprise Linux 5 - Testing',
Follow ups