def test_archive_discard_section(self): ''' Test discard a section from the archive. :return: ''' archive = MagicMock() with patch('salt.cli.support.collector.tarfile.TarFile', archive): self.collector.open() self.collector.add('solar-interference') self.collector.link(title='Thermal anomaly', path='/path/to/another/great.config') self.collector.add('foo') self.collector.link(title='Backup Path', path='/path/to/backup.config') self.collector._flush_content() assert archive.bz2open().addfile.call_count == 2 assert (archive.bz2open().addfile.mock_calls[0][2]['fileobj'].read() == to_bytes('Thermal anomaly\n---------------\n\npath=/dev/null\n\n\n')) self.collector.close() archive = MagicMock() with patch('salt.cli.support.collector.tarfile.TarFile', archive): self.collector.open() self.collector.add('solar-interference') self.collector.link(title='Thermal anomaly', path='/path/to/another/great.config') self.collector.discard_current() self.collector.add('foo') self.collector.link(title='Backup Path', path='/path/to/backup.config') self.collector._flush_content() assert archive.bz2open().addfile.call_count == 2 assert (archive.bz2open().addfile.mock_calls[0][2]['fileobj'].read() == to_bytes('Backup Path\n-----------\n\npath=/dev/null\n\n\n')) self.collector.close()
def _compute_signature(parameters, access_key_secret): ''' Generate aliyun request signature ''' def percent_encode(line): if not isinstance(line, six.string_types): return line s = line if sys.stdin.encoding is None: s = line.decode().encode('utf8') else: s = line.decode(sys.stdin.encoding).encode('utf8') res = _quote(s, '') res = res.replace('+', '%20') res = res.replace('*', '%2A') res = res.replace('%7E', '~') return res sortedParameters = sorted(list(parameters.items()), key=lambda items: items[0]) canonicalizedQueryString = '' for k, v in sortedParameters: canonicalizedQueryString += '&' + percent_encode(k) \ + '=' + percent_encode(v) # All aliyun API only support GET method stringToSign = 'GET&%2F&' + percent_encode(canonicalizedQueryString[1:]) h = hmac.new(to_bytes(access_key_secret + "&"), stringToSign, sha1) signature = base64.encodestring(h.digest()).strip() return signature
def _compute_signature(parameters, access_key_secret): """ Generate aliyun request signature """ def percent_encode(line): if not isinstance(line, str): return line s = line if sys.stdin.encoding is None: s = line.decode().encode("utf8") else: s = line.decode(sys.stdin.encoding).encode("utf8") res = urllib.parse.quote(s, "") res = res.replace("+", "%20") res = res.replace("*", "%2A") res = res.replace("%7E", "~") return res sortedParameters = sorted(list(parameters.items()), key=lambda items: items[0]) canonicalizedQueryString = "" for k, v in sortedParameters: canonicalizedQueryString += "&" + percent_encode( k) + "=" + percent_encode(v) # All aliyun API only support GET method stringToSign = "GET&%2F&" + percent_encode(canonicalizedQueryString[1:]) h = hmac.new(to_bytes(access_key_secret + "&"), stringToSign, sha1) signature = base64.encodestring(h.digest()).strip() return signature
def _toset(thing): """helper to convert various things to a set This enables flexibility in what users provide as the list of LDAP entry attribute values. Note that the LDAP spec prohibits duplicate values in an attribute. RFC 2251 states that: "The order of attribute values within the vals set is undefined and implementation-dependent, and MUST NOT be relied upon." However, OpenLDAP have an X-ORDERED that is used in the config schema. Using sets would mean we can't pass ordered values and therefore can't manage parts of the OpenLDAP configuration, hence the use of OrderedSet. Sets are also good for automatically removing duplicates. None becomes an empty set. Iterables except for strings have their elements added to a new set. Non-None scalars (strings, numbers, non-iterable objects, etc.) are added as the only member of a new set. """ if thing is None: return OrderedSet() if isinstance(thing, str): return OrderedSet((to_bytes(thing), )) if isinstance(thing, int): return OrderedSet((to_bytes(str(thing)), )) # convert numbers to strings and then bytes # so that equality checks work # (LDAP stores numbers as strings) try: return OrderedSet( to_bytes(str(x)) if isinstance(x, int) else to_bytes(x) for x in thing) except TypeError: return OrderedSet(str(thing), )
def test_archive_addwrite(self): ''' Test add to the archive a section and write to it. :return: ''' archive = MagicMock() with patch('salt.cli.support.collector.tarfile.TarFile', archive): self.collector.open() self.collector.add('foo') self.collector.write(title='title', data='data', output='null') self.collector._flush_content() assert (archive.bz2open().addfile.call_args[1]['fileobj'].read() == to_bytes('title\n-----\n\nraw-content: data\n\n\n\n'))
def test_archive_addlink(self): ''' Test add to the archive a section and link an external file or directory to it. :return: ''' archive = MagicMock() with patch('salt.cli.support.collector.tarfile.TarFile', archive): self.collector.open() self.collector.add('foo') self.collector.link(title='Backup Path', path='/path/to/backup.config') self.collector._flush_content() assert archive.bz2open().addfile.call_count == 1 assert (archive.bz2open().addfile.call_args[1]['fileobj'].read() == to_bytes('Backup Path\n-----------\n\npath=/dev/null\n\n\n'))
def _update_entry(entry, status, directives): """Update an entry's attributes using the provided directives :param entry: A dict mapping each attribute name to a set of its values :param status: A dict holding cross-invocation status (whether delete_others is True or not, and the set of mentioned attributes) :param directives: A dict mapping directive types to directive-specific state """ for directive, state in directives.items(): if directive == "delete_others": status["delete_others"] = state continue for attr, vals in state.items(): status["mentioned_attributes"].add(attr) vals = [to_bytes(val) for val in _toset(vals)] if directive == "default": if vals and (attr not in entry or not entry[attr]): entry[attr] = vals elif directive == "add": vals.update(entry.get(attr, ())) if vals: entry[attr] = vals elif directive == "delete": existing_vals = entry.pop(attr, OrderedSet()) if vals: existing_vals -= vals if existing_vals: entry[attr] = existing_vals elif directive == "replace": entry.pop(attr, None) if vals: entry[attr] = vals else: raise ValueError("unknown directive: " + directive)
def _test_helper(self, init_db, expected_ret, replace, delete_others=False): _init_db(copy.deepcopy(init_db)) old = _dump_db() new = _dump_db() expected_db = copy.deepcopy(init_db) for dn, attrs in replace.items(): for attr, vals in attrs.items(): vals = [to_bytes(val) for val in vals] if vals: new.setdefault(dn, {})[attr] = list(OrderedSet(vals)) expected_db.setdefault(dn, {})[attr] = OrderedSet(vals) elif dn in expected_db: new[dn].pop(attr, None) expected_db[dn].pop(attr, None) if not expected_db.get(dn, {}): new.pop(dn, None) expected_db.pop(dn, None) if delete_others: dn_to_delete = OrderedSet() for dn, attrs in expected_db.items(): if dn in replace: to_delete = OrderedSet() for attr, vals in attrs.items(): if attr not in replace[dn]: to_delete.add(attr) for attr in to_delete: del attrs[attr] del new[dn][attr] if not attrs: dn_to_delete.add(dn) for dn in dn_to_delete: del new[dn] del expected_db[dn] name = "ldapi:///" expected_ret["name"] = name expected_ret.setdefault("result", True) expected_ret.setdefault("comment", "Successfully updated LDAP entries") expected_ret.setdefault( "changes", { dn: { "old": { attr: vals for attr, vals in old[dn].items() if vals != new.get(dn, {}).get(attr, ()) } if dn in old else None, "new": { attr: vals for attr, vals in new[dn].items() if vals != old.get(dn, {}).get(attr, ()) } if dn in new else None, } for dn in replace if old.get(dn, {}) != new.get(dn, {}) }, ) entries = [ {dn: [{"replace": attrs}, {"delete_others": delete_others}]} for dn, attrs in replace.items() ] actual = salt.states.ldap.managed(name, entries) self.assertDictEqual(expected_ret, actual) self.assertDictEqual(expected_db, db)
def _test_helper_add(db, expected_ret, add_items, delete_others=False): old = db.dump_db() new = db.dump_db() expected_db = copy.deepcopy(db.db) for dn, attrs in add_items.items(): for attr, vals in attrs.items(): vals = [to_bytes(val) for val in vals] vals.extend(old.get(dn, {}).get(attr, OrderedSet())) vals.sort() if vals: new.setdefault(dn, {})[attr] = list(OrderedSet(vals)) expected_db.setdefault(dn, {})[attr] = OrderedSet(vals) elif dn in expected_db: new[dn].pop(attr, None) expected_db[dn].pop(attr, None) if not expected_db.get(dn, {}): new.pop(dn, None) expected_db.pop(dn, None) if delete_others: dn_to_delete = OrderedSet() for dn, attrs in expected_db.items(): if dn in add_items: to_delete = OrderedSet() for attr, vals in attrs.items(): if attr not in add_items[dn]: to_delete.add(attr) for attr in to_delete: del attrs[attr] del new[dn][attr] if not attrs: dn_to_delete.add(dn) for dn in dn_to_delete: del new[dn] del expected_db[dn] name = "ldapi:///" expected_ret["name"] = name expected_ret.setdefault("result", True) expected_ret.setdefault("comment", "Successfully updated LDAP entries") expected_ret.setdefault( "changes", { dn: { "old": { attr: vals for attr, vals in old[dn].items() if vals != new.get(dn, {}).get(attr, ()) } if dn in old else None, "new": { attr: vals for attr, vals in new[dn].items() if vals != old.get(dn, {}).get(attr, ()) } if dn in new else None, } for dn in add_items if old.get(dn, {}) != new.get(dn, {}) }, ) entries = [{ dn: [{ "add": attrs }, { "delete_others": delete_others }] } for dn, attrs in add_items.items()] actual = salt.states.ldap.managed(name, entries) assert expected_ret == actual assert expected_db == db.db