Skip to content

Commit

Permalink
Merge "Add an endpoint to proxy downloads via rhdl"
Browse files Browse the repository at this point in the history
  • Loading branch information
Zuul CI authored and Gerrit Code Review committed Sep 4, 2024
2 parents 0ffa7da + b6a122c commit 0f16dbc
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 3 deletions.
37 changes: 37 additions & 0 deletions dci/api/v2/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2024 Red Hat, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import flask
from flask import json

import logging

logger = logging.getLogger(__name__)

api = flask.Blueprint("api_v2", __name__)


@api.route("/", strict_slashes=False)
def index():
logger.info("control server is ok...")
return flask.Response(
json.dumps({"_status": "OK", "message": "Distributed CI.", "version": "2"}),
status=200,
content_type="application/json",
)


import dci.api.v2.components # noqa
63 changes: 63 additions & 0 deletions dci/api/v2/components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2024 Red Hat, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import os

import flask
import logging
import requests

from dci.api.v2 import api
from dci.api.v1 import base
from dci.api.v1.components import _verify_component_and_topic_access
from dci import decorators
from dci.common import exceptions as dci_exc
from dci.dci_config import CONFIG
from dci.db import models2


logger = logging.getLogger(__name__)


@api.route("/components/<uuid:c_id>/files/<path:filepath>", methods=["GET", "HEAD"])
@decorators.login_required
def get_component_file_from_rhdl(user, c_id, filepath):
component = base.get_resource_orm(models2.Component, c_id)
_verify_component_and_topic_access(user, component)

if filepath == "dci_files_list.json":
filepath = "rhdl_files_list.json"
normalized_filepath = os.path.normpath("/" + filepath).lstrip("/")
normalized_rhdl_component_filepath = os.path.join(
component.display_name, "files", normalized_filepath
)
rhdl_component_filepath = os.path.join(component.display_name, "files", filepath)
if rhdl_component_filepath != normalized_rhdl_component_filepath:
raise dci_exc.DCIException("Request malformed: filepath is invalid")

rhdl_file_url = os.path.join(
CONFIG["RHDL_API_URL"], "components", normalized_rhdl_component_filepath
)

redirect = requests.get(
rhdl_file_url, allow_redirects=False, timeout=CONFIG["REQUESTS_TIMEOUT"]
)
if redirect.status_code != 302:
raise dci_exc.DCIException(
message=redirect.content, status_code=redirect.status_code
)

return flask.Response(None, 302, headers={"Location": redirect.headers["Location"]})
4 changes: 3 additions & 1 deletion dci/app.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015-2016 Red Hat, Inc
# Copyright (C) 2015-2024 Red Hat, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
Expand All @@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from dci.api import v1 as api_v1
from dci.api import v2 as api_v2
from dci.common import exceptions
from dci.common import utils
from dci.db import models2
Expand Down Expand Up @@ -167,6 +168,7 @@ def teardown_request(_):

# Registering REST API v1
dci_app.register_blueprint(api_v1.api, url_prefix="/api/v1")
dci_app.register_blueprint(api_v2.api, url_prefix="/api/v2")

# Registering custom encoder
dci_app.json_encoder = utils.JSONEncoder
Expand Down
6 changes: 5 additions & 1 deletion dci/settings.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- encoding: utf-8 -*-
#
# Copyright 2015-2016 Red Hat, Inc.
# Copyright 2015-2024 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
Expand Down Expand Up @@ -115,3 +115,7 @@
SSO_REALM = os.getenv("SSO_REALM", "redhat-external")

CERTIFICATION_URL = "https://access.stage.redhat.com/hydra/rest/cwe/xmlrpc/v2"

RHDL_API_URL = "https://rhdl.distributed-ci.io/api/v1"

REQUESTS_TIMEOUT = (3.0, 10.0)
3 changes: 2 additions & 1 deletion test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ mock
pytest
dciclient
flake8
tox
tox
responses
105 changes: 105 additions & 0 deletions tests/api/v2/test_components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2024 Red Hat, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import responses

from dci import dci_config


@responses.activate
def test_get_component_file_from_rhdl_user_team_in_RHEL_with_released_component(
admin,
remoteci_context,
remoteci_user,
rhel_product,
rhel_80_component,
):
rhdl_api_url = dci_config.CONFIG["RHDL_API_URL"]
rhdl_composeinfo_url = (
f"{rhdl_api_url}/components/{rhel_80_component['name']}/files/.composeinfo"
)

responses.add(
method=responses.GET,
url=rhdl_composeinfo_url,
status=302,
headers={
"Location": "https://wedontcare",
},
)
responses.add(
method=responses.HEAD,
url=rhdl_composeinfo_url,
status=302,
headers={
"Location": "https://wedontcare",
},
)

r = remoteci_context.get(
f"/api/v2/components/{rhel_80_component['id']}/files/.composeinfo"
)
assert r.status_code == 302
assert r.headers["Location"] is not None
assert responses.assert_call_count(rhdl_composeinfo_url, 1) is True

r = remoteci_context.head(
f"/api/v2/components/{rhel_80_component['id']}/files/.composeinfo"
)
assert r.status_code == 302
assert r.headers["Location"] is not None

assert responses.assert_call_count(rhdl_composeinfo_url, 2) is True

# delete product team permission
r = admin.delete(
"/api/v1/products/%s/teams/%s" % (rhel_product["id"], remoteci_user["team_id"]),
)
assert r.status_code == 204

r = remoteci_context.get(
"/api/v1/components/%s/files/.composeinfo" % rhel_80_component["id"]
)
assert r.status_code == 401

r = remoteci_context.head(
"/api/v1/components/%s/files/.composeinfo" % rhel_80_component["id"]
)
assert r.status_code == 401


@responses.activate
def test_get_files_list_from_rhdl_renames_files_list(
remoteci_context,
rhel_80_component,
):
rhdl_api_url = dci_config.CONFIG["RHDL_API_URL"]
rhdl_files_list_url = f"{rhdl_api_url}/components/{rhel_80_component['name']}/files/rhdl_files_list.json"
responses.add(
method=responses.GET,
url=rhdl_files_list_url,
status=302,
headers={
"Location": "https://wedontcare",
},
)

r = remoteci_context.get(
f"/api/v2/components/{rhel_80_component['id']}/files/dci_files_list.json"
)
assert r.status_code == 302
assert r.headers["Location"] is not None
assert responses.assert_call_count(rhdl_files_list_url, 1) is True

0 comments on commit 0f16dbc

Please sign in to comment.