def create_topics(user): values = flask.request.json check_json_is_valid(create_topic_schema, values) values.update(v1_utils.common_values_dict()) if user.is_not_super_admin() and user.is_not_epm() and user.is_not_feeder( ): # noqa raise dci_exc.Unauthorized() # todo(yassine): enabled when client updated. # if values['component_types'] == []: # raise dci_exc.DCIException('component_types should not be void') query = _TABLE.insert().values(**values) try: flask.g.db_conn.execute(query) except sa_exc.IntegrityError: raise dci_exc.DCICreationConflict(_TABLE.name, 'name') result = json.dumps({'topic': values}) return flask.Response(result, 201, headers={'ETag': values['etag']}, content_type='application/json')
def update_product(user, product_id): # get If-Match header if_match_etag = utils.check_and_get_etag(flask.request.headers) values = flask.request.json check_json_is_valid(update_product_schema, values) if user.is_not_super_admin(): raise dci_exc.Unauthorized() v1_utils.verify_existence_and_get(product_id, _TABLE) values['etag'] = utils.gen_etag() where_clause = sql.and_(_TABLE.c.etag == if_match_etag, _TABLE.c.id == product_id) query = _TABLE.update().returning(*_TABLE.columns).\ where(where_clause).values(**values) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('Product update error', product_id) return flask.Response(json.dumps({'product': result.fetchone()}), 200, headers={'ETag': values['etag']}, content_type='application/json')
def create_feeders(user): values = flask.request.json check_json_is_valid(create_feeder_schema, values) values.update(v1_utils.common_values_dict()) if user.is_not_epm() and user.is_not_super_admin(): raise dci_exc.Unauthorized() values.update({ # XXX(fc): this should be populated as a default value from the # model, but we don't return values from the database :( 'api_secret': signature.gen_secret(), 'data': values.get('data', {}), }) query = _TABLE.insert().values(**values) try: flask.g.db_conn.execute(query) except sa_exc.IntegrityError: raise dci_exc.DCICreationConflict(_TABLE.name, 'name') return flask.Response(json.dumps({'feeder': values}), 201, headers={'ETag': values['etag']}, content_type='application/json')
def put_user(user, user_id): values = flask.request.json check_json_is_valid(update_user_schema, values) if_match_etag = utils.check_and_get_etag(flask.request.headers) # to update a user the caller must be a super admin if user.is_not_super_admin(): raise dci_exc.Unauthorized() values['etag'] = utils.gen_etag() if 'password' in values: values['password'] = auth.hash_password(values.get('password')) where_clause = sql.and_(_TABLE.c.etag == if_match_etag, _TABLE.c.id == user_id) query = _TABLE.update().returning(*_TABLE.columns).\ where(where_clause).values(**values) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('User', user_id) _result = dict(result.fetchone()) del _result['password'] return flask.Response(json.dumps({'user': _result}), 200, headers={'ETag': values['etag']}, content_type='application/json')
def update_components(user, c_id): component = v1_utils.verify_existence_and_get(c_id, _TABLE) if_match_etag = utils.check_and_get_etag(flask.request.headers) topic = v1_utils.verify_existence_and_get(component['topic_id'], models.TOPICS) export_control.verify_access_to_topic(user, topic) values = flask.request.json check_json_is_valid(update_component_schema, values) values['etag'] = utils.gen_etag() where_clause = sql.and_( _TABLE.c.etag == if_match_etag, _TABLE.c.id == c_id ) query = _TABLE.update().returning(*_TABLE.columns).where(where_clause).\ values(**values) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('Component', c_id) return flask.Response( json.dumps({'component': result.fetchone()}), 200, headers={'ETag': values['etag']}, content_type='application/json' )
def upload_certification(user, file_id): data = flask.request.json check_json_is_valid(file_upload_certification_schema, data) file = get_file_object(file_id) file_descriptor = get_file_descriptor(file) file_content = file_descriptor.read() username = data['username'] password = data['password'] conf = dci_config.CONFIG proxy = ServerProxy(conf['CERTIFICATION_URL']) certification_details = proxy.Cert.getOpenStack_4_7({ 'username': username, 'password': password, 'certification_id': data['certification_id'] }) certification = build_certification(username, password, certification_details['cert_nid'], file['name'], file_content) proxy.Cert.uploadTestLog(certification) return flask.Response(None, 204, content_type='application/json')
def create_users(user): values = flask.request.json check_json_is_valid(create_user_schema, values) values.update(v1_utils.common_values_dict()) if user.is_not_super_admin(): raise dci_exc.Unauthorized() values.update({ 'password': auth.hash_password(values.get('password')), 'fullname': values.get('fullname', values['name']), 'timezone': values.get('timezone', 'UTC'), 'sso_username': None }) query = _TABLE.insert().values(**values) try: flask.g.db_conn.execute(query) except sa_exc.IntegrityError: raise dci_exc.DCICreationConflict(_TABLE.name, 'name') # remove the password in the result for security reasons del values['password'] return flask.Response(json.dumps({'user': values}), 201, headers={'ETag': values['etag']}, content_type='application/json')
def attach_issue(resource_id, table, user_id): data = flask.request.json check_json_is_valid(issue_schema, data) issue = _get_or_create_issue(data) # Second, insert a join record in the JOIN_JOBS_ISSUES or # JOIN_COMPONENTS_ISSUES database. if table.name == 'jobs': join_table = models.JOIN_JOBS_ISSUES else: join_table = models.JOIN_COMPONENTS_ISSUES key = '%s_id' % table.name[0:-1] query = join_table.insert().values({ 'user_id': user_id, 'issue_id': issue['id'], key: resource_id }) try: flask.g.db_conn.execute(query) except sa_exc.IntegrityError: raise dci_exc.DCICreationConflict(join_table.name, '%s, issue_id' % key) result = json.dumps({'issue': dict(issue)}) return flask.Response(result, 201, content_type='application/json')
def test_check_json_is_valid(): schema = {"type": "object", "properties": {"name": Properties.string}} try: check_json_is_valid(schema, {"name": "foo"}) except DCIException: pytest.fail( "check_json_is_valid raises DCIException and it should not")
def put_feeder(user, f_id): if_match_etag = utils.check_and_get_etag(flask.request.headers) values = flask.request.json check_json_is_valid(update_feeder_schema, values) feeder = v1_utils.verify_existence_and_get(f_id, _TABLE) if not user.is_in_team(feeder['team_id']): raise dci_exc.Unauthorized() values['etag'] = utils.gen_etag() where_clause = sql.and_(_TABLE.c.etag == if_match_etag, _TABLE.c.state != 'archived', _TABLE.c.id == f_id) query = (_TABLE.update().returning( *_TABLE.columns).where(where_clause).values(**values)) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('Feeder', f_id) _result = dict(result.fetchone()) del _result['api_secret'] return flask.Response(json.dumps({'feeder': _result}), 200, headers={'ETag': values['etag']}, content_type='application/json')
def create_jobs(user): values = flask.request.json check_json_is_valid(create_job_schema, values) values.update(v1_utils.common_values_dict()) components_ids = values.pop('components') if user.is_not_remoteci(): raise dci_exc.DCIException('Only remoteci can create job') topic_id = values.get('topic_id') topic = v1_utils.verify_existence_and_get(topic_id, models.TOPICS) export_control.verify_access_to_topic(user, topic) previous_job_id = values.get('previous_job_id') if previous_job_id: v1_utils.verify_existence_and_get(previous_job_id, _TABLE) values.update({ 'status': 'new', 'remoteci_id': user.id, 'topic_id': topic_id, 'user_agent': flask.request.environ.get('HTTP_USER_AGENT'), 'client_version': flask.request.environ.get('HTTP_CLIENT_VERSION'), 'previous_job_id': previous_job_id, 'team_id': user.teams_ids[0], 'product_id': topic['product_id'], 'duration': 0 }) # create the job and feed the jobs_components table with flask.g.db_conn.begin(): query = _TABLE.insert().values(**values) flask.g.db_conn.execute(query) jobs_components_to_insert = [] for cmpt_id in components_ids: v1_utils.verify_existence_and_get(cmpt_id, models.COMPONENTS) jobs_components_to_insert.append({ 'job_id': values['id'], 'component_id': cmpt_id }) if jobs_components_to_insert: flask.g.db_conn.execute(models.JOIN_JOBS_COMPONENTS.insert(), jobs_components_to_insert) return flask.Response(json.dumps({'job': values}), 201, headers={'ETag': values['etag']}, content_type='application/json')
def create_issue(user): data = flask.request.json check_json_is_valid(issue_test_schema, data) issue = _get_or_create_issue(data) result = json.dumps({'issue': dict(issue)}) return flask.Response(result, 201, headers={'ETag': issue['etag']}, content_type='application/json')
def test_create_user_schema_required_value(user_json): with pytest.raises(DCIException) as e: check_json_is_valid(create_user_schema, {}) result = e.value assert result.status_code == 400 assert len(result.payload["errors"]) == len(user_json.keys()) errors = "\n".join(result.payload["errors"]) for key in user_json.keys(): assert "'%s' is a required property" % key in errors
def create_new_upgrade_job_from_an_existing_job(user): """Create a new job in the 'next topic' of the topic of the provided job_id.""" values = flask.request.json check_json_is_valid(upgrade_job_schema, values) values.update({ 'id': utils.gen_uuid(), 'created_at': get_utc_now().isoformat(), 'updated_at': get_utc_now().isoformat(), 'etag': utils.gen_etag(), 'status': 'new' }) original_job_id = values.pop('job_id') original_job = v1_utils.verify_existence_and_get(original_job_id, models.JOBS) if user.is_not_in_team(original_job['team_id']) and user.is_not_epm(): raise dci_exc.Unauthorized() # get the remoteci remoteci_id = str(original_job['remoteci_id']) remoteci = v1_utils.verify_existence_and_get(remoteci_id, models.REMOTECIS) values.update({'remoteci_id': remoteci_id}) # get the associated topic topic_id = str(original_job['topic_id']) topic = v1_utils.verify_existence_and_get(topic_id, models.TOPICS) values.update({ 'user_agent': flask.request.environ.get('HTTP_USER_AGENT'), 'client_version': flask.request.environ.get('HTTP_CLIENT_VERSION'), }) next_topic_id = topic['next_topic_id'] if not next_topic_id: raise dci_exc.DCIException("topic %s does not contains a next topic" % topic_id) topic = v1_utils.verify_existence_and_get(next_topic_id, models.TOPICS) product_id = topic['product_id'] # instantiate a new job in the next_topic_id # todo(yassine): make possible the upgrade to choose specific components values = _build_job(product_id, next_topic_id, remoteci, [], values, previous_job_id=original_job_id) return flask.Response(json.dumps({'job': values}), 201, headers={'ETag': values['etag']}, content_type='application/json')
def test_check_json_is_valid_check_positive_or_null_integer_type(): schema = { "type": "object", "properties": { "positive_or_null_integer": Properties.positive_or_null_integer }, "required": ["positive_or_null_integer"], } try: check_json_is_valid(schema, {"positive_or_null_integer": 0}) except DCIException: pytest.fail("positive_or_null_integer() is invalid")
def test_update_user_schema(): try: check_json_is_valid( update_user_schema, { "id": "909b4ad1-1c38-4fc3-9454-57dc6d80b44d", "etag": "8407cdbf-04d1-4453-8d35-19e4425c535b", "name": "jdoe", "fullname": "John Doe", "email": "*****@*****.**", }) except DCIException: pytest.fail("update_user_schema is invalid")
def test_allow_none_values(): schema = { "type": "object", "properties": { "foo": allow_none(Properties.uuid) }, "required": [], } try: check_json_is_valid(schema, {"foo": None}) except DCIException: pytest.fail("allow None for foo doesn't work")
def compare_performance(user): values = flask.request.json check_json_is_valid(performance_schema, values) base_job_id = values["base_job_id"] jobs_ids = values["jobs"] tests_filenames = _get_tests_filenames(base_job_id) res = [] for tf in tests_filenames: baseline_tests, tests = _get_test_files(base_job_id, jobs_ids, tf) # noqa baseline_tests_file_with_fd, tests_files_with_fds = _get_test_files_with_fds(baseline_tests, tests) # noqa perf_res = get_performance_tests(baseline_tests_file_with_fd, tests_files_with_fds) res.append({tf: perf_res}) return flask.jsonify({"performance": res}), 200
def test_check_json_is_valid_required_field(): schema = { "type": "object", "properties": { "name": Properties.string }, "required": ["name"], } with pytest.raises(DCIException) as e: check_json_is_valid(schema, {}) result = e.value assert result.status_code == 400 assert len(result.payload["errors"]) == 1 assert result.payload["errors"][0] == "'name' is a required property" assert result.message == "Request malformed"
def add_test_to_issue(user, issue_id): values = flask.request.json check_json_is_valid(issue_test_schema, values) issue_id = v1_utils.verify_existence_and_get(issue_id, _TABLE, get_id=True) values['issue_id'] = issue_id v1_utils.verify_existence_and_get(values.get('test_id'), models.TESTS, get_id=True) q_insert = models.JOIN_ISSUES_TESTS.insert().values(**values) flask.g.db_conn.execute(q_insert) return flask.Response(json.dumps(values), 201, content_type='application/json')
def test_check_json_is_valid_no_additional_properties(): schema = { "type": "object", "properties": { "name": Properties.string }, "additionalProperties": False, } with pytest.raises(DCIException) as e: check_json_is_valid(schema, {"name": "foo", "age": 32}) result = e.value assert result.status_code == 400 assert len(result.payload["errors"]) == 1 assert (result.payload["errors"][0] == "Additional properties are not allowed ('age' was unexpected)") assert result.message == "Request malformed"
def create_jobstates(user): values = flask.request.json check_json_is_valid(jobstate_schema, values) # if one create a 'failed' jobstates and the current state is either # 'run' or 'pre-run' then set the job to 'error' state job_id = values.get('job_id') job = v1_utils.verify_existence_and_get(job_id, models.JOBS) job = dict(job) if values.get('status') in ['failure', 'error']: if job['status'] in ['new', 'pre-run']: values['status'] = 'error' insert_jobstate(user, values) # Update job status job_duration = datetime.datetime.utcnow() - job['created_at'] query_update_job = (models.JOBS.update().where( sql.and_(models.JOBS.c.id == job_id, models.JOBS.c.status != values.get('status'))).values( status=values.get('status'), duration=job_duration.seconds)) result = flask.g.db_conn.execute(query_update_job) # send notification in case of final jobstate status if result.rowcount and values.get('status') in models.FINAL_STATUSES: embeds = ['components', 'topic', 'remoteci', 'results'] embeds_many = { 'components': True, 'topic': False, 'remoteci': False, 'results': True } job = base.get_resource_by_id(user, job, models.JOBS, embed_many=embeds_many, embeds=embeds, jsonify=False) job = dict(job) jobs_events.create_event(job['id'], values['status'], job['topic_id']) if values.get('status') in models.FINAL_FAILURE_STATUSES: notifications.dispatcher(job) result = json.dumps({'jobstate': values}) return flask.Response(result, 201, content_type='application/json')
def test_default_values_string_value(): schema = { "type": "object", "properties": { "foo": with_default(Properties.string, "bar") }, "required": [], } try: obj = {} check_json_is_valid(schema, obj) assert obj == {"foo": "bar"} obj = {"foo": "foo"} check_json_is_valid(schema, obj) assert obj == {"foo": "foo"} except DCIException: pytest.fail("default string value doesn't work")
def test_default_values_boolean_value(): schema = { "type": "object", "properties": { "foo": with_default(Properties.boolean, False) }, "required": [], } try: obj = {} check_json_is_valid(schema, obj) assert obj == {"foo": False} obj = {"foo": True} check_json_is_valid(schema, obj) assert obj == {"foo": True} except DCIException: pytest.fail("default boolean value doesn't work")
def create_components(user): values = flask.request.json check_json_is_valid(create_component_schema, values) values.update(v1_utils.common_values_dict()) if str(values['topic_id']) not in v1_utils.user_topic_ids(user): raise dci_exc.Unauthorized() query = _TABLE.insert().values(**values) try: flask.g.db_conn.execute(query) except sa_exc.IntegrityError: raise dci_exc.DCICreationConflict(_TABLE.name, 'name') result = json.dumps({'component': values}) return flask.Response(result, 201, content_type='application/json')
def test_default_values_none_value(): schema = { "type": "object", "properties": { "foo": with_default(Properties.uuid, None) }, "required": [], } try: obj = {} check_json_is_valid(schema, obj) assert obj == {"foo": None} obj = {"foo": "b82dca4a-0597-4c70-b90f-0c422fc05c38"} check_json_is_valid(schema, obj) assert obj == {"foo": "b82dca4a-0597-4c70-b90f-0c422fc05c38"} except DCIException: pytest.fail("default None value doesn't work")
def test_default_values_array_value(): schema = { "type": "object", "properties": { "foo": with_default(Properties.array, []) }, "required": [], } try: obj = {} check_json_is_valid(schema, obj) assert obj == {"foo": []} obj = {"foo": ["bar"]} check_json_is_valid(schema, obj) assert obj == {"foo": ["bar"]} except DCIException: pytest.fail("default array value doesn't work")
def test_check_json_is_valid_check_url_type(): schema = { "type": "object", "properties": { "url": Properties.url }, "required": ["url"], } try: check_json_is_valid(schema, {"url": "https://distributed-ci.io"}) except DCIException: pytest.fail("url() is invalid") with pytest.raises(DCIException) as e: check_json_is_valid(schema, {"url": "not an url"}) errors = e.value.payload["errors"] assert len(errors) == 1 assert errors[0] == "url: 'not an url' is not a valid 'url'"
def test_check_json_is_valid_check_string_type(): schema = { "type": "object", "properties": { "name": Properties.string }, "required": ["name"], } try: check_json_is_valid(schema, {"name": "good string"}) except DCIException: pytest.fail("string() is invalid") with pytest.raises(DCIException) as e: check_json_is_valid(schema, {"name": None}) errors = e.value.payload["errors"] assert len(errors) == 1 assert errors[0] == "name: None is not of type 'string'"
def test_check_json_is_valid_check_enum_type(): schema = { "type": "object", "properties": { "status": Properties.enum(["success", "error"]) }, "required": ["status"], } try: check_json_is_valid(schema, {"status": "success"}) except DCIException: pytest.fail("string() is invalid") with pytest.raises(DCIException) as e: check_json_is_valid(schema, {"status": "running"}) errors = e.value.payload["errors"] assert len(errors) == 1 assert errors[0] == "'running' is not one of ['success', 'error']"