Skip to content

Commit 892281e

Browse files
feat(api): add support for remote project import from AWS S3 (#2357)
1 parent e5dc72d commit 892281e

File tree

4 files changed

+141
-4
lines changed

4 files changed

+141
-4
lines changed

docs/gl_objects/projects.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,20 @@ Import the project using file stored on a remote URL::
352352
override_params={'visibility': 'private'},
353353
)
354354

355+
Import the project using file stored on AWS S3::
356+
357+
output = gl.projects.remote_import_s3(
358+
path="my_new_remote_project",
359+
region="aws-region",
360+
bucket_name="aws-bucket-name",
361+
file_key="aws-file-key",
362+
access_key_id="aws-access-key-id",
363+
secret_access_key="secret-access-key",
364+
name="My New Remote Project",
365+
namespace="my-group",
366+
override_params={'visibility': 'private'},
367+
)
368+
355369
Project custom attributes
356370
=========================
357371

gitlab/v4/objects/projects.py

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,7 @@ class ProjectManager(CRUDMixin, RESTManager):
807807
def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Project:
808808
return cast(Project, super().get(id=id, lazy=lazy, **kwargs))
809809

810+
@exc.on_http_error(exc.GitlabImportError)
810811
def import_project(
811812
self,
812813
file: str,
@@ -833,7 +834,7 @@ def import_project(
833834
834835
Raises:
835836
GitlabAuthenticationError: If authentication is not correct
836-
GitlabListError: If the server failed to perform the request
837+
GitlabImportError: If the server failed to perform the request
837838
838839
Returns:
839840
A representation of the import status.
@@ -851,6 +852,7 @@ def import_project(
851852
"/projects/import", post_data=data, files=files, **kwargs
852853
)
853854

855+
@exc.on_http_error(exc.GitlabImportError)
854856
def remote_import(
855857
self,
856858
url: str,
@@ -877,7 +879,7 @@ def remote_import(
877879
878880
Raises:
879881
GitlabAuthenticationError: If authentication is not correct
880-
GitlabListError: If the server failed to perform the request
882+
GitlabImportError: If the server failed to perform the request
881883
882884
Returns:
883885
A representation of the import status.
@@ -894,6 +896,66 @@ def remote_import(
894896
"/projects/remote-import", post_data=data, **kwargs
895897
)
896898

899+
@exc.on_http_error(exc.GitlabImportError)
900+
def remote_import_s3(
901+
self,
902+
path: str,
903+
region: str,
904+
bucket_name: str,
905+
file_key: str,
906+
access_key_id: str,
907+
secret_access_key: str,
908+
name: Optional[str] = None,
909+
namespace: Optional[str] = None,
910+
overwrite: bool = False,
911+
override_params: Optional[Dict[str, Any]] = None,
912+
**kwargs: Any,
913+
) -> Union[Dict[str, Any], requests.Response]:
914+
"""Import a project from an archive file stored on AWS S3.
915+
916+
Args:
917+
region: AWS S3 region name where the file is stored
918+
bucket_name: AWS S3 bucket name where the file is stored
919+
file_key: AWS S3 file key to identify the file.
920+
access_key_id: AWS S3 access key ID.
921+
secret_access_key: AWS S3 secret access key.
922+
path: Name and path for the new project
923+
name: The name of the project to import. If not provided,
924+
defaults to the path of the project.
925+
namespace: The ID or path of the namespace that the project
926+
will be imported to
927+
overwrite: If True overwrite an existing project with the
928+
same path
929+
override_params: Set the specific settings for the project
930+
**kwargs: Extra options to send to the server (e.g. sudo)
931+
932+
Raises:
933+
GitlabAuthenticationError: If authentication is not correct
934+
GitlabImportError: If the server failed to perform the request
935+
936+
Returns:
937+
A representation of the import status.
938+
"""
939+
data = {
940+
"region": region,
941+
"bucket_name": bucket_name,
942+
"file_key": file_key,
943+
"access_key_id": access_key_id,
944+
"secret_access_key": secret_access_key,
945+
"path": path,
946+
"overwrite": str(overwrite),
947+
}
948+
if override_params:
949+
for k, v in override_params.items():
950+
data[f"override_params[{k}]"] = v
951+
if name is not None:
952+
data["name"] = name
953+
if namespace:
954+
data["namespace"] = namespace
955+
return self.gitlab.http_post(
956+
"/projects/remote-import-s3", post_data=data, **kwargs
957+
)
958+
897959
def import_bitbucket_server(
898960
self,
899961
bitbucket_server_url: str,

tests/functional/api/test_import_export.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def test_project_import_export(gl, project, temp_dir):
6969

7070

7171
def test_project_remote_import(gl):
72-
with pytest.raises(gitlab.exceptions.GitlabHttpError) as err_info:
72+
with pytest.raises(gitlab.exceptions.GitlabImportError) as err_info:
7373
gl.projects.remote_import(
7474
"ftp://whatever.com/url", "remote-project", "remote-project", "root"
7575
)
@@ -78,3 +78,23 @@ def test_project_remote_import(gl):
7878
"File url is blocked: Only allowed schemes are https"
7979
in err_info.value.error_message
8080
)
81+
82+
83+
def test_project_remote_import_s3(gl):
84+
gl.features.set("import_project_from_remote_file_s3", True)
85+
with pytest.raises(gitlab.exceptions.GitlabImportError) as err_info:
86+
gl.projects.remote_import_s3(
87+
"remote-project",
88+
"aws-region",
89+
"aws-bucket-name",
90+
"aws-file-key",
91+
"aws-access-key-id",
92+
"secret-access-key",
93+
"remote-project",
94+
"root",
95+
)
96+
assert err_info.value.response_code == 400
97+
assert (
98+
"Failed to open 'aws-file-key' in 'aws-bucket-name'"
99+
in err_info.value.error_message
100+
)

tests/unit/objects/test_project_import_export.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,30 @@ def resp_remote_import():
5353
yield rsps
5454

5555

56+
@pytest.fixture
57+
def resp_remote_import_s3():
58+
content = {
59+
"id": 1,
60+
"description": None,
61+
"name": "remote-project-s3",
62+
"name_with_namespace": "Administrator / remote-project-s3",
63+
"path": "remote-project-s3",
64+
"path_with_namespace": "root/remote-project-s3",
65+
"created_at": "2018-02-13T09:05:58.023Z",
66+
"import_status": "scheduled",
67+
}
68+
69+
with responses.RequestsMock() as rsps:
70+
rsps.add(
71+
method=responses.POST,
72+
url="http://localhost/api/v4/projects/remote-import-s3",
73+
json=content,
74+
content_type="application/json",
75+
status=200,
76+
)
77+
yield rsps
78+
79+
5680
@pytest.fixture
5781
def resp_import_status():
5882
content = {
@@ -125,7 +149,24 @@ def test_import_project(gl, resp_import_project):
125149

126150
def test_remote_import(gl, resp_remote_import):
127151
project_import = gl.projects.remote_import(
128-
"https://whatever.com/url", "remote-project", "remote-project", "root"
152+
"https://whatever.com/url/file.tar.gz",
153+
"remote-project",
154+
"remote-project",
155+
"root",
156+
)
157+
assert project_import["import_status"] == "scheduled"
158+
159+
160+
def test_remote_import_s3(gl, resp_remote_import_s3):
161+
project_import = gl.projects.remote_import_s3(
162+
"remote-project",
163+
"aws-region",
164+
"aws-bucket-name",
165+
"aws-file-key",
166+
"aws-access-key-id",
167+
"secret-access-key",
168+
"remote-project",
169+
"root",
129170
)
130171
assert project_import["import_status"] == "scheduled"
131172

0 commit comments

Comments
 (0)