def test_device_deployments_already_installed(self): """Check case with already installed artifact """ dev = Device() self.log.info('fake device with ID: %s', dev.devid) self.inventory_add_dev(dev) data = b'foo_bar' artifact_name = 'hammer-update-' + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() with ac.with_added_artifact(description='desc', size=art.size, data=art) as artid: newdep = self.make_new_deployment(name='foo', artifact_name=artifact_name, devices=[dev.devid]) with self.with_added_deployment(newdep) as depid: dc = SimpleDeviceClient() self.log.debug('device token %s', dev.fake_token) # pretend we have the same artifact installed already # NOTE: asking for a deployment while having it already # installed is special in the sense that the status of # deployment for this device will be marked as 'already-installed' nextdep = dc.get_next_deployment(dev.fake_token, artifact_name=artifact_name, device_type=dev.device_type) self.log.info('device next: %s', nextdep) assert nextdep == None # verify that device status was properly recorded self.verify_deployment_stats(depid, expected={ 'already-installed': 1, })
def test_deplyments_get_devices(self): """Create deployments, get devices with pagination""" devices = [] devices_qty = 30 device_ids = [] default_per_page = 20 device_type = "test-hammer-type" # create devices for i in range(0, devices_qty): device = Device(device_type) self.inventory_add_dev(device) devices.append(device) device_ids.append(device.devid) data = b"foo_bar" artifact_name = "pagination-test-" + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=device_type) as art: ac = SimpleArtifactsClient() ac.add_artifact(description="some description", size=art.size, data=art) new_dep = self.d.make_new_deployment(name="pagination deployment", artifact_name=artifact_name, devices=device_ids) dep_id = self.d.add_deployment(new_dep) for dev in devices: dc = SimpleDeviceClient() dc.get_next_deployment( dev.fake_token, artifact_name="different {}".format(artifact_name), device_type=dev.device_type, ) # check default 'page' and 'per_page' values res = self.d.client.Management_API.List_Devices_in_Deployment_with_pagination( Authorization="foo", deployment_id=dep_id).result()[0] assert len(res) == default_per_page # check custom 'per_page' res = self.d.client.Management_API.List_Devices_in_Deployment_with_pagination( Authorization="foo", deployment_id=dep_id, per_page=devices_qty).result()[0] assert len(res) == devices_qty # check 2nd page devices_qty_on_second_page = devices_qty - default_per_page res = self.d.client.Management_API.List_Devices_in_Deployment_with_pagination( Authorization="foo", deployment_id=dep_id, page=2, per_page=default_per_page).result()[0] assert len(res) == devices_qty_on_second_page
def test_ok(self, api_client_int, clean_db, mongo, test_set): """ Happy path - correct link obtained from the service, leading to a successful download of a correct artifact. """ # set up deployment tenant_id = str(ObjectId()) _, r = api_client_int.create_tenant(tenant_id) assert r.status_code == 201 deployment_id = str(uuid4()) dev = Device() dev.device_type = test_set["dev_type"] configuration_deployment = { "name": test_set["name"], "configuration": test_set["config"], } make_deployment( api_client_int, tenant_id, deployment_id, dev.devid, configuration_deployment, ) # obtain + verify deployment instructions dc = SimpleDeviceClient() nextdep = dc.get_next_deployment( dev.fake_token_mt(tenant_id), artifact_name="dontcare", device_type=test_set["dev_type"], ) assert nextdep.artifact["artifact_name"] == test_set["name"] assert nextdep.artifact["source"]["uri"] is not None assert nextdep.artifact["source"]["expire"] is not None assert nextdep.artifact["device_types_compatible"] == [test_set["dev_type"]] # get/verify download contents r = requests.get( nextdep.artifact["source"]["uri"], verify=False, ) assert r.status_code == 200 with open("/testing/out.mender", "wb+") as f: f.write(r.content) self.verify_artifact( "/testing/out.mender", test_set["name"], test_set["dev_type"], test_set["config"], )
def test_device_deployments_logs(self): """Check that device can get next deployment, full cycle """ dev = Device() self.log.info('fake device with ID: %s', dev.devid) self.inventory_add_dev(dev) data = b'foo_bar' artifact_name = 'hammer-update ' + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() with ac.with_added_artifact(description='desc', size=art.size, data=art) as artid: newdep = self.make_new_deployment(name='foo', artifact_name=artifact_name, devices=[dev.devid]) with self.with_added_deployment(newdep) as depid: dc = SimpleDeviceClient() self.log.debug('device token %s', dev.fake_token) # pretend we have another artifact installed nextdep = dc.get_next_deployment( dev.fake_token, artifact_name='different {}'.format(artifact_name), device_type=dev.device_type) self.log.info('device next: %s', nextdep) assert nextdep dc.upload_logs(dev.fake_token, nextdep.id, logs=[ 'foo bar baz', 'lorem ipsum', ]) rsp = self.client.deployments.get_deployments_deployment_id_devices_device_id_log( Authorization='foo', deployment_id=depid, device_id=dev.devid).result()[1] logs = rsp.text self.log.info('device logs\n%s', logs) assert 'lorem ipsum' in logs assert 'foo bar baz' in logs
def test_device_deployments_logs(self): """Check that device can get next deployment, full cycle""" dev = Device() self.d.log.info("fake device with ID: %s", dev.devid) self.inventory_add_dev(dev) data = b"foo_bar" artifact_name = "hammer-update-" + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() with ac.with_added_artifact(description="desc", size=art.size, data=art) as artid: newdep = self.d.make_new_deployment( name="foo", artifact_name=artifact_name, devices=[dev.devid]) with self.d.with_added_deployment(newdep) as depid: dc = SimpleDeviceClient() self.d.log.debug("device token %s", dev.fake_token) # pretend we have another artifact installed nextdep = dc.get_next_deployment( dev.fake_token, artifact_name="different {}".format(artifact_name), device_type=dev.device_type, ) self.d.log.info("device next: %s", nextdep) assert nextdep dc.upload_logs(dev.fake_token, nextdep.id, logs=["foo bar baz", "lorem ipsum"]) rsp = self.d.client.Management_API.Get_Deployment_Log_for_Device( Authorization="foo", deployment_id=depid, device_id=dev.devid).result()[1] logs = rsp.text self.d.log.info("device logs\n%s", logs) assert "lorem ipsum" in logs assert "foo bar baz" in logs
def test_fail_no_upgrade(self, api_client_int, clean_db, mongo): # start with a valid deployment tenant_id = str(ObjectId()) _, r = api_client_int.create_tenant(tenant_id) assert r.status_code == 201 deployment_id = str(uuid4()) dev = Device() configuration_deployment = {"name": "foo", "configuration": '{"foo":"bar"}'} make_deployment( api_client_int, tenant_id, deployment_id, dev.devid, configuration_deployment, ) # try get upgrade # valid device id + type, but different tenant other_tenant_id = str(ObjectId()) _, r = api_client_int.create_tenant(other_tenant_id) assert r.status_code == 201 dc = SimpleDeviceClient() nodep = dc.get_next_deployment( dev.fake_token_mt(other_tenant_id), artifact_name="dontcare", device_type=dev.device_type, ) assert nodep is None # correct tenant, incorrect device id (but correct type) otherdev = Device() nodep = dc.get_next_deployment( otherdev.fake_token_mt(tenant_id), artifact_name="dontcare", device_type=dev.device_type, ) assert nodep is None # correct tenant, correct device id, incorrect type nodep = dc.get_next_deployment( otherdev.fake_token_mt(tenant_id), artifact_name="dontcare", device_type=otherdev.device_type, ) assert nodep is None
def test_device_deployments_simple(self): """Check that device can get next deployment, simple cases: - bogus token - valid update - device type incompatible with artifact """ dev = Device() self.log.info('fake device with ID: %s', dev.devid) self.inventory_add_dev(dev) data = b'foo_bar' artifact_name = 'hammer-update-' + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() with ac.with_added_artifact(description='desc', size=art.size, data=art) as artid: newdep = self.make_new_deployment(name='foo', artifact_name=artifact_name, devices=[dev.devid]) with self.with_added_deployment(newdep) as depid: dc = SimpleDeviceClient() self.log.debug('device token %s', dev.fake_token) # try with some bogus token try: dc.get_next_deployment('foo-bar-baz', artifact_name=artifact_name, device_type=dev.device_type) except bravado.exception.HTTPError as err: assert 400 <= err.response.status_code < 500 else: raise AssertionError('expected to fail') # pretend we have another artifact installed nextdep = dc.get_next_deployment(dev.fake_token, artifact_name='different {}'.format(artifact_name), device_type=dev.device_type) self.log.info('device next: %s', nextdep) assert nextdep assert dev.device_type in nextdep.artifact['device_types_compatible'] # pretend our device type is different than expected nextdep = dc.get_next_deployment(dev.fake_token, artifact_name='different {}'.format(artifact_name), device_type='other {}'.format(dev.device_type)) self.log.info('device next: %s', nextdep) assert nextdep == None # verify that device status was properly recorded self.verify_deployment_stats(depid, expected={ 'noartifact': 1, })
def test_device_deployments_full(self): """Check that device can get next deployment, full cycle """ dev = Device() self.log.info('fake device with ID: %s', dev.devid) self.inventory_add_dev(dev) data = b'foo_bar' artifact_name = 'hammer-update-' + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() with ac.with_added_artifact(description='desc', size=art.size, data=art) as artid: newdep = self.make_new_deployment(name='foo', artifact_name=artifact_name, devices=[dev.devid]) with self.with_added_deployment(newdep) as depid: dc = SimpleDeviceClient() self.log.debug('device token %s', dev.fake_token) self.verify_deployment_stats(depid, expected={ 'pending': 1, }) # pretend we have another artifact installed nextdep = dc.get_next_deployment(dev.fake_token, artifact_name='different {}'.format(artifact_name), device_type=dev.device_type) self.log.info('device next: %s', nextdep) assert nextdep assert dev.device_type in nextdep.artifact['device_types_compatible'] for st in ['downloading', 'installing', 'rebooting']: dc.report_status(token=dev.fake_token, devdepid=nextdep.id, status=st) self.verify_deployment_stats(depid, expected={ st: 1, }) # we have reported some statuses now, but not the final # status, try to get the next deployment againdep = dc.get_next_deployment(dev.fake_token, artifact_name='different {}'.format(artifact_name), device_type=dev.device_type) assert againdep assert againdep.id == nextdep.id # deployment should be marked as inprogress dep = self.client.deployments.get_deployments_id(Authorization='foo', id=depid).result()[0] assert dep.status == 'inprogress' # report final status dc.report_status(token=dev.fake_token, devdepid=nextdep.id, status='success') self.verify_deployment_stats(depid, expected={ 'success': 1, }) dep = self.client.deployments.get_deployments_id(Authorization='foo', id=depid).result()[0] assert dep.status == 'finished' # report failure as final status dc.report_status(token=dev.fake_token, devdepid=nextdep.id, status='failure') self.verify_deployment_stats(depid, expected={ 'failure': 1, }) # deployment is finished, should get no more updates nodep = dc.get_next_deployment(dev.fake_token, artifact_name='other {}'.format(artifact_name), device_type=dev.device_type) assert nodep == None # as a joke, report rebooting now dc.report_status(token=dev.fake_token, devdepid=nextdep.id, status='rebooting') self.verify_deployment_stats(depid, expected={ 'rebooting': 1, }) # deployment is in progress again dep = self.client.deployments.get_deployments_id(Authorization='foo', id=depid).result()[0] assert dep.status == 'inprogress' # go on, let's pretend that the artifact is already installed nodep = dc.get_next_deployment(dev.fake_token, artifact_name=artifact_name, device_type=dev.device_type) assert nodep == None self.verify_deployment_stats(depid, expected={ 'already-installed': 1, })
def test_deployments_new_valid(self): """Add a new valid deployment, verify its status, verify device deployment status, abort and verify eveything once again""" dev = Device() self.d.log.info("fake device with ID: %s", dev.devid) self.inventory_add_dev(dev) data = b"foo_bar" artifact_name = "hammer-update-" + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() artid = ac.add_artifact(description="some description", size=art.size, data=art) newdep = self.d.make_new_deployment(name="fake deployment", artifact_name=artifact_name, devices=[dev.devid]) depid = self.d.add_deployment(newdep) # artifact is used in deployment, so attempts to remove it should # fail try: ac.delete_artifact(artid) except ArtifactsClientError as ace: # artifact is used in deployment assert ace.response.status_code == 409 else: raise AssertionError("expected to fail") dc = SimpleDeviceClient() nextdep = dc.get_next_deployment( dev.fake_token, artifact_name="different {}".format(artifact_name), device_type=dev.device_type, ) dep = self.d.client.Management_API.Show_Deployment( Authorization="foo", id=depid).result()[0] assert dep.artifact_name == artifact_name assert dep.id == depid assert dep.status == "pending" # fetch device status depdevs = self.d.client.Management_API.List_Devices_in_Deployment( Authorization="foo", deployment_id=depid).result()[0] assert len(depdevs) == 1 depdev = depdevs[0] assert depdev.status == "pending" assert depdev.id == dev.devid # verify statistics self.d.verify_deployment_stats(depid, expected={"pending": 1}) # abort deployment self.d.abort_deployment(depid) # that it's 'finished' now aborted_dep = self.d.client.Management_API.Show_Deployment( Authorization="foo", id=depid).result()[0] self.d.log.debug("deployment dep: %s", aborted_dep) assert aborted_dep.status == "finished" # verify statistics once again self.d.verify_deployment_stats(depid, expected={"aborted": 1}) # fetch device status depdevs = self.d.client.Management_API.List_Devices_in_Deployment( Authorization="foo", deployment_id=depid).result()[0] self.d.log.debug("deployment devices: %s", depdevs) assert len(depdevs) == 1 depdev = depdevs[0] assert depdev.status == "aborted" # deleting artifact should succeed ac.delete_artifact(artid)
def test_device_deployments_full(self): """Check that device can get next deployment, full cycle""" dev = Device() self.d.log.info("fake device with ID: %s", dev.devid) self.inventory_add_dev(dev) data = b"foo_bar" artifact_name = "hammer-update-" + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() with ac.with_added_artifact(description="desc", size=art.size, data=art) as artid: newdep = self.d.make_new_deployment( name="foo", artifact_name=artifact_name, devices=[dev.devid]) with self.d.with_added_deployment(newdep) as depid: dc = SimpleDeviceClient() self.d.log.debug("device token %s", dev.fake_token) # pretend we have another artifact installed nextdep = dc.get_next_deployment( dev.fake_token, artifact_name="different {}".format(artifact_name), device_type=dev.device_type, ) self.d.log.info("device next: %s", nextdep) assert nextdep assert (dev.device_type in nextdep.artifact["device_types_compatible"]) self.d.verify_deployment_stats(depid, expected={"pending": 1}) for st in ["downloading", "installing", "rebooting"]: dc.report_status(token=dev.fake_token, devdepid=nextdep.id, status=st) self.d.verify_deployment_stats(depid, expected={st: 1}) # we have reported some statuses now, but not the final # status, try to get the next deployment againdep = dc.get_next_deployment( dev.fake_token, artifact_name="different {}".format(artifact_name), device_type=dev.device_type, ) assert againdep assert againdep.id == nextdep.id # deployment should be marked as inprogress dep = self.d.client.Management_API.Show_Deployment( Authorization="foo", id=depid).result()[0] assert dep.status == "inprogress" # report final status dc.report_status(token=dev.fake_token, devdepid=nextdep.id, status="success") self.d.verify_deployment_stats(depid, expected={"success": 1}) dep = self.d.client.Management_API.Show_Deployment( Authorization="foo", id=depid).result()[0] assert dep.status == "finished" # report failure as final status dc.report_status(token=dev.fake_token, devdepid=nextdep.id, status="failure") self.d.verify_deployment_stats(depid, expected={"failure": 1}) # deployment is finished, should get no more updates nodep = dc.get_next_deployment( dev.fake_token, artifact_name="other {}".format(artifact_name), device_type=dev.device_type, ) assert nodep == None # as a joke, report rebooting now dc.report_status(token=dev.fake_token, devdepid=nextdep.id, status="rebooting") self.d.verify_deployment_stats(depid, expected={"rebooting": 1}) # deployment is in progress again dep = self.d.client.Management_API.Show_Deployment( Authorization="foo", id=depid).result()[0] assert dep.status == "inprogress" # go on, let's pretend that the artifact is already installed nodep = dc.get_next_deployment( dev.fake_token, artifact_name=artifact_name, device_type=dev.device_type, ) assert nodep == None self.d.verify_deployment_stats( depid, expected={"already-installed": 1})
def test_failures(self, api_client_int, clean_db, mongo): """ Simulate invalid or malicious download requests. """ # for reference - get a real, working link to an actual deployment tenant_id = str(ObjectId()) _, r = api_client_int.create_tenant(tenant_id) assert r.status_code == 201 deployment_id = str(uuid4()) dev = Device() configuration_deployment = {"name": "foo", "configuration": '{"foo":"bar"}'} make_deployment( api_client_int, tenant_id, deployment_id, dev.devid, configuration_deployment, ) dc = SimpleDeviceClient() nextdep = dc.get_next_deployment( dev.fake_token_mt(tenant_id), artifact_name="dontcare", device_type="hammer", ) uri = nextdep.artifact["source"]["uri"] qs = parse_qs(urlparse(uri).query) # now break the url in various ways # wrong deployment (signature error) uri_bad_depl = uri.replace(deployment_id, str(uuid4())) r = requests.get( uri_bad_depl, verify=False, ) assert r.status_code == 403 # wrong tenant in url (signature error) other_tenant_id = str(ObjectId()) _, r = api_client_int.create_tenant(other_tenant_id) assert r.status_code == 201 uri_bad_tenant = uri.replace(tenant_id, other_tenant_id) r = requests.get( uri_bad_tenant, verify=False, ) assert r.status_code == 403 # wrong dev type (signature error) other_dev = Device() other_dev.device_type = "foo" uri_bad_devtype = uri.replace(dev.device_type, other_dev.device_type) r = requests.get( uri_bad_devtype, verify=False, ) assert r.status_code == 403 # wrong dev id (signature error) uri_bad_devid = uri.replace(dev.devid, other_dev.devid) r = requests.get( uri_bad_devid, verify=False, ) assert r.status_code == 403 # wrong x-men-signature uri_bad_sig = uri.replace( qs["x-men-signature"][0], "mftJRzBafnvMXhmMBH3THQertiEk0dZKP075bjBKccc" ) r = requests.get( uri_bad_sig, verify=False, ) assert r.status_code == 403 # no x-men-signature uri_no_sig = uri.replace("&x-men-signature=", "") r = requests.get( uri_no_sig, verify=False, ) assert r.status_code == 400 # no x-men-expire uri_no_exp = uri.replace("&x-men-expire=", "") r = requests.get( uri_no_exp, verify=False, ) assert r.status_code == 400
def test_device_deployments_simple(self): """Check that device can get next deployment, simple cases: - bogus token - valid update - already installed - device type incompatible with artifact """ dev = Device() self.log.info('fake device with ID: %s', dev.devid) self.inventory_add_dev(dev) data = b'foo_bar' artifact_name = 'hammer-update ' + str(uuid4()) # come up with an artifact with artifact_from_data(name=artifact_name, data=data, devicetype=dev.device_type) as art: ac = SimpleArtifactsClient() with ac.with_added_artifact(description='desc', size=art.size, data=art) as artid: newdep = self.make_new_deployment(name='foo', artifact_name=artifact_name, devices=[dev.devid]) with self.with_added_deployment(newdep) as depid: dc = SimpleDeviceClient() self.log.debug('device token %s', dev.fake_token) # try with some bogus token try: dc.get_next_deployment('foo-bar-baz', artifact_name=artifact_name, device_type=dev.device_type) except bravado.exception.HTTPError as err: assert 400 <= err.response.status_code < 500 else: raise AssertionError('expected to fail') # pretend we have another artifact installed nextdep = dc.get_next_deployment( dev.fake_token, artifact_name='different {}'.format(artifact_name), device_type=dev.device_type) self.log.info('device next: %s', nextdep) assert nextdep assert dev.device_type in nextdep.artifact[ 'device_types_compatible'] # pretend our device type is different than expected nextdep = dc.get_next_deployment( dev.fake_token, artifact_name='different {}'.format(artifact_name), device_type='other {}'.format(dev.device_type)) self.log.info('device next: %s', nextdep) # TODO: this should fail or a deployment should come with a # new artifact, come back when # https://tracker.mender.io/browse/MEN-782 is resolved # assert nextdep == None # pretend we have the same artifact installed already # NOTE: asking for a deployment while having it already # installed is special in the sense that the status of # deployment for this device will be marked as 'already-installed' nextdep = dc.get_next_deployment( dev.fake_token, artifact_name=artifact_name, device_type=dev.device_type) self.log.info('device next: %s', nextdep) assert nextdep == None # verify that device status was properly recorded self.verify_deployment_stats(depid, expected={ 'already-installed': 1, })