diff --git a/rapyuta_io/__init__.py b/rapyuta_io/__init__.py index c18d78bf..27cf0996 100644 --- a/rapyuta_io/__init__.py +++ b/rapyuta_io/__init__.py @@ -11,8 +11,7 @@ BuildOptions from .clients.buildoperation import BuildOperation, BuildOperationInfo from .clients.project import Project -from .clients.secret import Secret, SecretConfigSourceSSHAuth, SecretConfigDocker, \ - SecretConfigSourceBasicAuth +from .clients.secret import Secret, SecretConfigDocker from .clients.rosbag import UploadOptions from .clients.user_group import UserGroup diff --git a/rapyuta_io/clients/secret.py b/rapyuta_io/clients/secret.py index cae9a76c..4c45ffd7 100644 --- a/rapyuta_io/clients/secret.py +++ b/rapyuta_io/clients/secret.py @@ -29,7 +29,7 @@ class Secret(ObjBase): :param name: Name of the Secret :type name: str :param secret_config: Secret Configuration - :type secret_config: Union[:py:class:`~rapyuta_io.clients.secret.SecretConfigSourceSSHAuth`, :py:class:`~rapyuta_io.clients.secret.SecretConfigSourceBasicAuth`, :py:class:`~rapyuta_io.clients.secret.SecretConfigDocker`] + :type secret_config: Union[:py:class:`~rapyuta_io.clients.secret.SecretConfigDocker`] """ SECRET_PATH = '/api/secret' @@ -111,8 +111,6 @@ def __str__(self): return str(self.value) DOCKER = 'kubernetes.io/dockercfg' - SOURCE_BASIC_AUTH = 'kubernetes.io/basic-auth' - SOURCE_SSH_AUTH = 'kubernetes.io/ssh-auth' class _SecretConfigBase(six.with_metaclass(ABCMeta, ObjBase)): @@ -132,77 +130,6 @@ def get_serialize_map(self): pass -class SecretConfigSourceSSHAuth(_SecretConfigBase): - """ - SecretConfigSSHAuth represents Source Secret with SSH Authentication. This type of secrets can be used to access - private Git repositories using SSH, for building the Docker images from Source Code. - - :param ssh_key: Private SSH key for authenticating with the Git repository hosting - :type ssh_key: str - """ - - def __init__(self, ssh_key): - self.validate(ssh_key) - self.ssh_key = ssh_key - - @staticmethod - def validate(ssh_key): - if not (isinstance(ssh_key, str) or isinstance(ssh_key, six.string_types)) or ssh_key == '': - raise InvalidParameterException('ssh_key cannot be empty') - - @classmethod - def get_type(cls): - return SecretType.SOURCE_SSH_AUTH - - def serialize(self): - return { - 'ssh-privatekey': base64.b64encode(self.ssh_key.encode()).decode() - } - - -class SecretConfigSourceBasicAuth(_SecretConfigBase): - """ - SecretConfigSourceBasicAuth represents Source Secret with Basic Authentication. This type of secrets can be used to - access private Git repositories exposing HTTP interface, for building the Docker images from Source Code. - - :param username: Username for the Git repository hosting - :type username: str - :param password: Password for the Git repository hosting - :type password: str - :param ca_cert: If the Git repository is using self-signed certificates, a CA Root Certificate can optionally be provided. - :type ca_cert: str - """ - - def __init__(self, username, password, ca_cert=None): - self.validate(username, password, ca_cert) - self.username = username - self.password = password - self.ca_cert = ca_cert - - @staticmethod - def validate(username, password, ca_cert): - if not isinstance(username, six.string_types) or username == '': - raise InvalidParameterException('username cannot be empty') - if not isinstance(password, six.string_types) or password == '': - raise InvalidParameterException('password cannot be empty') - if ca_cert is not None and (not isinstance(ca_cert, six.string_types) or ca_cert == ''): - raise InvalidParameterException('ca_cert cannot be empty') - - @classmethod - def get_type(cls): - return SecretType.SOURCE_BASIC_AUTH - - def serialize(self): - ret = { - 'username': base64.b64encode(self.username.encode()).decode(), - 'password': base64.b64encode(self.password.encode()).decode(), - } - if self.ca_cert is not None: - ret['ca.crt'] = base64.b64encode(self.ca_cert.encode()).decode() - - return ret - - class SecretConfigDocker(_SecretConfigBase): """ SecretConfigDocker represents Docker Secret for Docker registries. This type of secrets can be used to access diff --git a/rapyuta_io/rio_client.py b/rapyuta_io/rio_client.py index 89fa6e5f..96d48ef9 100644 --- a/rapyuta_io/rio_client.py +++ b/rapyuta_io/rio_client.py @@ -485,7 +485,7 @@ def delete_device(self, device_id): raise InvalidParameterException('device_id needs to be a non empty string') return self._dmClient.delete_device(device_id) - def toggle_features(self, device_id, features): + def toggle_features(self, device_id, features, config=None): """ Patch a device on rapyuta.io platform. @@ -493,12 +493,14 @@ def toggle_features(self, device_id, features): :type device_id: str :param features: A tuple of featues and their states :type features: list + :param config: A dict of additional feature configuration + :type config: dict Following example demonstrates how to toggle features a device. >>> from rapyuta_io import Client >>> client = Client(auth_token='auth_token', project='project_guid') - >>> client.toggle_features('device-id', [('vpn', True), ('tracing', False)]) + >>> client.toggle_features('device-id', [('vpn', True), ('tracing', False)], config={'vpn': {'advertise_routes': True}}) """ if not device_id or not isinstance(device_id, six.string_types): raise InvalidParameterException('device_id needs to be a non empty string') @@ -510,6 +512,9 @@ def toggle_features(self, device_id, features): feature, state = entry data[feature] = state + if config is not None: + data['config'] = config + return self._dmClient.patch_daemons(device_id, data) def create_package_from_manifest(self, manifest_filepath, retry_limit=0): diff --git a/sdk_test/config.py b/sdk_test/config.py index ece4a1f6..d8be9335 100644 --- a/sdk_test/config.py +++ b/sdk_test/config.py @@ -6,7 +6,7 @@ import six from six.moves import filter -from rapyuta_io import Client, SecretConfigSourceSSHAuth, SecretConfigDocker, \ +from rapyuta_io import Client, SecretConfigDocker, \ DeviceArch, Secret, Project from rapyuta_io.utils.error import InvalidParameterException from rapyuta_io.utils.utils import create_auth_header, \ @@ -154,12 +154,10 @@ def set_devices(self, devices): self._devices = list(filter(filter_devices_by_name(), devices)) def create_secrets(self): - ssh_key = self._config['git']['ssh-key'] - git_secret = self.client.create_secret(Secret('git-secret', SecretConfigSourceSSHAuth(ssh_key))) docker = self._config['docker'] docker_secret = self.client.create_secret(Secret('docker-secret', SecretConfigDocker( docker['username'], docker['password'], docker['email']))) - self._secrets = {'git': git_secret, 'docker': docker_secret} + self._secrets = {'docker': docker_secret} def delete_secrets(self): for secret in self._secrets.values(): diff --git a/sdk_test/coreapi/project_test.py b/sdk_test/coreapi/project_test.py index 9e9edc78..8b28be4f 100644 --- a/sdk_test/coreapi/project_test.py +++ b/sdk_test/coreapi/project_test.py @@ -2,7 +2,7 @@ import unittest -from rapyuta_io import Client, Project, Secret, SecretConfigSourceSSHAuth +from rapyuta_io import Client, Project, Secret, SecretConfigDocker from rapyuta_io.utils import BadRequestError from rapyuta_io.utils.utils import generate_random_value from sdk_test.config import Configuration @@ -46,6 +46,7 @@ def test_client_without_project(self): self.assertRaises( BadRequestError, lambda: client.create_secret( - Secret('test-secret', SecretConfigSourceSSHAuth('test')) + Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', + registry='quay.io')) ) ) diff --git a/sdk_test/coreapi/secret_test.py b/sdk_test/coreapi/secret_test.py index a8114cc0..79ae3244 100644 --- a/sdk_test/coreapi/secret_test.py +++ b/sdk_test/coreapi/secret_test.py @@ -1,8 +1,7 @@ from __future__ import absolute_import import unittest -from rapyuta_io import Secret, SecretConfigSourceSSHAuth, SecretConfigSourceBasicAuth, \ - SecretConfigDocker +from rapyuta_io import Secret, SecretConfigDocker from sdk_test.config import Configuration from sdk_test.util import get_logger @@ -23,26 +22,19 @@ def assertSecret(self, secret): self.assertIsNotNone(secret.created_at) self.assertIsNotNone(secret.secret_type) - def test_create_secret_source_ssh_auth(self): - self.secret = self.config.client.create_secret(Secret('ssh-auth-test', SecretConfigSourceSSHAuth('test-ssh-key'))) - self.assertSecret(self.secret) - - def test_create_secret_source_http_auth(self): - self.secret = self.config.client.create_secret(Secret('basic-auth-test', SecretConfigSourceBasicAuth('user', 'pass'))) - self.assertSecret(self.secret) def test_create_secret_docker(self): self.secret = self.config.client.create_secret(Secret('docker-test', SecretConfigDocker('user','pass', 'email'))) self.assertSecret(self.secret) def test_list_secret_docker(self): - self.secret = self.config.client.create_secret(Secret('ssh-auth-test', SecretConfigSourceSSHAuth('test-ssh-key'))) + self.secret = self.config.client.create_secret(Secret('docker-test', SecretConfigDocker('user','pass', 'email'))) secret_list = self.config.client.list_secrets() secret_list = [s for s in secret_list if s.guid == self.secret.guid] self.assertEqual(len(secret_list), 1) - def test_update_secret_source_basic_auth(self): - self.secret = self.config.client.create_secret(Secret('basic-auth-test', SecretConfigSourceBasicAuth('user', 'pass'))) - self.secret = self.config.client.update_secret(self.secret.guid, Secret('basic-auth-test', SecretConfigSourceBasicAuth('user', 'newpass'))) + def test_update_secret_source_docker(self): + self.secret = self.config.client.create_secret(Secret('docker-test', SecretConfigDocker('user','pass', 'email'))) + self.secret = self.config.client.update_secret(self.secret.guid, Secret('docker-test', SecretConfigDocker('user1','pass1', 'email1'))) self.assertSecret(self.secret) diff --git a/tests/secret_test.py b/tests/secret_test.py index 9f9fb031..505e0d1e 100644 --- a/tests/secret_test.py +++ b/tests/secret_test.py @@ -6,7 +6,7 @@ from mock import Mock, call, patch -from rapyuta_io.clients.secret import SecretConfigSourceBasicAuth, SecretConfigSourceSSHAuth, SecretType, \ +from rapyuta_io.clients.secret import SecretType, \ SecretConfigDocker, DOCKER_HUB_REGISTRY, Secret from rapyuta_io.utils import InvalidParameterException, InternalServerError, ResourceNotFoundError from tests.utils.client import get_client, headers, AUTH_TOKEN @@ -14,58 +14,6 @@ class SecretConfigTests(unittest.TestCase): - def test_bad_secret_config_ssh_auth(self): - expected_err_msg = 'ssh_key cannot be empty' - with self.assertRaises(InvalidParameterException) as e: - SecretConfigSourceSSHAuth(ssh_key='') - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_secret_config_ssh_auth(self): - secret_config = SecretConfigSourceSSHAuth(ssh_key='ssh-key') - expected_serialize = {'ssh-privatekey': base64.b64encode('ssh-key'.encode()).decode()} - self.assertEqual('ssh-key', secret_config.ssh_key) - self.assertEqual(SecretType.SOURCE_SSH_AUTH, secret_config.get_type()) - self.assertEqual(expected_serialize, secret_config.serialize()) - - def test_bad_secret_config_basic_auth_empty_username(self): - expected_err_msg = 'username cannot be empty' - with self.assertRaises(InvalidParameterException) as e: - SecretConfigSourceBasicAuth(username='', password='password', ca_cert='ca-cert') - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_bad_secret_config_basic_auth_empty_password(self): - expected_err_msg = 'password cannot be empty' - with self.assertRaises(InvalidParameterException) as e: - SecretConfigSourceBasicAuth(username='username', password='', ca_cert='ca-cert') - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_bad_secret_config_basic_auth_empty_ca_cert(self): - expected_err_msg = 'ca_cert cannot be empty' - with self.assertRaises(InvalidParameterException) as e: - SecretConfigSourceBasicAuth(username='username', password='password', ca_cert='') - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_secret_config_basic_auth_empty_ca_cert(self): - secret_config = SecretConfigSourceBasicAuth(username='username', password='password') - expected_serialize = {'username': base64.b64encode('username'.encode()).decode(), - 'password': base64.b64encode('password'.encode()).decode()} - self.assertEqual('username', secret_config.username) - self.assertEqual('password', secret_config.password) - self.assertIsNone(secret_config.ca_cert) - self.assertEqual(SecretType.SOURCE_BASIC_AUTH, secret_config.get_type()) - self.assertEqual(expected_serialize, secret_config.serialize()) - - def test_secret_config_basic_auth_with_ca_cert(self): - secret_config = SecretConfigSourceBasicAuth(username='username', password='password', ca_cert='ca-cert') - expected_serialize = {'username': base64.b64encode('username'.encode()).decode(), - 'password': base64.b64encode('password'.encode()).decode(), - 'ca.crt': base64.b64encode('ca-cert'.encode()).decode()} - self.assertEqual('username', secret_config.username) - self.assertEqual('password', secret_config.password) - self.assertEqual('ca-cert', secret_config.ca_cert) - self.assertEqual(SecretType.SOURCE_BASIC_AUTH, secret_config.get_type()) - self.assertEqual(expected_serialize, secret_config.serialize()) - def test_bad_secret_config_docker_empty_username(self): expected_err_msg = 'username cannot be empty' with self.assertRaises(InvalidParameterException) as e: @@ -118,42 +66,36 @@ class SecretTests(unittest.TestCase): def test_bad_secret_name_length(self): expected_err_msg = 'length of name must be between 3 and 253 characters' with self.assertRaises(InvalidParameterException) as e: - Secret(name='a' * 300, secret_config=SecretConfigSourceSSHAuth('ssh-key')) + Secret(name='a' * 300, secret_config=SecretConfigDocker(username='username', password='password', email='test@example.com', + registry='quay.io')) self.assertEqual(expected_err_msg, str(e.exception)) def test_bad_secret_name_pattern(self): expected_err_msg = 'name must consist of lower case alphanumeric characters or - and must start and end with ' \ 'an alphanumeric character' with self.assertRaises(InvalidParameterException) as e: - Secret(name='-SECRET-', secret_config=SecretConfigSourceSSHAuth('ssh-key')) + Secret(name='-SECRET-', secret_config=SecretConfigDocker(username='username', password='password', email='test@example.com', + registry='quay.io')) self.assertEqual(expected_err_msg, str(e.exception)) def test_bad_secret_name_type(self): expected_err_msg = 'name must be a string' with self.assertRaises(InvalidParameterException) as e: - Secret(name=123, secret_config=SecretConfigSourceSSHAuth('ssh-key')) - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_bad_secret_config(self): - expected_err_msg = 'secret_config must be of type SourceSecretBasicConfig, SourceSecretSSHConfig or ' \ - 'DockerSecretConfig' - with self.assertRaises(InvalidParameterException) as e: - Secret(name='bad', secret_config='invalid-secret') + Secret(name=123, secret_config=SecretConfigDocker(username='username', password='password', email='test@example.com', + registry='quay.io')) self.assertEqual(expected_err_msg, str(e.exception)) - def test_create_secret_invalid_secret_type(self): - expected_err_msg = 'secret must be non-empty and of type rapyuta_io.clients.secret.Secret' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_secret('invalid-secret-type') - - self.assertEqual(str(e.exception), expected_err_msg) - @patch('requests.request') def test_create_secret_internal_server_error(self, mock_request): - secret = Secret('test-secret', SecretConfigSourceSSHAuth('ssh-key')) + secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', + registry='quay.io')) + docker_config = '{"quay.io": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' client = get_client() - expected_payload = {'type': SecretType.SOURCE_SSH_AUTH, 'data': {'ssh-privatekey': base64.b64encode('ssh-key'.encode()).decode()}, 'name': 'test-secret'} + expected_payload = { + 'type': str(SecretType.DOCKER), + 'data': {'.dockercfg': base64.b64encode(docker_config.encode()).decode()}, + 'name': 'test-secret' + } expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/create' mock_secret = Mock() mock_secret.status_code = requests.codes.INTERNAL_SERVER_ERROR @@ -166,9 +108,15 @@ def test_create_secret_internal_server_error(self, mock_request): @patch('requests.request') def test_create_secret_success(self, mock_request): - secret = Secret('test-secret', SecretConfigSourceSSHAuth('ssh-key')) + secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', + registry='quay.io')) + docker_config = '{"quay.io": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' client = get_client() - expected_payload = {'type': SecretType.SOURCE_SSH_AUTH, 'data': {'ssh-privatekey': base64.b64encode('ssh-key'.encode()).decode()}, 'name': 'test-secret'} + expected_payload = { + 'type': str(SecretType.DOCKER), + 'data': {'.dockercfg': base64.b64encode(docker_config.encode()).decode()}, + 'name': 'test-secret' + } expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/create' mock_secret = Mock() mock_secret.text = SECRET_CREATE_SUCCESS @@ -296,7 +244,7 @@ def test_delete_secret_success(self, mock_request): @patch('requests.request') def test_delete_method_internal_server_error(self, mock_request): - secret = Secret('test-secret', SecretConfigSourceSSHAuth('ssh-key')) + secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com')) setattr(secret, '_core_api_host', 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io') setattr(secret, '_auth_token', 'Bearer ' + AUTH_TOKEN) setattr(secret, '_project', 'test_project') @@ -313,7 +261,7 @@ def test_delete_method_internal_server_error(self, mock_request): ]) def test_delete_method_invalid_parameter(self): - secret = Secret('test-secret', SecretConfigSourceSSHAuth('ssh-key')) + secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com')) expected_err_msg = 'Secret must be created first' setattr(secret, 'guid', 'secret-guid') with self.assertRaises(InvalidParameterException) as e: @@ -322,7 +270,8 @@ def test_delete_method_invalid_parameter(self): @patch('requests.request') def test_delete_method_success(self, mock_request): - secret = Secret('test-secret', SecretConfigSourceSSHAuth('ssh-key')) + secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', + registry='quay.io')) setattr(secret, '_core_api_host', 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io') setattr(secret, '_auth_token', 'Bearer ' + AUTH_TOKEN) setattr(secret, '_project', 'test_project') @@ -340,14 +289,13 @@ def test_delete_method_success(self, mock_request): @patch('requests.request') def test_update_method_success(self, mock_request): - secret = Secret('test-secret', SecretConfigSourceBasicAuth(username="testuser", password='testpassword')) + secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', + registry='quay.io')) + docker_config = '{"quay.io": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' client = get_client() expected_payload = { - 'type': SecretType.SOURCE_BASIC_AUTH, - 'data': { - "username": base64.b64encode('testuser'.encode()).decode(), - "password": base64.b64encode('testpassword'.encode()).decode() - }, + 'type': str(SecretType.DOCKER), + 'data': {'.dockercfg': base64.b64encode(docker_config.encode()).decode()}, 'name': 'test-secret' } expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/secret-guid/update' @@ -363,14 +311,13 @@ def test_update_method_success(self, mock_request): @patch('requests.request') def test_update_method_internal_server_error(self, mock_request): - secret = Secret('test-secret', SecretConfigSourceBasicAuth(username="testuser", password='testpassword')) + secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', + registry='quay.io')) + docker_config = '{"quay.io": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' client = get_client() expected_payload = { - 'type': SecretType.SOURCE_BASIC_AUTH, - 'data': { - "username": base64.b64encode('testuser'.encode()).decode(), - "password": base64.b64encode('testpassword'.encode()).decode() - }, + 'type': str(SecretType.DOCKER), + 'data': {'.dockercfg': base64.b64encode(docker_config.encode()).decode()}, 'name': 'test-secret' } expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/secret-guid/update' @@ -385,14 +332,13 @@ def test_update_method_internal_server_error(self, mock_request): @patch('requests.request') def test_update_method_not_found_error(self, mock_request): - secret = Secret('test-secret', SecretConfigSourceBasicAuth(username="testuser", password='testpassword')) + secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', + registry='quay.io')) + docker_config = '{"quay.io": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' client = get_client() expected_payload = { - 'type': SecretType.SOURCE_BASIC_AUTH, - 'data': { - "username": base64.b64encode('testuser'.encode()).decode(), - "password": base64.b64encode('testpassword'.encode()).decode() - }, + 'type': str(SecretType.DOCKER), + 'data': {'.dockercfg': base64.b64encode(docker_config.encode()).decode()}, 'name': 'test-secret' } expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/secret-guid/update'