def test_abort_on_invalid_status(self, mocker, status): client = mocker.Mock() client.put.return_value = http.Response(status, "") with pytest.raises(errors.SyncError, match=str(status)): utils.put(client, "/put", {"payload": "data"}) client.put.assert_called_once_with("/put", {"payload": "data"})
def sync(state, client, list_path, resource_path, payload, check_mode): datastore = utils.get(client, resource_path) # When we are deleting stores, we do not care if there is more than one # datastore present. We just make sure the currently manipulated store is # gone. This makes our module useful in "let us clean up the mess" # scenarios. if state == "absent" and datastore is None: return False, None if state == "absent": if not check_mode: utils.delete(client, resource_path) return True, None # If the store exists, update it and ignore the fact that there might be # more than one present. if datastore: if _do_differ(datastore, payload): if check_mode: return True, payload utils.put(client, resource_path, payload) return True, utils.get(client, resource_path) return False, datastore # When adding a new datastore, we first make sure there is no other # datastore present because we do not want to be the ones who brought # backends into an inconsistent state. if utils.get(client, list_path): raise errors.Error("Some other external datastore is already active.") if check_mode: return True, payload utils.put(client, resource_path, payload) return True, utils.get(client, resource_path)
def update_state(client, path, old_disabled, new_disabled, check_mode): changed = old_disabled != new_disabled if not check_mode and changed: if new_disabled: # `state: disabled` input parameter utils.delete(client, path) else: # `state: enabled` input parameter utils.put(client, path + '/reinstate', None) return changed
def sync(client, path, payload, check_mode): remote_object = get(client, path) if utils.do_differ(remote_object, payload): if check_mode: return True, payload utils.put(client, path, payload) return True, get(client, path) return False, remote_object
def sync(remote_object, state, client, path, payload, check_mode): if state == 'disabled' and remote_object is not None: if not check_mode: utils.delete(client, path) return True, utils.get(client, path) if utils.do_differ(remote_object, payload): if check_mode: return True, payload utils.put(client, path, payload) return True, utils.get(client, path) return False, remote_object
def sync(remote_object, client, path, payload, check_mode): # Create new user (either enabled or disabled) if remote_object is None: if check_mode: return True, payload utils.put(client, path, payload) return True, utils.get(client, path) # Update existing user. We do this on a field-by-field basis because the # upsteam API for updating users requires a password field to be set. Of # course, we do not want to force users to specify an existing password # just for the sake of updating the group membership, so this is why we # use field-specific API endpoints to update the user data. changed = False if 'password' in payload: changed = update_password( client, path, payload['username'], payload['password'], check_mode, ) or changed if 'groups' in payload: changed = update_groups( client, path, remote_object.get('groups') or [], payload['groups'], check_mode, ) or changed if 'disabled' in payload: changed = update_state( client, path, remote_object['disabled'], payload['disabled'], check_mode, ) or changed if check_mode: # Backend does not return back passwords, so we should follow the # example set by the backend API. return changed, dict( remote_object, **{k: v for k, v in payload.items() if k != 'password'}) return changed, utils.get(client, path)
def update_password(client, path, username, password, check_mode): # Hit the auth testing API and try to validate the credentials. If the API # says they are invalid, we need to update them. if client.validate_auth_data(username, password): return False if not check_mode: utils.put(client, path + '/password', dict( username=username, password=password, )) return True
def update_groups(client, path, old_groups, new_groups, check_mode): to_delete = set(old_groups).difference(new_groups) to_add = set(new_groups).difference(old_groups) if not check_mode: # Next few lines are far from atomic, which means that we can leave a # user in any of the intermediate states, but this is the best we can # do given the API limitations. for g in to_add: utils.put(client, path + '/groups/' + g, None) for g in to_delete: utils.delete(client, path + '/groups/' + g) return len(to_delete) + len(to_add) > 0
def test_valid_put(self, mocker, status): client = mocker.Mock() client.put.return_value = http.Response(status, '{"put": "data"}') object = utils.put(client, "/put", {"payload": "data"}) assert object is None client.put.assert_called_once_with("/put", {"payload": "data"})
def update_password(client, path, username, password, check_mode): # Hit the auth testing API and try to validate the credentials. If the API # says they are invalid, we need to update them. if client.validate_auth_data(username, password): return False if not check_mode: if client.version < "5.21.0": utils.put(client, path + '/password', dict( username=username, password=password, )) else: hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) utils.put(client, path + '/reset_password', dict( username=username, password_hash=hash.decode('ascii'), )) return True
def send_event(client, path, payload, check_mode): if not check_mode: utils.put(client, path, payload) return True, payload
def send_event(client, path, payload, check_mode): if check_mode: return True, payload utils.put(client, path, payload) return True, utils.get(client, path)