diff --git a/pyhelm/repo.py b/pyhelm/repo.py index cf414f1..484ec78 100644 --- a/pyhelm/repo.py +++ b/pyhelm/repo.py @@ -10,7 +10,7 @@ from urllib.parse import urlparse except ImportError: from urlparse import urlparse -from posixpath import join as urljoin # https://stackoverflow.com/a/15279799 +from posixpath import join as urljoin # https://stackoverflow.com/a/15279799 class HTTPGetError(RuntimeError): @@ -49,6 +49,7 @@ def __init__(self, repository): def _semver_sorter(x): return list(map(int, ''.join(i for i in x['version'] if i in '0123456789.').split('.'))) + def _get_from_http(repo_url, file_url, **kwargs): """Downloads the Chart's repo index from HTTP(S)""" @@ -60,6 +61,7 @@ def _get_from_http(repo_url, file_url, **kwargs): raise HTTPGetError(file_url, index.status_code, index.text) return index.content + def _get_from_s3(repo_url, file_url): """Download the index / Chart from S3 bucket""" import boto3.s3 @@ -94,9 +96,30 @@ def _get_from_s3(repo_url, file_url): else: raise + +def _get_from_gcs(repo_url, file_url): + """ + Download the index / Chart from GCS bucket + + https://bucket-name.storage.googleapis.com/ + """ + from google.cloud import storage + + repo_url_parsed = urlparse(repo_url) + bucket_name = repo_url_parsed.netloc + + file_url_parsed = urlparse(file_url) + file_name = file_url_parsed.path.strip("/") + + storage_client = storage.Client() + bucket = storage_client.get_bucket(bucket_name) + blob = bucket.blob(file_name) + + return blob.download_as_string() + + def _get_from_repo(repo_scheme, repo_url, file_url, **kwargs): """Wrap download from specific repository""" - if repo_scheme == 's3': return _get_from_s3( repo_url, @@ -108,9 +131,12 @@ def _get_from_repo(repo_scheme, repo_url, file_url, **kwargs): file_url, **kwargs ) + elif repo_scheme == "gs": + return _get_from_gcs(repo_url, file_url) else: raise SchemeError(repo_scheme.upper()) + def repo_index(repo_url, headers=None): """Downloads the Chart's repo index""" repo_scheme = urlparse(repo_url).scheme @@ -124,6 +150,7 @@ def repo_index(repo_url, headers=None): ) ) + def from_repo(repo_url, chart, version=None, headers=None): """Downloads the chart from a repo to a temporary dir, the path of which is determined by the platform. @@ -165,7 +192,7 @@ def git_clone(repo_url, branch='master', path=''): """clones repo to a temporary dir, the path of which is determined by the platform""" _tmp_dir = tempfile.mkdtemp(prefix='pyhelm-') - repo = Repo.clone_from(repo_url, _tmp_dir, branch=branch) + Repo.clone_from(repo_url, _tmp_dir, branch=branch) return os.path.join(_tmp_dir, path) diff --git a/requirements.txt b/requirements.txt index 117a3fc..49836f3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ requests PyYAML boto3 botocore +google-cloud-storage==1.20.0 diff --git a/tests/test_repo.py b/tests/test_repo.py index 0d32b21..4235d3d 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -110,3 +110,25 @@ def test_get_from_s3_client_error(self, mocked_s3client): {'Error': {'Code': ''}}, '') with self.assertRaises(ClientError): repo._get_from_repo('s3', 'test', 'foo') + + def test_get_from_gcs_ok(self, mocked_gcsclient): + repo._get_from_repo('gcs', 'test', 'bar') + mocked_gcsclient.return_value.get_object.assert_called() + + def test_get_from_gcs_repo_error(self, mocked_gcsclient): + mocked_gcsclient.return_value.get_object.side_effect = ClientError( + {'Error': {'Code': 'NoSuchBucket'}}, '') + with self.assertRaises(repo.RepositoryError): + repo._get_from_repo('gcs', 'test', 'foo') + + def test_get_from_gcs_chart_error(self, mocked_gcsclient): + mocked_gcsclient.return_value.get_object.side_effect = ClientError( + {'Error': {'Code': 'NoSuchKey'}}, '') + with self.assertRaises(repo.ChartError): + repo._get_from_repo('gcs', 'test', 'foo') + + def test_get_from_gcs_client_error(self, mocked_gcsclient): + mocked_gcsclient.return_value.get_object.side_effect = ClientError( + {'Error': {'Code': ''}}, '') + with self.assertRaises(ClientError): + repo._get_from_repo('gcs', 'test', 'foo')