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
							
								
									850c490b6e
								
							
						
					
					
						commit
						156ef0acfd
					
				| @ -187,62 +187,129 @@ def blueprints_diff(socket_path, api_version, args, show_json=False): | ||||
|         return rc | ||||
| 
 | ||||
|     for diff in result["diff"]: | ||||
|         print(prettyDiffEntry(diff)) | ||||
|         print(pretty_diff_entry(diff)) | ||||
| 
 | ||||
|     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. | ||||
| 
 | ||||
|     :param diff: Difference entry dict | ||||
|     :type diff: dict | ||||
|     :returns: Nice string | ||||
|     """ | ||||
|     def change(diff): | ||||
|     if diff["old"] and diff["new"]: | ||||
|             return "Changed" | ||||
|         change = "Changed" | ||||
|     elif diff["new"] and not diff["old"]: | ||||
|             return "Added" | ||||
|         change = "Added" | ||||
|     elif diff["old"] and not diff["new"]: | ||||
|             return "Removed" | ||||
|         change = "Removed" | ||||
|     else: | ||||
|             return "Unknown" | ||||
|         change = "Unknown" | ||||
| 
 | ||||
|     def name(diff): | ||||
|     if diff["old"]: | ||||
|             return list(diff["old"].keys())[0] | ||||
|         name = list(diff["old"].keys())[0] | ||||
|     elif diff["new"]: | ||||
|             return list(diff["new"].keys())[0] | ||||
|         name = list(diff["new"].keys())[0] | ||||
|     else: | ||||
|             return "Unknown" | ||||
|         name = "Unknown" | ||||
| 
 | ||||
|     def details(diff): | ||||
|         if change(diff) == "Changed": | ||||
|             if name(diff) == "Description": | ||||
|                 return '"%s" -> "%s"' % (diff["old"][name(diff)], diff["new"][name(diff)]) | ||||
|             elif name(diff) == "Version": | ||||
|                 return "%s -> %s" % (diff["old"][name(diff)], diff["new"][name(diff)]) | ||||
|             elif name(diff) in ["Module", "Package"]: | ||||
|                 return "%s %s -> %s" % (diff["old"][name(diff)]["name"], diff["old"][name(diff)]["version"], | ||||
|                                         diff["new"][name(diff)]["version"]) | ||||
|         if change == "Changed": | ||||
|             if type(diff["old"][name]) == type(""): | ||||
|                 if name == "Description" or " " in diff["old"][name]: | ||||
|                     return '"%s" -> "%s"' % (diff["old"][name], diff["new"][name]) | ||||
|                 else: | ||||
|                     return "%s -> %s" % (diff["old"][name], diff["new"][name]) | ||||
|             elif name in ["Module", "Package"]: | ||||
|                 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: | ||||
|                 return "Unknown" | ||||
|         elif change(diff) == "Added": | ||||
|             if name(diff) in ["Module", "Package"]: | ||||
|                 return "%s %s" % (diff["new"][name(diff)]["name"], diff["new"][name(diff)]["version"]) | ||||
|             elif name(diff) in ["Group"]: | ||||
|                 return diff["new"][name(diff)]["name"] | ||||
|         elif change == "Added": | ||||
|             if name in ["Module", "Package"]: | ||||
|                 return "%s %s" % (diff["new"][name]["name"], diff["new"][name]["version"]) | ||||
|             elif name in ["Group"]: | ||||
|                 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: | ||||
|                 return " ".join([diff["new"][k] for k in diff["new"]]) | ||||
|         elif change(diff) == "Removed": | ||||
|             if name(diff) in ["Module", "Package"]: | ||||
|                 return "%s %s" % (diff["old"][name(diff)]["name"], diff["old"][name(diff)]["version"]) | ||||
|             elif name(diff) in ["Group"]: | ||||
|                 return diff["old"][name(diff)]["name"] | ||||
|                 return "unknown/todo: %s" % type(diff["new"][name]) | ||||
|         elif change == "Removed": | ||||
|             if name in ["Module", "Package"]: | ||||
|                 return "%s %s" % (diff["old"][name]["name"], diff["old"][name]["version"]) | ||||
|             elif name in ["Group"]: | ||||
|                 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: | ||||
|                 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): | ||||
|     """Save the blueprint to a TOML file | ||||
|  | ||||
| @ -19,9 +19,10 @@ import unittest | ||||
| 
 | ||||
| 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_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'}}, | ||||
|                 {'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': {'Module': {'name': 'httpd', 'version': '3.8.*'}}, | ||||
|                  '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 = [ | ||||
|     'Changed Description "Old reliable description" -> "Shiny new description"', | ||||
| @ -37,12 +65,81 @@ diff_result = [ | ||||
|     'Added Module openssh 2.8.1', | ||||
|     'Removed Module bash 5.*', | ||||
|     '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): | ||||
|     def test_prettyDiffEntry(self): | ||||
|     def test_pretty_diff_entry(self): | ||||
|         """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") | ||||
|     def test_list(self): | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user