composer-cli: Update diff support for customizations and repos.git
composer-cli will now output information about changes to customizations entries and the repos.git entries.
This commit is contained in:
parent
9011a564e8
commit
932ff5812c
@ -187,62 +187,129 @@ def blueprints_diff(socket_path, api_version, args, show_json=False):
|
|||||||
return rc
|
return rc
|
||||||
|
|
||||||
for diff in result["diff"]:
|
for diff in result["diff"]:
|
||||||
print(prettyDiffEntry(diff))
|
print(pretty_diff_entry(diff))
|
||||||
|
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
def prettyDiffEntry(diff):
|
def pretty_dict(d):
|
||||||
|
"""Return the dict as a human readable single line
|
||||||
|
|
||||||
|
:param d: key/values
|
||||||
|
:type d: dict
|
||||||
|
:returns: String of the dict's keys and values
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
key="str", key="str1,str2", ...
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
for k in d:
|
||||||
|
if type(d[k]) == type(""):
|
||||||
|
result.append('%s="%s"' % (k, d[k]))
|
||||||
|
elif type(d[k]) == type([]) and type(d[k][0]) == type(""):
|
||||||
|
result.append('%s="%s"' % (k, ", ".join(d[k])))
|
||||||
|
elif type(d[k]) == type([]) and type(d[k][0]) == type({}):
|
||||||
|
result.append('%s="%s"' % (k, pretty_dict(d[k])))
|
||||||
|
return " ".join(result)
|
||||||
|
|
||||||
|
def dict_names(lst):
|
||||||
|
"""Return comma-separated list of the dict's name/user fields
|
||||||
|
|
||||||
|
:param d: key/values
|
||||||
|
:type d: dict
|
||||||
|
:returns: String of the dict's keys and values
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
root, norm
|
||||||
|
"""
|
||||||
|
if "user" in lst[0]:
|
||||||
|
field_name = "user"
|
||||||
|
elif "name" in lst[0]:
|
||||||
|
field_name = "name"
|
||||||
|
else:
|
||||||
|
# Use first fields in sorted keys
|
||||||
|
field_name = sorted(lst[0].keys())[0]
|
||||||
|
|
||||||
|
return ", ".join(d[field_name] for d in lst)
|
||||||
|
|
||||||
|
def pretty_diff_entry(diff):
|
||||||
"""Generate nice diff entry string.
|
"""Generate nice diff entry string.
|
||||||
|
|
||||||
:param diff: Difference entry dict
|
:param diff: Difference entry dict
|
||||||
:type diff: dict
|
:type diff: dict
|
||||||
:returns: Nice string
|
:returns: Nice string
|
||||||
"""
|
"""
|
||||||
def change(diff):
|
|
||||||
if diff["old"] and diff["new"]:
|
if diff["old"] and diff["new"]:
|
||||||
return "Changed"
|
change = "Changed"
|
||||||
elif diff["new"] and not diff["old"]:
|
elif diff["new"] and not diff["old"]:
|
||||||
return "Added"
|
change = "Added"
|
||||||
elif diff["old"] and not diff["new"]:
|
elif diff["old"] and not diff["new"]:
|
||||||
return "Removed"
|
change = "Removed"
|
||||||
else:
|
else:
|
||||||
return "Unknown"
|
change = "Unknown"
|
||||||
|
|
||||||
def name(diff):
|
|
||||||
if diff["old"]:
|
if diff["old"]:
|
||||||
return list(diff["old"].keys())[0]
|
name = list(diff["old"].keys())[0]
|
||||||
elif diff["new"]:
|
elif diff["new"]:
|
||||||
return list(diff["new"].keys())[0]
|
name = list(diff["new"].keys())[0]
|
||||||
else:
|
else:
|
||||||
return "Unknown"
|
name = "Unknown"
|
||||||
|
|
||||||
def details(diff):
|
def details(diff):
|
||||||
if change(diff) == "Changed":
|
if change == "Changed":
|
||||||
if name(diff) == "Description":
|
if type(diff["old"][name]) == type(""):
|
||||||
return '"%s" -> "%s"' % (diff["old"][name(diff)], diff["new"][name(diff)])
|
if name == "Description" or " " in diff["old"][name]:
|
||||||
elif name(diff) == "Version":
|
return '"%s" -> "%s"' % (diff["old"][name], diff["new"][name])
|
||||||
return "%s -> %s" % (diff["old"][name(diff)], diff["new"][name(diff)])
|
else:
|
||||||
elif name(diff) in ["Module", "Package"]:
|
return "%s -> %s" % (diff["old"][name], diff["new"][name])
|
||||||
return "%s %s -> %s" % (diff["old"][name(diff)]["name"], diff["old"][name(diff)]["version"],
|
elif name in ["Module", "Package"]:
|
||||||
diff["new"][name(diff)]["version"])
|
return "%s %s -> %s" % (diff["old"][name]["name"], diff["old"][name]["version"],
|
||||||
|
diff["new"][name]["version"])
|
||||||
|
elif type(diff["old"][name]) == type([]):
|
||||||
|
if type(diff["old"][name][0]) == type(""):
|
||||||
|
return "%s -> %s" % (" ".join(diff["old"][name]), " ".join(diff["new"][name]))
|
||||||
|
elif type(diff["old"][name][0]) == type({}):
|
||||||
|
# Lists of dicts are too long to display in detail, just show their names
|
||||||
|
return "%s -> %s" % (dict_names(diff["old"][name]), dict_names(diff["new"][name]))
|
||||||
|
elif type(diff["old"][name]) == type({}):
|
||||||
|
return "%s -> %s" % (pretty_dict(diff["old"][name]), pretty_dict(diff["new"][name]))
|
||||||
else:
|
else:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
elif change(diff) == "Added":
|
elif change == "Added":
|
||||||
if name(diff) in ["Module", "Package"]:
|
if name in ["Module", "Package"]:
|
||||||
return "%s %s" % (diff["new"][name(diff)]["name"], diff["new"][name(diff)]["version"])
|
return "%s %s" % (diff["new"][name]["name"], diff["new"][name]["version"])
|
||||||
elif name(diff) in ["Group"]:
|
elif name in ["Group"]:
|
||||||
return diff["new"][name(diff)]["name"]
|
return diff["new"][name]["name"]
|
||||||
|
elif type(diff["new"][name]) == type(""):
|
||||||
|
return diff["new"][name]
|
||||||
|
elif type(diff["new"][name]) == type([]):
|
||||||
|
if type(diff["new"][name][0]) == type(""):
|
||||||
|
return " ".join(diff["new"][name])
|
||||||
|
elif type(diff["new"][name][0]) == type({}):
|
||||||
|
# Lists of dicts are too long to display in detail, just show their names
|
||||||
|
return dict_names(diff["new"][name])
|
||||||
|
elif type(diff["new"][name]) == type({}):
|
||||||
|
return pretty_dict(diff["new"][name])
|
||||||
else:
|
else:
|
||||||
return " ".join([diff["new"][k] for k in diff["new"]])
|
return "unknown/todo: %s" % type(diff["new"][name])
|
||||||
elif change(diff) == "Removed":
|
elif change == "Removed":
|
||||||
if name(diff) in ["Module", "Package"]:
|
if name in ["Module", "Package"]:
|
||||||
return "%s %s" % (diff["old"][name(diff)]["name"], diff["old"][name(diff)]["version"])
|
return "%s %s" % (diff["old"][name]["name"], diff["old"][name]["version"])
|
||||||
elif name(diff) in ["Group"]:
|
elif name in ["Group"]:
|
||||||
return diff["old"][name(diff)]["name"]
|
return diff["old"][name]["name"]
|
||||||
|
elif type(diff["old"][name]) == type(""):
|
||||||
|
return diff["old"][name]
|
||||||
|
elif type(diff["old"][name]) == type([]):
|
||||||
|
if type(diff["old"][name][0]) == type(""):
|
||||||
|
return " ".join(diff["old"][name])
|
||||||
|
elif type(diff["old"][name][0]) == type({}):
|
||||||
|
# Lists of dicts are too long to display in detail, just show their names
|
||||||
|
return dict_names(diff["old"][name])
|
||||||
|
elif type(diff["old"][name]) == type({}):
|
||||||
|
return pretty_dict(diff["old"][name])
|
||||||
else:
|
else:
|
||||||
return " ".join([diff["old"][k] for k in diff["old"]])
|
return "unknown/todo: %s" % type(diff["new"][name])
|
||||||
|
|
||||||
return change(diff) + " " + name(diff) + " " + details(diff)
|
return change + " " + name + " " + details(diff)
|
||||||
|
|
||||||
def blueprints_save(socket_path, api_version, args, show_json=False):
|
def blueprints_save(socket_path, api_version, args, show_json=False):
|
||||||
"""Save the blueprint to a TOML file
|
"""Save the blueprint to a TOML file
|
||||||
|
@ -19,9 +19,10 @@ import unittest
|
|||||||
|
|
||||||
from ..lib import captured_output
|
from ..lib import captured_output
|
||||||
|
|
||||||
from composer.cli.blueprints import prettyDiffEntry, blueprints_list, blueprints_show, blueprints_changes
|
from composer.cli.blueprints import pretty_diff_entry, blueprints_list, blueprints_show, blueprints_changes
|
||||||
from composer.cli.blueprints import blueprints_diff, blueprints_save, blueprints_delete, blueprints_depsolve
|
from composer.cli.blueprints import blueprints_diff, blueprints_save, blueprints_delete, blueprints_depsolve
|
||||||
from composer.cli.blueprints import blueprints_push, blueprints_freeze, blueprints_undo, blueprints_tag
|
from composer.cli.blueprints import blueprints_push, blueprints_freeze, blueprints_undo, blueprints_tag
|
||||||
|
from composer.cli.blueprints import pretty_dict, dict_names
|
||||||
|
|
||||||
diff_entries = [{'new': {'Description': 'Shiny new description'}, 'old': {'Description': 'Old reliable description'}},
|
diff_entries = [{'new': {'Description': 'Shiny new description'}, 'old': {'Description': 'Old reliable description'}},
|
||||||
{'new': {'Version': '0.3.1'}, 'old': {'Version': '0.1.1'}},
|
{'new': {'Version': '0.3.1'}, 'old': {'Version': '0.1.1'}},
|
||||||
@ -29,7 +30,34 @@ diff_entries = [{'new': {'Description': 'Shiny new description'}, 'old': {'Descr
|
|||||||
{'new': None, 'old': {'Module': {'name': 'bash', 'version': '5.*'}}},
|
{'new': None, 'old': {'Module': {'name': 'bash', 'version': '5.*'}}},
|
||||||
{'new': {'Module': {'name': 'httpd', 'version': '3.8.*'}},
|
{'new': {'Module': {'name': 'httpd', 'version': '3.8.*'}},
|
||||||
'old': {'Module': {'name': 'httpd', 'version': '3.7.*'}}},
|
'old': {'Module': {'name': 'httpd', 'version': '3.7.*'}}},
|
||||||
{'new': {'Package': {'name': 'git', 'version': '2.13.*'}}, 'old': None}]
|
{'new': {'Package': {'name': 'git', 'version': '2.13.*'}}, 'old': None},
|
||||||
|
# New items
|
||||||
|
{"new": {"Group": {"name": "core"}}, "old": None},
|
||||||
|
{"new": {"Customizations.firewall": {"ports": ["8888:tcp", "22:tcp", "dns:udp", "9090:tcp"], "services": ["smtp"]}}, "old": None},
|
||||||
|
{"new": {"Customizations.hostname": "foobar"}, "old": None},
|
||||||
|
{"new": {"Customizations.locale": {"keyboard": "US"}}, "old": None},
|
||||||
|
{"new": {"Customizations.sshkey": [{"key": "ssh-rsa AAAAB3NzaC1... norm@localhost.localdomain", "user": "norm" }]}, "old": None},
|
||||||
|
{"new": {"Customizations.timezone": {"ntpservers": ["ntp.nowhere.com" ], "timezone": "PST8PDT"}}, "old": None},
|
||||||
|
{"new": {"Customizations.user": [{"key": "ssh-rsa AAAAB3NzaC1... root@localhost.localdomain", "name": "root", "password": "fobarfobar"}]}, "old": None},
|
||||||
|
{"new": {"Repos.git": {"destination": "/opt/server-1/", "ref": "v1.0", "repo": "PATH OF GIT REPO TO CLONE", "rpmname": "server-config", "rpmrelease": "2", "rpmversion": "1.0", "summary": "Setup files for server deployment"}}, "old": None},
|
||||||
|
# Removed items (just reversed old/old from above block)
|
||||||
|
{"old": {"Group": {"name": "core"}}, "new": None},
|
||||||
|
{"old": {"Customizations.firewall": {"ports": ["8888:tcp", "22:tcp", "dns:udp", "9090:tcp"], "services": ["smtp"]}}, "new": None},
|
||||||
|
{"old": {"Customizations.hostname": "foobar"}, "new": None},
|
||||||
|
{"old": {"Customizations.locale": {"keyboard": "US"}}, "new": None},
|
||||||
|
{"old": {"Customizations.sshkey": [{"key": "ssh-rsa AAAAB3NzaC1... norm@localhost.localdomain", "user": "norm" }]}, "new": None},
|
||||||
|
{"old": {"Customizations.timezone": {"ntpservers": ["ntp.nowhere.com" ], "timezone": "PST8PDT"}}, "new": None},
|
||||||
|
{"old": {"Customizations.user": [{"key": "ssh-rsa AAAAB3NzaC1... root@localhost.localdomain", "name": "root", "password": "fobarfobar"}]}, "new": None},
|
||||||
|
{"old": {"Repos.git": {"destination": "/opt/server-1/", "ref": "v1.0", "repo": "PATH OF GIT REPO TO CLONE", "rpmname": "server-config", "rpmrelease": "2", "rpmversion": "1.0", "summary": "Setup files for server deployment"}}, "new": None},
|
||||||
|
# Changed items
|
||||||
|
{"old": {"Customizations.firewall": {"ports": ["8888:tcp", "22:tcp", "dns:udp", "9090:tcp"], "services": ["smtp"]}}, "new": {"Customizations.firewall": {"ports": ["8888:tcp", "22:tcp", "25:tcp"]}}},
|
||||||
|
{"old": {"Customizations.hostname": "foobar"}, "new": {"Customizations.hostname": "grues"}},
|
||||||
|
{"old": {"Customizations.locale": {"keyboard": "US"}}, "new": {"Customizations.locale": {"keyboard": "US", "languages": ["en_US.UTF-8"]}}},
|
||||||
|
{"old": {"Customizations.sshkey": [{"key": "ssh-rsa AAAAB3NzaC1... norm@localhost.localdomain", "user": "norm" }]}, "new": {"Customizations.sshkey": [{"key": "ssh-rsa ABCDEF01234... norm@localhost.localdomain", "user": "norm" }]}},
|
||||||
|
{"old": {"Customizations.timezone": {"ntpservers": ["ntp.nowhere.com" ], "timezone": "PST8PDT"}}, "new": {"Customizations.timezone": {"timezone": "Antarctica/Palmer"}}},
|
||||||
|
{"old": {"Customizations.user": [{"key": "ssh-rsa AAAAB3NzaC1... root@localhost.localdomain", "name": "root", "password": "fobarfobar"}]}, "new": {"Customizations.user": [{"key": "ssh-rsa AAAAB3NzaC1... root@localhost.localdomain", "name": "root", "password": "qweqweqwe"}]}},
|
||||||
|
{"old": {"Repos.git": {"destination": "/opt/server-1/", "ref": "v1.0", "repo": "PATH OF GIT REPO TO CLONE", "rpmname": "server-config", "rpmrelease": "2", "rpmversion": "1.0", "summary": "Setup files for server deployment"}}, "new": {"Repos.git": {"destination": "/opt/server-1/", "ref": "v1.0", "repo": "PATH OF GIT REPO TO CLONE", "rpmname": "server-config", "rpmrelease": "1", "rpmversion": "1.1", "summary": "Setup files for server deployment"}}}
|
||||||
|
]
|
||||||
|
|
||||||
diff_result = [
|
diff_result = [
|
||||||
'Changed Description "Old reliable description" -> "Shiny new description"',
|
'Changed Description "Old reliable description" -> "Shiny new description"',
|
||||||
@ -37,12 +65,81 @@ diff_result = [
|
|||||||
'Added Module openssh 2.8.1',
|
'Added Module openssh 2.8.1',
|
||||||
'Removed Module bash 5.*',
|
'Removed Module bash 5.*',
|
||||||
'Changed Module httpd 3.7.* -> 3.8.*',
|
'Changed Module httpd 3.7.* -> 3.8.*',
|
||||||
'Added Package git 2.13.*']
|
'Added Package git 2.13.*',
|
||||||
|
'Added Group core',
|
||||||
|
'Added Customizations.firewall ports="8888:tcp, 22:tcp, dns:udp, 9090:tcp" services="smtp"',
|
||||||
|
'Added Customizations.hostname foobar',
|
||||||
|
'Added Customizations.locale keyboard="US"',
|
||||||
|
'Added Customizations.sshkey norm',
|
||||||
|
'Added Customizations.timezone ntpservers="ntp.nowhere.com" timezone="PST8PDT"',
|
||||||
|
'Added Customizations.user root',
|
||||||
|
'Added Repos.git destination="/opt/server-1/" ref="v1.0" repo="PATH OF GIT REPO TO CLONE" rpmname="server-config" rpmrelease="2" rpmversion="1.0" summary="Setup files for server deployment"',
|
||||||
|
'Removed Group core',
|
||||||
|
'Removed Customizations.firewall ports="8888:tcp, 22:tcp, dns:udp, 9090:tcp" services="smtp"',
|
||||||
|
'Removed Customizations.hostname foobar',
|
||||||
|
'Removed Customizations.locale keyboard="US"',
|
||||||
|
'Removed Customizations.sshkey norm',
|
||||||
|
'Removed Customizations.timezone ntpservers="ntp.nowhere.com" timezone="PST8PDT"',
|
||||||
|
'Removed Customizations.user root',
|
||||||
|
'Removed Repos.git destination="/opt/server-1/" ref="v1.0" repo="PATH OF GIT REPO TO CLONE" rpmname="server-config" rpmrelease="2" rpmversion="1.0" summary="Setup files for server deployment"',
|
||||||
|
'Changed Customizations.firewall ports="8888:tcp, 22:tcp, dns:udp, 9090:tcp" services="smtp" -> ports="8888:tcp, 22:tcp, 25:tcp"',
|
||||||
|
'Changed Customizations.hostname foobar -> grues',
|
||||||
|
'Changed Customizations.locale keyboard="US" -> keyboard="US" languages="en_US.UTF-8"',
|
||||||
|
'Changed Customizations.sshkey norm -> norm',
|
||||||
|
'Changed Customizations.timezone ntpservers="ntp.nowhere.com" timezone="PST8PDT" -> timezone="Antarctica/Palmer"',
|
||||||
|
'Changed Customizations.user root -> root',
|
||||||
|
'Changed Repos.git destination="/opt/server-1/" ref="v1.0" repo="PATH OF GIT REPO TO CLONE" rpmname="server-config" rpmrelease="2" rpmversion="1.0" summary="Setup files for server deployment" -> destination="/opt/server-1/" ref="v1.0" repo="PATH OF GIT REPO TO CLONE" rpmname="server-config" rpmrelease="1" rpmversion="1.1" summary="Setup files for server deployment"',
|
||||||
|
]
|
||||||
|
|
||||||
|
dict_entries = [{"ports": ["8888:tcp", "22:tcp", "dns:udp", "9090:tcp"]},
|
||||||
|
{"ports": ["8888:tcp", "22:tcp", "dns:udp", "9090:tcp"], "services": ["smtp"]},
|
||||||
|
{ "destination": "/opt/server-1/", "ref": "v1.0", "repo": "PATH OF GIT REPO TO CLONE", "rpmname": "server-config", "rpmrelease": "1", "rpmversion": "1.0", "summary": "Setup files for server deployment" },
|
||||||
|
{"foo": ["one", "two"], "bar": {"baz": "three"}}]
|
||||||
|
|
||||||
|
dict_results = ['ports="8888:tcp, 22:tcp, dns:udp, 9090:tcp"',
|
||||||
|
'ports="8888:tcp, 22:tcp, dns:udp, 9090:tcp" services="smtp"',
|
||||||
|
'destination="/opt/server-1/" ref="v1.0" repo="PATH OF GIT REPO TO CLONE" rpmname="server-config" rpmrelease="1" rpmversion="1.0" summary="Setup files for server deployment"',
|
||||||
|
'foo="one, two"']
|
||||||
|
|
||||||
|
dict_name_entry1 = [{"name": "bart", "home": "Springfield"},
|
||||||
|
{"name": "lisa", "instrument": "Saxaphone"},
|
||||||
|
{"name": "homer", "kids": ["bart", "maggie", "lisa"]}]
|
||||||
|
|
||||||
|
dict_name_results1 = "bart, lisa, homer"
|
||||||
|
|
||||||
|
dict_name_entry2 = [{"user": "root", "password": "qweqweqwe"},
|
||||||
|
{"user": "norm", "password": "b33r"},
|
||||||
|
{"user": "cliff", "password": "POSTMASTER"}]
|
||||||
|
|
||||||
|
dict_name_results2 = "root, norm, cliff"
|
||||||
|
|
||||||
|
dict_name_entry3 = [{"home": "/root", "key": "skeleton"},
|
||||||
|
{"home": "/home/norm", "key": "SSH KEY"},
|
||||||
|
{"home": "/home/cliff", "key": "lost"}]
|
||||||
|
|
||||||
|
dict_name_results3 = "/root, /home/norm, /home/cliff"
|
||||||
|
|
||||||
|
|
||||||
class BlueprintsTest(unittest.TestCase):
|
class BlueprintsTest(unittest.TestCase):
|
||||||
def test_prettyDiffEntry(self):
|
def test_pretty_diff_entry(self):
|
||||||
"""Return a nice representation of a diff entry"""
|
"""Return a nice representation of a diff entry"""
|
||||||
self.assertEqual([prettyDiffEntry(entry) for entry in diff_entries], diff_result)
|
self.assertEqual([pretty_diff_entry(entry) for entry in diff_entries], diff_result)
|
||||||
|
|
||||||
|
def test_pretty_dict(self):
|
||||||
|
"""Return a human readable single line"""
|
||||||
|
self.assertEqual([pretty_dict(entry) for entry in dict_entries], dict_results)
|
||||||
|
|
||||||
|
def test_dict_names_users(self):
|
||||||
|
"""Return a list of the name field of the list of dicts"""
|
||||||
|
self.assertEqual(dict_names(dict_name_entry1), dict_name_results1)
|
||||||
|
|
||||||
|
def test_dict_names_sshkey(self):
|
||||||
|
"""Return a list of the user field of the list of dicts"""
|
||||||
|
self.assertEqual(dict_names(dict_name_entry2), dict_name_results2)
|
||||||
|
|
||||||
|
def test_dict_names_other(self):
|
||||||
|
"""Return a list of the unknown field of the list of dicts"""
|
||||||
|
self.assertEqual(dict_names(dict_name_entry3), dict_name_results3)
|
||||||
|
|
||||||
@unittest.skipUnless(os.path.exists("/run/weldr/api.socket"), "Test requires a running API server")
|
@unittest.skipUnless(os.path.exists("/run/weldr/api.socket"), "Test requires a running API server")
|
||||||
def test_list(self):
|
def test_list(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user