From abdea61ef947d3d4acc91d326351c962c3852ded Mon Sep 17 00:00:00 2001 From: anjastrunk <119566837+anjastrunk@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:16:42 +0200 Subject: [PATCH] Fix CLI commands (#121) * Fix CLI commands Signed-off-by: Anja Strunk * Revert unneccessary changes Signed-off-by: Anja Strunk * Revert unneccessary changes Signed-off-by: Anja Strunk * Revert unneccessary changes Signed-off-by: Anja Strunk * Revert unneccessary changes Signed-off-by: Anja Strunk * Fix pipeline Signed-off-by: Anja Strunk * Update docu. Add config.yaml to gitignore Signed-off-by: Anja Strunk * Fix test cases Signed-off-by: Anja Strunk * Change file names to be in sync with VC ids Signed-off-by: Anja Strunk * Fix test cases Signed-off-by: Anja Strunk * Change default path of config file Signed-off-by: Anja Strunk * Delete obsolete vsmo.json Signed-off-by: Anja Strunk * Revert move cli.py out Signed-off-by: Anja Strunk * Fix test cases Signed-off-by: Anja Strunk * Update README.md Signed-off-by: Anja Strunk * Fix markdown lint Signed-off-by: Anja Strunk --------- Signed-off-by: Anja Strunk --- .gitignore | 1 + README.md | 3 +++ config/{config.yaml => config.yaml.template} | 2 +- generator/cli.py | 25 ++++++++----------- generator/common/const.py | 2 +- generator/discovery/csp_generator.py | 2 +- .../openstack/server_flavor_discovery.py | 2 +- tests/common.py | 2 +- tests/test_cli.py | 22 ++++++++-------- tests/test_csp_generator.py | 14 +++++------ 10 files changed, 38 insertions(+), 37 deletions(-) rename config/{config.yaml => config.yaml.template} (99%) diff --git a/.gitignore b/.gitignore index 1731690..5c1e6a2 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ coverage.xml os_secret k8s_secret .gx-credentials +config/config.yaml diff --git a/README.md b/README.md index fb38714..923a3d1 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ ![Static Badge](https://img.shields.io/badge/version-1.0.0-green) ![Static Badge](https://img.shields.io/badge/Gaia--X_Release-Tagus-blue?color=%23D901D9) ![Static Badge](https://img.shields.io/badge/Gaia--X_Compliance-Conformity-blue?color=%23D901D9) +![Python 3.10](https://img.shields.io/badge/python-3.10-blue.svg) Tool for creating compliant Gaia-X Credentials, previously known as Gaia-X Self-Description, for SCS-compliant cloud infrastructures. To get familiar with Gaia-X Credentials, please consult the corresponding [documentation](https://docs.gaia-x.eu/). @@ -80,6 +81,8 @@ Installing dependecies into a Python [virtualenv](https://virtualenv.pypa.io/en/ pip install -r requirements.txt ``` +Please use python version 3.10! + ### Cloud Service Provider #### 1. Create configuration file diff --git a/config/config.yaml b/config/config.yaml.template similarity index 99% rename from config/config.yaml rename to config/config.yaml.template index 50d2a4a..492e863 100644 --- a/config/config.yaml +++ b/config/config.yaml.template @@ -47,7 +47,7 @@ IaaS: format-type: "plain" # Endpoints of Gaia-X Digital Clearing House -gxdch: +GXDCH: notary-service: https://registrationnumber.notary.lab.gaia-x.eu/v1-staging compliance-service: https://compliance.lab.gaia-x.eu/v1-staging registry-service: https://registry.lab.gaia-x.eu/v1-staging diff --git a/generator/cli.py b/generator/cli.py index 84aacc9..94271b4 100755 --- a/generator/cli.py +++ b/generator/cli.py @@ -36,11 +36,11 @@ DATA_FILE_FORMAT = "json-ld" VC_NAME_LOOKUP = { - "lp": "Legal Person", + "legal_person": "Legal Person", "lrn": "Legal Registration Number", "tandc": "Gaia-X Terms and Conditions", - "cs_csp": "GXDCH Compliance Service", - "cs_so": "GXDCH Compliance Service", + "csp_compliance": "GXDCH Compliance Service", + "so_compliance": "GXDCH Compliance Service", "so": "Service Offering", "vmso": "Virtual Machine Service Offering", } @@ -94,7 +94,7 @@ def cli_commands(): pass -@click.command() +@cli_commands.command() @add_logging_options @click.option( "--auto-sign/--no-auto-sign", @@ -147,7 +147,7 @@ def openstack(cloud, timeout, config, out_dir, auto_sign): _print_vcs(vcs, out_dir) -@click.command() +@cli_commands.command() @add_logging_options def kubernetes(): """Generates Gaia-X Credentials for CSP and Kubernetes.""" @@ -160,7 +160,7 @@ def kubernetes(): # graph.parse(filepath, format=file_format) # return graph -@click.command() +@cli_commands.command() @add_logging_options @click.option( "--auto-sign/--no-auto-sign", @@ -232,6 +232,7 @@ def create_vmso_vcs(conf: Config, cloud: str, csp_vcs: List[dict], timeout: int @param timeout: timeout for connection to OpenStack cloud. If timeout expires, connection is initialed a second time. @return: A list of Gaia-X Credentials describing given OpenStack cloud. """ + csp = conf.get_value([const.CONFIG_CSP]) # iaas = conf.get_value([const.CONFIG_IAAS]) not yet used, as Gaia-X "abuses" id attribute of Verifiable Credentials cred_settings = conf.get_value([const.CONFIG_CRED]) @@ -247,7 +248,7 @@ def create_vmso_vcs(conf: Config, cloud: str, csp_vcs: List[dict], timeout: int vmso_vc = { '@context': [const.VC_CONTEXT, const.JWS_CONTEXT, const.REG_CONTEXT], 'type': "VerifiableCredential", - 'id': cred_settings[const.CONFIG_CRED_BASE_CRED_URL] + "/vmo.json", + 'id': cred_settings[const.CONFIG_CRED_BASE_CRED_URL] + "/vmso.json", 'issuer': csp['did'], 'issuanceDate': str(datetime.now(tz=timezone.utc).isoformat()), 'credentialSubject': json.loads(json.dumps(vm_offering, default=json_ld.to_json_ld)), @@ -269,7 +270,7 @@ def create_vmso_vcs(conf: Config, cloud: str, csp_vcs: List[dict], timeout: int "type": "gx:ServiceOffering", "id": cred_settings[const.CONFIG_CRED_BASE_CRED_URL] + "/so.json#subject", # iaas['did'], "gx:providedBy": { - 'id': csp_vcs['lp']['credentialSubject']['id'] + 'id': csp_vcs['legal_person']['credentialSubject']['id'] }, "gx:termsAndConditions": [ {'gx:URL': s_tac.url, 'gx:hash': s_tac.hash} @@ -291,12 +292,12 @@ def create_vmso_vcs(conf: Config, cloud: str, csp_vcs: List[dict], timeout: int # Request Gaia-X Compliance Credential for Service Offering logging.info('Request VC of type "gx:compliance" for Service Offering at GXDCH Compliance Service...') - vp = credentials.convert_to_vp(creds=[csp_vcs['tandc'], csp_vcs['lrn'], csp_vcs['lp'], so_vc_signed]) + vp = credentials.convert_to_vp(creds=[csp_vcs['tandc'], csp_vcs['lrn'], csp_vcs['legal_person'], so_vc_signed]) comp_vc = compliance.request_compliance_vc(vp, cred_settings[const.CONFIG_CRED_BASE_CRED_URL] + "/so_compliance.json") logging.info('ok') - return {'so': so_vc, 'cs_so': json.loads(comp_vc), 'vmso': vmso_vc_signed, 'vp_so': vp} + return {'so': so_vc, 'so_compliance': json.loads(comp_vc), 'vmso': vmso_vc_signed, 'vp_so': vp} def _get_timestamp(): @@ -356,9 +357,5 @@ def _are_gaiax_tandc_signed(conf: Config) -> bool: return False -cli_commands.add_command(openstack) -cli_commands.add_command(kubernetes) -cli_commands.add_command(csp) - if __name__ == "__main__": cli_commands() diff --git a/generator/common/const.py b/generator/common/const.py index 58f3af4..1a750f0 100644 --- a/generator/common/const.py +++ b/generator/common/const.py @@ -35,7 +35,7 @@ CONFIG_CSP_EUID = "euid" # GXDCH -CONST_GXDCH = "gxdch" +CONST_GXDCH = "GXDCH" CONST_GXDCH_NOT = "notary-service" CONST_GXDCH_COMP = "compliance-service" CONST_GXDCH_REG = "registry-service" diff --git a/generator/discovery/csp_generator.py b/generator/discovery/csp_generator.py index 158b3c9..b314474 100644 --- a/generator/discovery/csp_generator.py +++ b/generator/discovery/csp_generator.py @@ -59,7 +59,7 @@ def generate(self) -> dict: vp = credentials.convert_to_vp(creds=[tandc_vc, lrn_vc, lp_vc]) cs_vc = self.compliance.request_compliance_vc(vp, self.cred_base_url + "/csp_compliance.json") logging.info('ok') - return {'tandc': tandc_vc, 'lrn': lrn_vc, 'lp': lp_vc, 'cs_csp': json.loads(cs_vc), 'vp_csp': vp} + return {'tandc': tandc_vc, 'lrn': lrn_vc, 'legal_person': lp_vc, 'csp_compliance': json.loads(cs_vc), 'vp_csp': vp} def _sign_gaia_x_terms_and_conditions(self) -> dict: """ diff --git a/generator/discovery/openstack/server_flavor_discovery.py b/generator/discovery/openstack/server_flavor_discovery.py index 4b58946..64639de 100644 --- a/generator/discovery/openstack/server_flavor_discovery.py +++ b/generator/discovery/openstack/server_flavor_discovery.py @@ -93,7 +93,7 @@ def _convert_to_gx(self, os_flavor: OS_Flavor) -> GX_Flavor: # Initialize Gaia-X Server Flavor disks = self._get_disks(os_flavor, flavorname) gx_flavor = GX_Flavor( - # name=os_flavor.name, + name=os_flavor.name, cpu=self._get_cpu(os_flavor, flavorname), ram=self._get_ram(os_flavor, flavorname), bootVolume=disks[0], diff --git a/tests/common.py b/tests/common.py index 747be0f..f9e07ac 100644 --- a/tests/common.py +++ b/tests/common.py @@ -26,7 +26,7 @@ def get_absolute_path(relative_path: str) -> str: def get_config() -> Config: - with open(get_absolute_path("config/config.yaml"), "r") as config_file: + with open(get_absolute_path("config/config.yaml.template"), "r") as config_file: return Config(yaml.safe_load(config_file)) diff --git a/tests/test_cli.py b/tests/test_cli.py index 716db64..ba726cf 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -9,7 +9,7 @@ from jwcrypto.jwt import JWK from generator import cli -from generator.common import config, const +from generator.common import config from generator.common.gx_schema import (DataAccountExport, TermsAndConditions, VirtualMachineServiceOffering) from tests.common import MockConnection, get_absolute_path @@ -45,10 +45,10 @@ def test_generate_vsmo(self, cli_print_vs, os_connect, os_discover, load_jwk, gx encryption_algorithm=serialization.NoEncryption())) csp_vcs = {'tandc': {"credentialSubject": {"type": "gx:GaiaXTermsAndConditions"}}, "lrn": {"credentialSubject": {"type": "gx:LegalRegistrationNumber", "id": "foo"}}, - "lp": {"credentialSubject": {"type": "gx:LegalParticipant", "id": "foo"}}} + "legal_person": {"credentialSubject": {"type": "gx:LegalParticipant", "id": "foo"}}} gxdch_req_compl.return_value = "{\"credentialSubject\": {\"type\": \"gx:ComplianceCredential\", \"id\": \"foo\"}}" - with open(get_absolute_path(const.CONFIG_FILE), "r") as config_file: + with open(get_absolute_path("config/config.yaml.template"), "r") as config_file: conf = config.Config(yaml.safe_load(config_file)) vcs = cli.create_vmso_vcs(conf, "myCloud", csp_vcs) @@ -58,7 +58,7 @@ def test_generate_vsmo(self, cli_print_vs, os_connect, os_discover, load_jwk, gx self.assertEqual(4, len(vcs)) self.assertEqual("gx:ServiceOffering", vcs['so']['credentialSubject']["type"]) - self.assertEqual("gx:ComplianceCredential", vcs['cs_so']['credentialSubject']["type"]) + self.assertEqual("gx:ComplianceCredential", vcs['so_compliance']['credentialSubject']["type"]) self.assertEqual("gx:VirtualMachineServiceOffering", vcs['vmso']['credentialSubject']["type"]) self.assertIsNotNone(vcs['vp_so']) @@ -72,7 +72,7 @@ def test_openstack(self, cli_print_vs, gen_vmso, gen_csp): runner = CliRunner() result = runner.invoke( - cli.openstack, "myCloud --config=" + get_absolute_path(const.CONFIG_FILE) + " --auto-sign" + cli.openstack, "myCloud --config=" + get_absolute_path("config/config.yaml.template") + " --auto-sign" ) gen_csp.assert_called_once() @@ -91,7 +91,7 @@ def test_openstack_auto_sign(self, cli_print_vs, gen_vmso, gen_csp): runner = CliRunner() result = runner.invoke( - cli.openstack, "myCloud --config=" + get_absolute_path(const.CONFIG_FILE) + " --auto-sign" + cli.openstack, "myCloud --config=" + get_absolute_path("config/config.yaml.template") + " --auto-sign" ) gen_csp.assert_called_once() @@ -111,7 +111,7 @@ def test_openstack_no_auto_sign(self, cli_print_vs, gen_vmso, gen_csp): with patch('builtins.input', return_value="n"): runner = CliRunner() result = runner.invoke( - cli.openstack, "myCloud --config=" + get_absolute_path(const.CONFIG_FILE) + " --no-auto-sign" + cli.openstack, "myCloud --config=" + get_absolute_path("config/config.yaml.template") + " --no-auto-sign" ) self.assertEqual(0, gen_csp.call_count) @@ -131,7 +131,7 @@ def test_openstack_sign(self, cli_print_vs, gen_vmso, gen_csp): with patch('builtins.input', return_value="y"): runner = CliRunner() result = runner.invoke( - cli.openstack, "myCloud --config=" + get_absolute_path(const.CONFIG_FILE) + cli.openstack, "myCloud --config=" + get_absolute_path("config/config.yaml.template") ) gen_csp.assert_called_once() @@ -159,7 +159,7 @@ def test_csp_auto_sign(self, cli_print_vs, gen_csp): runner = CliRunner() result = runner.invoke( - cli.csp, "--config=" + get_absolute_path(const.CONFIG_FILE) + " --auto-sign" + cli.csp, "--config=" + get_absolute_path("config/config.yaml.template") + " --auto-sign" ) gen_csp.assert_called_once() @@ -175,7 +175,7 @@ def test_csp_sign(self, cli_print_vs, gen_csp): with patch('builtins.input', return_value="y"): runner = CliRunner() result = runner.invoke( - cli.csp, "--config=" + get_absolute_path(const.CONFIG_FILE) + cli.csp, "--config=" + get_absolute_path("config/config.yaml.template") ) gen_csp.assert_called_once() self.assertIsNone(result.exception) @@ -190,7 +190,7 @@ def test_csp_no_auto_sign(self, cli_print_vs, gen_csp): with patch('builtins.input', return_value="n"): runner = CliRunner() result = runner.invoke( - cli.csp, "--config=" + get_absolute_path(const.CONFIG_FILE) + " --no-auto-sign" + cli.csp, "--config=" + get_absolute_path("config/config.yaml.template") + " --no-auto-sign" ) self.assertEqual(0, gen_csp.call_count) diff --git a/tests/test_csp_generator.py b/tests/test_csp_generator.py index 536aa31..4f51915 100644 --- a/tests/test_csp_generator.py +++ b/tests/test_csp_generator.py @@ -15,7 +15,7 @@ class CspPGeneratorTestCase(unittest.TestCase): @classmethod def setUpClass(cls): - with open(get_absolute_path(const.CONFIG_FILE), "r") as config_file: + with open(get_absolute_path("config/config.yaml.template"), "r") as config_file: cls.conf = config.Config(yaml.safe_load(config_file)) cls.private_key = JWK.from_pem(rsa.generate_private_key( @@ -70,14 +70,14 @@ def _verify_csp_gen(self, csp_vcs, get_tandc, request_req_number, request_comp_v self.assertEqual("gx:legalRegistrationNumber", csp_vcs['lrn']['credentialSubject']['type']) self.assertEqual( self.conf.get_value([const.CONFIG_CRED, const.CONFIG_CRED_BASE_CRED_URL]) + "/legal_person.json", - csp_vcs['lp']['id']) + csp_vcs['legal_person']['id']) self.assertEqual( self.conf.get_value([const.CONFIG_CRED, const.CONFIG_CRED_BASE_CRED_URL]) + "/legal_person.json#subject", - csp_vcs['lp']['credentialSubject']['id']) - self.assertEqual(self.conf.get_value([const.CONFIG_CSP, const.CONFIG_DID]), csp_vcs['lp']['issuer']) - self.assertIsNotNone(csp_vcs['lp']['proof']) - self.assertEqual("gx:LegalParticipant", csp_vcs['lp']['credentialSubject']['type']) - self.assertEqual("gx:ComplianceCredential", csp_vcs['cs_csp']['credentialSubject']['type']) + csp_vcs['legal_person']['credentialSubject']['id']) + self.assertEqual(self.conf.get_value([const.CONFIG_CSP, const.CONFIG_DID]), csp_vcs['legal_person']['issuer']) + self.assertIsNotNone(csp_vcs['legal_person']['proof']) + self.assertEqual("gx:LegalParticipant", csp_vcs['legal_person']['credentialSubject']['type']) + self.assertEqual("gx:ComplianceCredential", csp_vcs['csp_compliance']['credentialSubject']['type']) if __name__ == '__main__':