diff --git a/.travis.yml b/.travis.yml index 6ca05e6..c51fa47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,13 +6,14 @@ install: - pip install jmespath script: - ansible-playbook tests/write-inventory-files.yml -- ansible-playbook -i tests/inventory tests/3scale-saas-with-hosted-apicast-apikey.yml -- ansible-playbook -i tests/inventory tests/3scale-saas-with-hosted-apicast-oidc.yml -- ansible-playbook -i tests/inventory tests/3scale-saas-with-hosted-apicast-with-basePath.yml +- ansible-playbook -v -i tests/inventory tests/3scale-saas-with-hosted-apicast-apikey.yml +- ansible-playbook -v -i tests/inventory tests/3scale-saas-with-hosted-apicast-oidc.yml +- ansible-playbook -v -i tests/inventory tests/3scale-saas-with-hosted-apicast-with-basePath.yml +- ansible-playbook -v -i tests/inventory tests/3scale-saas-with-hosted-apicast-multi-environment.yml env: global: # travis encrypt "THREESCALE_INVENTORY=$(yaml2json tests/3scale-inventory.yaml|base64)" - secure: "YKSBZJonKq/RwBtrg1wrlbw9GUaQhY0LmbUpShIPMeD1DqLQtOz9OIijAP2Uvtnn5F8josyzyYeZoRz4HOWT6DNdbZoclIT86FdT9yp9pIBuKYqaGOMmdwhTl+BXudeTCAvuj4k6eNux24WY+AqWZoXgr4E0rRJQJyD/G7gn4CMDQmn+an+RK43nhkXgMYNm6SHbR3c3wBypdWJivfgvdBfJb2VY8Q3XXQNh+ivUNW5us4Sf+G+UsjbQKeTF6G1rz8FUxGc2tElWcc+6fXWDTWI1WkZVfNm7f6cGii7X22OBDgOkoUjUKeVj/vmpgp7uOSk3XiWIC+gNsT/cS9/6XzZCnfhGmPal3QP5hXnsP5gBfKYzy8zZEp9H8NLyGA8K7M0cZGuFDdxg0HnIyXNnn7Denjyt3TopFR9ENsNOEbBar8XEb4oZicRWrXC9O/Sse4rCm5vGffXt+lcoAuypmxhASDZKNKsQRXhG+JRMZ2ONB8QOdH22mQ+JEOhOFNtiY2O9eEQOdtU92B9vWYWOztSGZ8/+AQ729bCzOw7AfxtxUyYadFnEdibAeWsl+xnlAdwmo0sNoCTGBKFMXlyP/gmcE7NdCLqWS6MSM2u4ARuQt7IS29wwlRseaMUli69vp0SqvjT6lelx4bonLFi74treKIQVLoxqSQaiU7KVTr0=" + secure: "SKrCC5Nd1lXFU9mCrmGUSbqmEFGzT6/3KTXGQ/bASgSx4r0AuDHt48cI/XPQ6XGCIGaxAt2oRWzJZJ00+Y+5A1TCYAXI4X75mTVl+mgZ3ul5hSK1/KfPPoLciZIcv678FLmmpryNRapK+zxG+OKR1puNFQm9himhF9x0JICigFZSVLMLGnpvHDo2GguFv+4aO1tkdZMT5IzBlPD11Kn98QVCruF/dHiBXtSxuo5ja0/uDsGotMcUQRNa637WIQ5D7YgDREpeLrHzmbpW2zr7HI30oA68k+BxZFqlQ/cyI7f5ogNE73ID+FBSSxVXqcn7TD2nmYL3NQpMztTCzM6YlODIuAvdWUxggeBJflpIVoza0HLP7CB76GAmRSkvwGbnGAHWvCOtvczmJ/hXGgAEdRL5q3eJiGebRvhb6SAVMZ3LOH9LlLU9fKDVGqzolFi4+Jaxami600zgBB/yGkFckpapUZLEK2O0QdHBu3bjd7+9C0EgYONrbyMMkMoWr8TiX/y0qTHg3SclOEacDqLw3kb0MAe9V9WtE+MKOAM38lkXN1v1J9x2izeEqKBDDuzxMOsRxQwfSlA5MVW1kOiKaQgKl37F5t+msfIsPDlr2DRM4JTsIBaQKMY9E50tQ0cMW+vU7P+kn8UlNdTh53TLwKMjPcU99XG1f95fGAMocAA=" notifications: webhooks: https://galaxy.ansible.com/api/v1/notifications/ branches: diff --git a/README.md b/README.md index b30eb28..86a6cfd 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,8 @@ securityDefinitions: In this Swagger file, the following fields are used: -- `x-threescale-system-name` is used as system_name for the configuration objects in 3scale. +- `x-threescale-system-name` is used as a basis for the system_name for the + configuration objects in 3scale. - `title` is used as the name of the service definition. - `version` is used for proper versioning and follows the [semver scheme](https://semver.org/). - `host` is the DNS name of the existing API backend to expose. @@ -128,12 +129,6 @@ ansible-playbook -i inventory deploy-api.yaml ## Inventory -Three kinds of systems can be declared in the inventory and used with this role: - -- 3scale Admin Portal -- Red Hat SSO -- APIcast instances - The 3scale Admin Portal that will be provisionned is the one that is referenced in the playbook that includes this role. For instance, in the previous example, the provisioned 3scale Admin Portal will be `-admin.3scale.net` because @@ -175,60 +170,31 @@ And you can also define it globally, for instance as playbook vars: threescale_cicd_access_token: 123...456 ``` -The Red Hat SSO instance (currently there can only be one), is taken by convention -from the `sso` group. The `client_id`/`client_secret` used by Zync to synchronize -the 3scale applications are fetched from the inventory variables, as well as the -scheme (`http`/`https`) and the target realm. - -Example: - -```ini -[sso] -sso.acme.corp client_id=3scale client_secret=123 realm=acme scheme=https -``` - -Otherwise, if you don't want to follow this convention, you can use the -corresponding extra variable: `threescale_cicd_sso_issuer_endpoint`. For -the previous example, the variable would be: - -```ini -threescale_cicd_sso_issuer_endpoint=https://3scale:123@sso.acme.corp/auth/realms/acme -``` +The Red Hat SSO instance (currently there can only be one), is defined by +the `threescale_cicd_sso_issuer_endpoint` variable of the `threescale` group. -If both the `sso` group and the `threescale_cicd_sso_issuer_endpoint` extra -variable are specified, the extra variable has precedence over the inventory. - -The APIcast instances are fetched from the `apicast-sandbox` and `apicast-production` -groups. There can only be one hostname in each group and it is the public hostname -of each APIcast cluster (not each individual member). +Its syntax is `https://:@hostname/auth/realms/`. +The `client_id`/`client_secret` are used by Zync to synchronize the 3scale +applications with Red Hat SSO. Example: ```ini -[apicast-sandbox] -api-test.acme.corp scheme=http - -[apicast-production] -api.acme.corp scheme=https +threescale_cicd_sso_issuer_endpoint=https://3scale:123@sso.acme.corp/auth/realms/acme ``` -If you do not want to follow this convention, you can use the corresponding extra -variables: +The APIcast instances are defined from the following extra variables: - `threescale_cicd_apicast_sandbox_endpoint` - `threescale_cicd_apicast_production_endpoint` -For the previous example, the variables would be: +Example: ```ini threescale_cicd_apicast_sandbox_endpoint=http://api-test.acme.corp threescale_cicd_apicast_production_endpoint=https://api.acme.corp ``` -If both the `apicast-*` groups and the `threescale_cicd_apicast_*_endpoint` -extra variables are specified, the extra variables have precedence over the -inventory. - ## OpenAPI Specification fields This role currently supports only OpenAPI Specifications v2.0 (aka. Swagger 2.0). @@ -245,7 +211,7 @@ The following extended fields of the OpenAPI Specifications can be used: If the extended fields cannot be used (if for instance you do not want to alter your API Contract), you can use the corresponding extra variable: -- `threescale_cicd_api_system_name` +- `threescale_cicd_api_base_system_name` - `threescale_cicd_openapi_smoketest_operation` Here is an example of an OpenAPI Specification using those extended fields: @@ -284,7 +250,7 @@ To achieve the same effect without the OpenAPI extended fields, you would have to pass the following extra variables: ```ini -threescale_cicd_api_system_name=echo-api +threescale_cicd_api_base_system_name=echo-api threescale_cicd_openapi_smoketest_operation=Echo # The operationId of the "GET /" method ``` @@ -389,14 +355,28 @@ Defines the system_name of the 3scale Service that will be provisioned. - **Syntax:** lower case alphanumeric + underscore - **Required:** no -- **Default value:** if not defined, the system_name is taken from the OpenAPI - Specification `x-threescale-system-name` extended field, suffixed by the - API major version number. If no `x-threescale-system-name` extended field - can be found, the `title` field is sanitized and then used. +- **Default value:** if not defined, the system_name is taken from the + `threescale_cicd_api_base_system_name` variable. This base system_name + is then suffixed by the API major version number and prefixed by the + environment name (only if `threescale_cicd_api_environment_name` is defined). +- **Example:** `dev_my_wonderful_service_1` + +### `threescale_cicd_api_base_system_name` + +Is used as a basis to compute the `threescale_cicd_api_system_name`. + +- **Syntax:** lower case alphanumeric + underscore +- **Required:** no +- **Default value:** if not defined, the OpenAPI Specification + `x-threescale-system-name` extended field or as a last resort, the `title` + field is sanitized and then used. If no title can be found, the default value `API` is used. If no version number can be found, `0` is used. - **Example:** `my_wonderful_service` +Note: If both `threescale_cicd_api_base_system_name` and `threescale_cicd_api_system_name` +are set, the later has precedence. + ### `threescale_cicd_wildcard_domain` Automatically defines the APIcast public URLs based on a scheme. @@ -414,7 +394,7 @@ Automatically defines the APIcast public URLs based on a scheme. ```ini threescale_cicd_wildcard_domain=acme.corp - threescale_cicd_api_system_name=my_wonderful_service + threescale_cicd_api_base_system_name=my_wonderful_service ``` are equivalent to: @@ -479,7 +459,7 @@ when deploying the same API multiple times on the same 3scale instance. ### Miscellaneous variables -Miscellaneous variables defined in [defaults/main.yml](defaults/main.yml]) +Miscellaneous variables defined in [defaults/main.yml](defaults/main.yml) provide sensible defaults. Have a look at them. ## Dependencies diff --git a/defaults/main.yml b/defaults/main.yml index 6451cdf..b1fe5ed 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -17,10 +17,12 @@ threescale_cicd_application_plans: state: hidden name: Ansible Test Plan -# Specifies that APIcast is self managed (if a public staging and/or -# production URL is given), or hosted -threescale_cicd_api_deployment_type: '{{ ''self_managed'' if threescale_cicd_apicast_sandbox_endpoint is defined or threescale_cicd_apicast_production_endpoint is defined else ''hosted'' }}' +# APIcast public base URLs +threescale_cicd_apicast_sandbox_endpoint: '{{ lookup(''template'', ''openapi/apicast_sandbox_endpoint.j2'') }}' +threescale_cicd_apicast_production_endpoint: '{{ lookup(''template'', ''openapi/apicast_production_endpoint.j2'') }}' +# SSO Issuer Endpoint +threescale_cicd_sso_issuer_endpoint: '{{ lookup(''template'', ''openapi/sso_issuer_endpoint.j2'') }}' ## ## Default Application (used for Smoke Tests) ## @@ -34,4 +36,14 @@ threescale_cicd_default_application_plan: '{{ (threescale_cicd_application_plans # Compute the default application's appid. By default, we are using a combination # of app, api and environment data, hashed toghether to produce a stable id. threescale_cicd_default_application_appid: '{{ (threescale_cicd_default_application_name ~ threescale_cicd_api_system_name ~ threescale_cicd_access_token)|hash(''sha1'') }}' -threescale_cicd_default_application_appsecret: '{{ (''secret'' ~ threescale_cicd_default_application_name ~ threescale_cicd_api_system_name ~ threescale_cicd_access_token)|hash(''sha1'') }}' \ No newline at end of file +threescale_cicd_default_application_appsecret: '{{ (''secret'' ~ threescale_cicd_default_application_name ~ threescale_cicd_api_system_name ~ threescale_cicd_access_token)|hash(''sha1'') }}' + +# The OpenAPI Operation to use for the smoketest +threescale_cicd_openapi_smoketest_operation: '{{ threescale_cicd_openapi_file_content|json_query(''paths.*.get[? "x-threescale-smoketests-operation" ].operationId|[0]'')|default("")|regex_replace(''[^0-9a-zA-Z_]+'', ''_'') }}' + +## +## OpenAPI Specification File parsing +## +threescale_cicd_api_base_system_name: '{{ lookup(''template'', ''openapi/generate_base_system_name.j2'') }}' +threescale_cicd_api_system_name: '{{ lookup(''template'', ''openapi/generate_final_system_name.j2'') }}' +threescale_cicd_private_base_url: '{{ lookup(''template'', ''openapi/private_base_url.j2'') }}' diff --git a/tasks/api-calls/update_proxy.yml b/tasks/api-calls/update_proxy.yml index 81d3687..a5a09bf 100644 --- a/tasks/api-calls/update_proxy.yml +++ b/tasks/api-calls/update_proxy.yml @@ -13,15 +13,10 @@ register: threescale_cicd_tmpresponse changed_when: 'threescale_cicd_tmpresponse.status == 200' -- name: Extract the staging gateway endpoint from the proxy definition +- name: Extract the staging and production gateway endpoint from the proxy definition set_fact: - threescale_cicd_apicast_sandbox_endpoint: '{{ threescale_cicd_tmpresponse.json.proxy.sandbox_endpoint }}' - when: "threescale_cicd_apicast_sandbox_endpoint is not defined" - -- name: Extract the production gateway endpoint from the proxy definition - set_fact: - threescale_cicd_apicast_production_endpoint: '{{ threescale_cicd_tmpresponse.json.proxy.endpoint }}' - when: "threescale_cicd_apicast_production_endpoint is not defined" + threescale_cicd_apicast_discovered_sandbox_endpoint: '{{ threescale_cicd_tmpresponse.json.proxy.sandbox_endpoint }}' + threescale_cicd_apicast_discovered_production_endpoint: '{{ threescale_cicd_tmpresponse.json.proxy.endpoint }}' - name: Wait for a couple seconds pause: diff --git a/tests/cleanup.yaml b/tasks/cleanup.yaml similarity index 99% rename from tests/cleanup.yaml rename to tasks/cleanup.yaml index 377c3ed..390e653 100644 --- a/tests/cleanup.yaml +++ b/tasks/cleanup.yaml @@ -9,6 +9,7 @@ register: threescale_cicd_tmpresponse changed_when: 'threescale_cicd_tmpresponse.status == 200' when: 'threescale_cicd_api_service_id is defined' + - name: Delete the created ActiveDocs uri: url: 'https://{{ inventory_hostname }}/admin/api/active_docs/{{ threescale_cicd_api_activedocs_id }}.json?access_token={{ threescale_cicd_access_token|urlencode }}' diff --git a/tasks/main.yml b/tasks/main.yml index 14d31e8..785f33b 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -3,7 +3,7 @@ # Make sure we have everything we need to run this playbook - import_tasks: steps/requirements.yml -# Compute missing variables from the inventory +# Warn the user about those deprecated features - import_tasks: steps/variables_from_inventory.yml # Load the API definition from the provided OpenAPI file diff --git a/tasks/steps/default_application.yml b/tasks/steps/default_application.yml index 4de5a08..860032b 100644 --- a/tasks/steps/default_application.yml +++ b/tasks/steps/default_application.yml @@ -4,7 +4,6 @@ when: 'threescale_cicd_default_account_id is not defined' - import_tasks: "api-calls/find_application.yml" - when: 'threescale_cicd_default_application_id is not defined' - import_tasks: "api-calls/update_application.yml" when: 'threescale_cicd_default_application_id is defined' diff --git a/tasks/steps/read_openapi.yml b/tasks/steps/read_openapi.yml index ace597c..a7fbfd1 100644 --- a/tasks/steps/read_openapi.yml +++ b/tasks/steps/read_openapi.yml @@ -1,17 +1,4 @@ --- -- name: Parse the OpenAPI file (YAML format) - set_fact: - threescale_cicd_openapi_file_content: '{{ lookup(''file'', threescale_cicd_openapi_file) |from_yaml }}' - when: "threescale_cicd_openapi_file_format|upper == 'YAML'" - -- name: Parse the OpenAPI file (JSON format) - set_fact: - threescale_cicd_openapi_file_content: '{{ lookup(''file'', threescale_cicd_openapi_file) |from_json }}' - when: "threescale_cicd_openapi_file_format|upper == 'JSON'" - -- name: Extract the OpenAPI format version - set_fact: - threescale_cicd_openapi_file_version: '{{ threescale_cicd_openapi_file_content|json_query(''swagger'') }}' - name: Check the OpenAPI format version assert: @@ -19,155 +6,27 @@ - "threescale_cicd_openapi_file_version == '2.0'" msg: "Currently only the OpenAPI/Swagger 2.0 is handled. If needed, fill an issue or submit a pull request!" -# TODO rewrite this in a more "Ansible compatible" way -- name: Extract API Methods - set_fact: - threescale_cicd_api_name: '{{ threescale_cicd_openapi_file_content.info.title|default("API") }}' - threescale_cicd_api_description: '{{ threescale_cicd_openapi_file_content.info.description|default("") }}' - threescale_cicd_api_version: '{{ threescale_cicd_openapi_file_content.info.version|default("0.0.1") }}' - threescale_cicd_api_basepath: '{{ threescale_cicd_openapi_file_content.basePath|default("") }}' - threescale_cicd_api_operations: >- - {% set operations = {} -%} - {% if 'paths' in threescale_cicd_openapi_file_content -%} - {% for path, verbs in threescale_cicd_openapi_file_content['paths'].items() -%} - {% if path.startswith('/') -%} - {% for verb, method_description in verbs.items() -%} - {% if verb != '$ref' and verb != 'parameters' -%} - {% if 'operationId' in method_description -%} - {% set operation_id = method_description['operationId'] -%} - {% else -%} - {% set operation_id = verb.upper() + path -%} - {% endif -%} - {% set operation_id = operation_id|regex_replace('[^0-9a-zA-Z_]+', '_') -%} - {% set operation = { operation_id: { 'path': path, 'verb': verb } } -%} - {% if 'summary' in method_description -%} - {% if operation[operation_id].update({ 'friendly_name': method_description.summary }) -%}{% endif -%} - {% endif -%} - {% if 'description' in method_description -%} - {% if operation[operation_id].update({ 'description': method_description.description }) -%}{% endif -%} - {% endif -%} - {% if operations.update(operation) -%}{% endif -%} - {% endif -%} - {% endfor -%} - {% endif -%} - {% endfor -%} - {% endif -%} - {{ operations }} - -- name: Extract components from the version number - set_fact: - threescale_cicd_api_version_components: '{{ threescale_cicd_api_version.split(".") }}' - -- name: Find the major version - set_fact: - threescale_cicd_api_version_major: '{{ threescale_cicd_api_version_components|first }}' - -- name: Compute the system_name suffix to append to the generated system_name - set_fact: - threescale_cicd_api_system_name_suffix: '{{ (threescale_cicd_api_system_name is not defined)|ternary("_" ~ (threescale_cicd_api_version_major|regex_replace(''[^a-zA-Z0-9_]+'', ''_'')), "") }}' - -- name: Compute the system_name prefix to prepend to the generated system_name - set_fact: - threescale_cicd_api_system_name_prefix: '{{ (threescale_cicd_api_system_name is not defined and threescale_cicd_api_environment_name is defined)|ternary((threescale_cicd_api_environment_name|default("")|regex_replace(''[^a-zA-Z0-9_]+'', ''_'')) ~ "_", "") }}' - -- name: Extract the wanted system_name from OpenAPI - set_fact: - threescale_cicd_api_system_name: '{{ threescale_cicd_openapi_file_content.info[''x-threescale-system-name'']|regex_replace(''[^a-zA-Z0-9_]+'', ''_'')|lower }}' - when: 'threescale_cicd_api_system_name is not defined and ''x-threescale-system-name'' in threescale_cicd_openapi_file_content.info' - -- name: Generate a system_name from the API title - set_fact: - threescale_cicd_api_system_name: '{{ threescale_cicd_openapi_file_content.info[''title'']|default(''api'')|regex_replace(''[^a-zA-Z0-9_]+'', ''_'')|lower }}' - when: 'threescale_cicd_api_system_name is not defined' - -- name: Append the major version to the system_name - set_fact: - threescale_cicd_api_system_name: '{{ threescale_cicd_api_system_name }}{{ threescale_cicd_api_system_name_suffix }}' - -- name: Set the threescale_cicd_apicast_{sandbox,production}_endpoint variable from the wildcard domain - set_fact: - threescale_cicd_apicast_sandbox_endpoint: '{{ threescale_cicd_default_apicast_scheme }}://{{ threescale_cicd_api_system_name|regex_replace(''[^a-zA-Z0-9-]+'', ''-'')|lower }}{{ threescale_cicd_default_staging_suffix }}.{{ threescale_cicd_wildcard_domain }}' - threescale_cicd_apicast_production_endpoint: '{{ threescale_cicd_default_apicast_scheme }}://{{ threescale_cicd_api_system_name|regex_replace(''[^a-zA-Z0-9-]+'', ''-'')|lower }}{{ threescale_cicd_default_production_suffix }}.{{ threescale_cicd_wildcard_domain }}' - when: 'threescale_cicd_wildcard_domain is defined' - -- name: Prefix the system_name with the environment - set_fact: - threescale_cicd_api_system_name: '{{ threescale_cicd_api_system_name_prefix }}{{ threescale_cicd_api_system_name }}' - -- name: Append the full version to the API title - set_fact: - threescale_cicd_api_name: '{{ threescale_cicd_api_name }} (v{{ threescale_cicd_api_version }})' - when: 'threescale_cicd_api_environment_name is not defined' - -- name: Append the full version and the environment to the API title - set_fact: - threescale_cicd_api_name: '{{ threescale_cicd_api_name }} ({{ threescale_cicd_api_environment_name|upper }}, v{{ threescale_cicd_api_version }})' - when: 'threescale_cicd_api_environment_name is defined' - -- name: Extract the security definitions and requirements from OpenAPI - set_fact: - threescale_cicd_api_security_requirements: '{{ threescale_cicd_openapi_file_content.security|default([]) }}' - threescale_cicd_api_security_definitions: '{{ threescale_cicd_openapi_file_content.securityDefinitions|default({}) }}' - - name: Make sure there is one and exactly one security requirement assert: that: - 'threescale_cicd_api_security_requirements|length == 1' msg: 'You have {{ threescale_cicd_api_security_requirements|length }} global security requirements. There must be one and only one security requirement.' -- name: Find the security requirement to use - set_fact: - threescale_cicd_api_security_scheme_name: '{{ threescale_cicd_api_security_requirements[0].keys()[0] }}' - -- name: Make sure the requested security definition exists - assert: - that: - - 'threescale_cicd_api_security_scheme_name in threescale_cicd_api_security_definitions' - -- name: Find the security definition to use - set_fact: - threescale_cicd_api_security_scheme: '{{ threescale_cicd_api_security_definitions[threescale_cicd_api_security_scheme_name] }}' - - name: Make sure the security scheme is consistent with 3scale assert: that: - - 'threescale_cicd_api_security_scheme.type == ''apiKey'' or (threescale_cicd_api_security_scheme.type == ''oauth2'' and threescale_cicd_sso_issuer_endpoint is defined)' - -- name: Find the correct backend_version to use - set_fact: - threescale_cicd_api_backend_version: '1' - when: 'threescale_cicd_api_security_scheme.type == ''apiKey''' - -- name: Find the correct backend_version to use - set_fact: - threescale_cicd_api_backend_version: 'oidc' - when: 'threescale_cicd_api_security_scheme.type == ''oauth2''' - -- name: Extract the backend hostname from OpenAPI - set_fact: - threescale_cicd_api_backend_hostname: '{{ threescale_cicd_openapi_file_content.host }}' - when: 'threescale_cicd_api_backend_hostname is not defined and ''host'' in threescale_cicd_openapi_file_content' - -- name: Extract the backend scheme from OpenAPI - set_fact: - threescale_cicd_api_backend_scheme: '{{ threescale_cicd_openapi_file_content.schemes|default(["http"])|first }}' - when: 'threescale_cicd_api_backend_scheme is not defined' - -- name: Compute the private base url from the OpenAPI file - set_fact: - threescale_cicd_private_base_url: '{{ threescale_cicd_api_backend_scheme ~ ''://'' ~ threescale_cicd_api_backend_hostname }}' - when: threescale_cicd_api_backend_hostname is defined and threescale_cicd_private_base_url is not defined + - '''type'' in threescale_cicd_api_security_scheme and threescale_cicd_api_security_scheme.type == ''apiKey'' or (threescale_cicd_api_security_scheme.type == ''oauth2'' and threescale_cicd_sso_issuer_endpoint is defined)' + msg: |- + The embedded security definition {{ threescale_cicd_api_security_scheme_name }} is not compatible with 3scale. + Please make sure you chose an "apiKey" or "oauth2" scheme. + Also, if you chose "oauth2", you will need to pass the threescale_cicd_sso_issuer_endpoint extra variable. + The security definition you chose: {{ threescale_cicd_api_security_scheme|to_nice_json }} - assert: that: - 'threescale_cicd_private_base_url is defined' msg: 'Either the private base url or the tuple backend hostname/scheme must be declared as extra variables (either threescale_cicd_private_base_url or threescale_cicd_api_backend_scheme / threescale_cicd_api_backend_hostname)' -- name: Find the smoke-test flagged operation - set_fact: - threescale_cicd_openapi_smoketest_operation: '{{ threescale_cicd_openapi_file_content|json_query(''paths.*.get[? "x-threescale-smoketests-operation" ].operationId|[0]'') }}' - when: 'threescale_cicd_openapi_smoketest_operation is not defined' - - assert: that: # Operation must exists @@ -177,21 +36,7 @@ # Must NOT have a placeholder in the path - 'threescale_cicd_api_operations[threescale_cicd_openapi_smoketest_operation].path.find("{") == -1' msg: "The smoketest operation {{ threescale_cicd_openapi_smoketest_operation }} must be a GET and cannot have a placeholder in its path." - when: 'threescale_cicd_openapi_smoketest_operation is defined and threescale_cicd_openapi_smoketest_operation|length > 0' - -- set_fact: - threescale_cicd_openapi_smoketest_operation: '{{ threescale_cicd_openapi_smoketest_operation|regex_replace(''[^0-9a-zA-Z_]+'', ''_'') }}' - when: 'threescale_cicd_openapi_smoketest_operation is defined and threescale_cicd_openapi_smoketest_operation|length > 0' - -- set_fact: - threescale_cicd_openapi_smoketest_path: '{{ threescale_cicd_api_basepath }}{{ threescale_cicd_api_operations[threescale_cicd_openapi_smoketest_operation].path }}' - when: 'threescale_cicd_openapi_smoketest_operation is defined and threescale_cicd_openapi_smoketest_operation|length > 0' - - -- name: Compute the service system_name - set_fact: - threescale_cicd_api_system_name: '{{ threescale_cicd_api_environment_name ~ "_" ~ threescale_cicd_api_system_name }}' - when: 'threescale_cicd_api_environment_name is defined' + when: 'threescale_cicd_openapi_smoketest_operation|length > 0' - debug: msg: "Will work on service with system_name = {{ threescale_cicd_api_system_name }}" diff --git a/tasks/steps/requirements.yml b/tasks/steps/requirements.yml index f0bf946..f458d5d 100644 --- a/tasks/steps/requirements.yml +++ b/tasks/steps/requirements.yml @@ -10,6 +10,13 @@ - threescale_cicd_access_token that contains an Access Token with Read/Write privileges on the 3scale Account Management API. This variable is usually set in your inventory file. - threescale_cicd_openapi_file that is the path to the OpenAPI file you want to deploy in 3scale. This variable is usually passed as an extra variable (-e threescale_cicd_openapi_file=...) +- name: Make sure the OpenAPI File Format is YAML or JSON + assert: + that: + - threescale_cicd_openapi_file_format|upper == 'JSON' or threescale_cicd_openapi_file_format|upper == 'YAML' + msg: |- + The threescale_cicd_openapi_file_format parameter needs to be either 'JSON' or 'YAML' + - name: Verify that Ansible version is >= 2.4 assert: that: "ansible_version.full is version_compare('2.4', '>=')" diff --git a/tasks/steps/variables_from_inventory.yml b/tasks/steps/variables_from_inventory.yml index fe73302..5c94bea 100644 --- a/tasks/steps/variables_from_inventory.yml +++ b/tasks/steps/variables_from_inventory.yml @@ -1,26 +1,28 @@ --- -- name: Set the threescale_cicd_sso_issuer_endpoint variable from the inventory - set_fact: - threescale_cicd_sso_issuer_endpoint: '{{ (hostvars[groups[''sso''][0]].scheme|default(''https'')) ~ ''://'' ~ hostvars[groups[''sso''][0]].client_id ~ '':'' ~ hostvars[groups[''sso''][0]].client_secret ~ ''@'' ~ groups[''sso''][0] ~ ''/auth/realms/'' ~ hostvars[groups[''sso''][0]].realm }}' - when: 'threescale_cicd_sso_issuer_endpoint is not defined and ''sso'' in groups and groups[''sso''] > 0' +- name: Abort on deprecated feature -> the "sso" inventory group + fail: + msg: > + You are currently using a deprecated feature (the 'sso' group in your inventory). + Please replace it with the 'threescale_cicd_sso_issuer_endpoint' variable. + Alternatively, you can also bypass this warning by setting the 'threescale_cicd_deprecated_features' + extra variable to 'true'. + when: 'threescale_cicd_sso_issuer_endpoint|default("")|length > 0 and ''sso'' in groups and groups[''sso''] > 0 and threescale_cicd_api_backend_version == ''oidc'' and not threescale_cicd_deprecated_features|default(false)|bool' -- name: Set the threescale_cicd_sso_realm_endpoint variable from the threescale_cicd_sso_issuer_endpoint - set_fact: - threescale_cicd_sso_realm_endpoint: '{{ (threescale_cicd_sso_issuer_endpoint|urlsplit(''scheme'')) ~ ''://'' ~ (threescale_cicd_sso_issuer_endpoint|urlsplit(''hostname'')) ~ (threescale_cicd_sso_issuer_endpoint|urlsplit(''path'')) }}' - when: 'threescale_cicd_sso_realm_endpoint is not defined and threescale_cicd_sso_issuer_endpoint is defined' +- name: Abort on deprecated feature -> the "apicast-sandbox" inventory group + fail: + msg: > + You are currently using a deprecated feature (the 'apicast-sandbox' group in your inventory). + Please replace it with the 'threescale_cicd_apicast_sandbox_endpoint' variable. + Alternatively, you can also bypass this warning by setting the 'threescale_cicd_deprecated_features' + extra variable to 'true'. + when: 'threescale_cicd_apicast_sandbox_endpoint|default("")|length > 0 and ''apicast-sandbox'' in groups and groups[''apicast-sandbox''] > 0 and not threescale_cicd_deprecated_features|default(false)|bool' -- name: Set the threescale_cicd_sso_admin_endpoint variable from the threescale_cicd_sso_realm_endpoint - set_fact: - threescale_cicd_sso_admin_endpoint: '{{ threescale_cicd_sso_realm_endpoint|replace(''/auth/realms/'', ''/auth/admin/realms/'') }}' - when: 'threescale_cicd_sso_admin_endpoint is not defined and threescale_cicd_sso_realm_endpoint is defined' - -- name: Set the threescale_cicd_apicast_sandbox_endpoint variable from the inventory - set_fact: - threescale_cicd_apicast_sandbox_endpoint: '{{ (hostvars[groups[''apicast-sandbox''][0]].scheme|default(''https'')) ~ ''://'' ~ groups[''apicast-sandbox''][0] }}' - when: 'threescale_cicd_apicast_sandbox_endpoint is not defined and ''apicast-sandbox'' in groups and groups[''apicast-sandbox''] > 0' - -- name: Set the threescale_cicd_apicast_production_endpoint variable from the inventory - set_fact: - threescale_cicd_apicast_production_endpoint: '{{ (hostvars[groups[''apicast-production''][0]].scheme|default(''https'')) ~ ''://'' ~ groups[''apicast-production''][0] }}' - when: 'threescale_cicd_apicast_production_endpoint is not defined and ''apicast-production'' in groups and groups[''apicast-production''] > 0' +- name: Abort on deprecated feature -> the "apicast-production" inventory group + fail: + msg: > + You are currently using a deprecated feature (the 'apicast-production' group in your inventory). + Please replace it with the 'threescale_cicd_apicast_production_endpoint' variable. + Alternatively, you can also bypass this warning by setting the 'threescale_cicd_deprecated_features' + extra variable to 'true'. + when: 'threescale_cicd_apicast_production_endpoint|default("")|length > 0 and ''apicast-production'' in groups and groups[''apicast-production''] > 0 and not threescale_cicd_deprecated_features|default(false)|bool' diff --git a/templates/api-calls/create_service.j2 b/templates/api-calls/create_service.j2 index 4343a25..e28d770 100644 --- a/templates/api-calls/create_service.j2 +++ b/templates/api-calls/create_service.j2 @@ -1,8 +1,13 @@ +{% if threescale_cicd_apicast_sandbox_endpoint|default("")|length > 0 or threescale_cicd_apicast_production_endpoint|default("")|length > 0 %} +{% set deployment_type = "self_managed" %} +{% else %} +{% set deployment_type = "hosted" %} +{% endif %} {% set payload = [ 'access_token=' ~ threescale_cicd_access_token|urlencode, 'name=' ~ threescale_cicd_api_name|urlencode, - 'deployment_option=' ~ threescale_cicd_api_deployment_type|urlencode, + 'deployment_option=' ~ deployment_type|urlencode, 'system_name=' ~ threescale_cicd_api_system_name|urlencode, 'backend_version=' ~ threescale_cicd_api_backend_version|urlencode ] diff --git a/templates/api-calls/smoke-test/url.j2 b/templates/api-calls/smoke-test/url.j2 index e19414e..a57e274 100644 --- a/templates/api-calls/smoke-test/url.j2 +++ b/templates/api-calls/smoke-test/url.j2 @@ -1,8 +1,8 @@ {%- if threescale_cicd_smoke_test_env == "staging" -%} -{{ threescale_cicd_apicast_sandbox_endpoint }} +{{ threescale_cicd_apicast_discovered_sandbox_endpoint }} {%- endif -%} {%- if threescale_cicd_smoke_test_env == "production" -%} -{{ threescale_cicd_apicast_production_endpoint }} +{{ threescale_cicd_apicast_discovered_production_endpoint }} {%- endif -%} {{ threescale_cicd_openapi_smoketest_path }} {%- if threescale_cicd_api_security_scheme.type == "apiKey" and threescale_cicd_api_credentials_location == "query" -%} diff --git a/templates/api-calls/update_proxy.j2 b/templates/api-calls/update_proxy.j2 index 9c927bd..99022af 100644 --- a/templates/api-calls/update_proxy.j2 +++ b/templates/api-calls/update_proxy.j2 @@ -11,10 +11,10 @@ {% if threescale_cicd_api_security_scheme.type == 'oauth2' %} {% do payload.append('oidc_issuer_endpoint=' ~ threescale_cicd_sso_issuer_endpoint|urlencode) %} {% endif %} -{% if threescale_cicd_apicast_sandbox_endpoint is defined %} +{% if threescale_cicd_apicast_sandbox_endpoint|default("")|length > 0 %} {% do payload.append('sandbox_endpoint=' ~ threescale_cicd_apicast_sandbox_endpoint|urlencode) %} {% endif %} -{% if threescale_cicd_apicast_production_endpoint is defined %} +{% if threescale_cicd_apicast_production_endpoint|default("")|length > 0 %} {% do payload.append('endpoint=' ~ threescale_cicd_apicast_production_endpoint|urlencode) %} {% endif %} {{ payload|join("&") }} \ No newline at end of file diff --git a/templates/openapi/apicast_production_endpoint.j2 b/templates/openapi/apicast_production_endpoint.j2 new file mode 100644 index 0000000..77b9d03 --- /dev/null +++ b/templates/openapi/apicast_production_endpoint.j2 @@ -0,0 +1,5 @@ +{%- if threescale_cicd_wildcard_domain is defined -%} +{{ threescale_cicd_default_apicast_scheme }}://{{ (threescale_cicd_api_base_system_name ~ "-" ~ threescale_cicd_api_version_major)|regex_replace('[^a-zA-Z0-9-]+', '-')|lower }}{{ threescale_cicd_default_production_suffix }}.{{ threescale_cicd_wildcard_domain }} +{%- elif 'apicast-production' in groups and groups['apicast-production'] > 0 -%} +{{ (hostvars[groups['apicast-production'][0]].scheme|default('https')) ~ '://' ~ groups['apicast-production'][0] }} +{%- endif -%} \ No newline at end of file diff --git a/templates/openapi/apicast_sandbox_endpoint.j2 b/templates/openapi/apicast_sandbox_endpoint.j2 new file mode 100644 index 0000000..02fe17b --- /dev/null +++ b/templates/openapi/apicast_sandbox_endpoint.j2 @@ -0,0 +1,5 @@ +{%- if threescale_cicd_wildcard_domain is defined -%} +{{ threescale_cicd_default_apicast_scheme }}://{{ (threescale_cicd_api_base_system_name ~ "-" ~ threescale_cicd_api_version_major)|regex_replace('[^a-zA-Z0-9-]+', '-')|lower }}{{ threescale_cicd_default_staging_suffix }}.{{ threescale_cicd_wildcard_domain }} +{%- elif 'apicast-sandbox' in groups and groups['apicast-sandbox'] > 0 -%} +{{ (hostvars[groups['apicast-sandbox'][0]].scheme|default('https')) ~ '://' ~ groups['apicast-sandbox'][0] }} +{%- endif -%} \ No newline at end of file diff --git a/templates/openapi/generate_base_system_name.j2 b/templates/openapi/generate_base_system_name.j2 new file mode 100644 index 0000000..63ffd79 --- /dev/null +++ b/templates/openapi/generate_base_system_name.j2 @@ -0,0 +1,6 @@ +{% if 'x-threescale-system-name' in threescale_cicd_openapi_file_content.info %} +{% set extracted_system_name = threescale_cicd_openapi_file_content.info['x-threescale-system-name']|regex_replace('[^a-zA-Z0-9_]+', '_')|lower %} +{% else %} +{% set extracted_system_name = threescale_cicd_openapi_file_content.info['title']|default('api')|regex_replace('[^a-zA-Z0-9_]+', '_')|lower %} +{% endif %} +{{ extracted_system_name }} \ No newline at end of file diff --git a/templates/openapi/generate_final_system_name.j2 b/templates/openapi/generate_final_system_name.j2 new file mode 100644 index 0000000..b001ec1 --- /dev/null +++ b/templates/openapi/generate_final_system_name.j2 @@ -0,0 +1,4 @@ +{%- if threescale_cicd_api_environment_name is defined -%} +{%- set system_name_prefix = threescale_cicd_api_environment_name|default("")|regex_replace('[^a-zA-Z0-9_]+', '_') ~ "_" -%} +{%- endif -%} +{{ system_name_prefix|default("") }}{{ threescale_cicd_api_base_system_name }}_{{ threescale_cicd_api_version_major|regex_replace('[^a-zA-Z0-9_]+', '_') }} \ No newline at end of file diff --git a/templates/openapi/openapi_operations.j2 b/templates/openapi/openapi_operations.j2 new file mode 100644 index 0000000..0beced1 --- /dev/null +++ b/templates/openapi/openapi_operations.j2 @@ -0,0 +1,26 @@ +{% set operations = {} -%} +{% if 'paths' in threescale_cicd_openapi_file_content -%} + {% for path, verbs in threescale_cicd_openapi_file_content['paths'].items() -%} + {% if path.startswith('/') -%} + {% for verb, method_description in verbs.items() -%} + {% if verb != '$ref' and verb != 'parameters' -%} + {% if 'operationId' in method_description -%} + {% set operation_id = method_description['operationId'] -%} + {% else -%} + {% set operation_id = verb.upper() + path -%} + {% endif -%} + {% set operation_id = operation_id|regex_replace('[^0-9a-zA-Z_]+', '_') -%} + {% set operation = { operation_id: { 'path': path, 'verb': verb } } -%} + {% if 'summary' in method_description -%} + {% do operation[operation_id].update({ 'friendly_name': method_description.summary }) -%} + {% endif -%} + {% if 'description' in method_description -%} + {% do operation[operation_id].update({ 'description': method_description.description }) -%} + {% endif -%} + {% do operations.update(operation) -%} + {% endif -%} + {% endfor -%} + {% endif -%} + {% endfor -%} +{% endif -%} +{{ operations }} \ No newline at end of file diff --git a/templates/openapi/private_base_url.j2 b/templates/openapi/private_base_url.j2 new file mode 100644 index 0000000..d6ca592 --- /dev/null +++ b/templates/openapi/private_base_url.j2 @@ -0,0 +1,11 @@ +{%- if threescale_cicd_api_backend_hostname is not defined and 'host' in threescale_cicd_openapi_file_content -%} +{%- set backend_hostname = threescale_cicd_openapi_file_content.host -%} +{%- else -%} +{%- set backend_hostname = threescale_cicd_api_backend_hostname -%} +{%- endif -%} +{%- if threescale_cicd_api_backend_scheme is not defined -%} +{%- set backend_scheme = threescale_cicd_openapi_file_content.schemes|default(["http"])|first -%} +{%- else -%} +{%- set backend_scheme = threescale_cicd_api_backend_scheme -%} +{%- endif -%} +{{ backend_scheme }}://{{ backend_hostname }} \ No newline at end of file diff --git a/templates/openapi/service_name.j2 b/templates/openapi/service_name.j2 new file mode 100644 index 0000000..8ffc392 --- /dev/null +++ b/templates/openapi/service_name.j2 @@ -0,0 +1,5 @@ +{%- if threescale_cicd_api_environment_name is defined -%} +{{ threescale_cicd_api_default_name }} ({{ threescale_cicd_api_environment_name|upper }}, v{{ threescale_cicd_api_version }}) +{%- else -%} +{{ threescale_cicd_api_default_name }} (v{{ threescale_cicd_api_version }}) +{%- endif -%} \ No newline at end of file diff --git a/templates/openapi/sso_issuer_endpoint.j2 b/templates/openapi/sso_issuer_endpoint.j2 new file mode 100644 index 0000000..34f3f24 --- /dev/null +++ b/templates/openapi/sso_issuer_endpoint.j2 @@ -0,0 +1,3 @@ +{%- if 'sso' in groups and groups['sso'] > 0 -%} +{{ (hostvars[groups['sso'][0]].scheme|default('https')) ~ '://' ~ hostvars[groups['sso'][0]].client_id ~ ':' ~ hostvars[groups['sso'][0]].client_secret ~ '@' ~ groups['sso'][0] ~ '/auth/realms/' ~ hostvars[groups['sso'][0]].realm }} +{%- endif -%} \ No newline at end of file diff --git a/templates/rewritten_openapi.j2 b/templates/rewritten_openapi.j2 index 87614fb..a19e4dd 100644 --- a/templates/rewritten_openapi.j2 +++ b/templates/rewritten_openapi.j2 @@ -11,8 +11,8 @@ {# Update the security definitions #} {% do new_openapi.update({ "securityDefinitions": security_definitions }) %} {# Update the "schemes" and "hostname" fields with the public apicast production URL #} -{% set apicast_production_scheme = threescale_cicd_apicast_production_endpoint|urlsplit('scheme') %} -{% set apicast_production_hostname = threescale_cicd_apicast_production_endpoint|urlsplit('hostname') %} +{% set apicast_production_scheme = threescale_cicd_apicast_discovered_production_endpoint|urlsplit('scheme') %} +{% set apicast_production_hostname = threescale_cicd_apicast_discovered_production_endpoint|urlsplit('hostname') %} {% do new_openapi.update({ "schemes": [ apicast_production_scheme ], "host": apicast_production_hostname diff --git a/tests/3scale-saas-with-hosted-apicast-apikey.yml b/tests/3scale-saas-with-hosted-apicast-apikey.yml index 9a8cff1..594b9fc 100644 --- a/tests/3scale-saas-with-hosted-apicast-apikey.yml +++ b/tests/3scale-saas-with-hosted-apicast-apikey.yml @@ -8,10 +8,14 @@ threescale_cicd_openapi_file_format: 'JSON' threescale_cicd_api_backend_hostname: echo-api.3scale.net threescale_cicd_openapi_smoketest_operation: GET_beer - roles: - # Test first deployment - - { role: 'nmasse-itix.threescale-cicd', vars: { 'round': 1 } } + tasks: + # Test a first deployment + - import_role: + name: 'nmasse-itix.threescale-cicd' # Verify idempotence - - { role: 'nmasse-itix.threescale-cicd', vars: { 'round': 2 } } - post_tasks: - - import_tasks: 'cleanup.yaml' + - import_role: + name: 'nmasse-itix.threescale-cicd' + # Delete the service + - import_role: + name: 'nmasse-itix.threescale-cicd' + tasks_from: 'cleanup' diff --git a/tests/3scale-saas-with-hosted-apicast-multi-environment.yml b/tests/3scale-saas-with-hosted-apicast-multi-environment.yml new file mode 100644 index 0000000..da34296 --- /dev/null +++ b/tests/3scale-saas-with-hosted-apicast-multi-environment.yml @@ -0,0 +1,45 @@ +--- + +- name: Deploy the Beer Catalog API to a 3scale SaaS instance in multi environment + hosts: threescale + gather_facts: no + vars: + threescale_cicd_openapi_file: '{{ playbook_dir }}/api/beer-catalog-api.json' + threescale_cicd_openapi_file_format: 'JSON' + threescale_cicd_api_backend_hostname: echo-api.3scale.net + threescale_cicd_openapi_smoketest_operation: GET_beer + threescale_cicd_api_base_system_name: beer_catalog + tasks: + # Deploy in DEV + - import_role: + name: 'nmasse-itix.threescale-cicd' + vars: + threescale_cicd_api_environment_name: dev + # Deploy in TEST + - import_role: + name: 'nmasse-itix.threescale-cicd' + vars: + threescale_cicd_api_environment_name: test + # Deploy in PROD + - import_role: + name: 'nmasse-itix.threescale-cicd' + vars: + threescale_cicd_api_environment_name: prod + # Cleanup the DEV + - import_role: + name: 'nmasse-itix.threescale-cicd' + tasks_from: 'cleanup' + vars: + threescale_cicd_api_environment_name: dev + # Cleanup the TEST + - import_role: + name: 'nmasse-itix.threescale-cicd' + tasks_from: 'cleanup' + vars: + threescale_cicd_api_environment_name: test + # Cleanup the PROD + - import_role: + name: 'nmasse-itix.threescale-cicd' + tasks_from: 'cleanup' + vars: + threescale_cicd_api_environment_name: prod diff --git a/tests/3scale-saas-with-hosted-apicast-oidc.yml b/tests/3scale-saas-with-hosted-apicast-oidc.yml index 3922ab9..ba2633e 100644 --- a/tests/3scale-saas-with-hosted-apicast-oidc.yml +++ b/tests/3scale-saas-with-hosted-apicast-oidc.yml @@ -5,10 +5,14 @@ gather_facts: no vars: threescale_cicd_openapi_file: '{{ playbook_dir }}/api/echo-api-oidc.yaml' - roles: - # Test first deployment - - { role: 'nmasse-itix.threescale-cicd', vars: { 'round': 1 } } + tasks: + # Test a first deployment + - import_role: + name: 'nmasse-itix.threescale-cicd' # Verify idempotence - - { role: 'nmasse-itix.threescale-cicd', vars: { 'round': 2 } } - post_tasks: - - import_tasks: 'cleanup.yaml' + - import_role: + name: 'nmasse-itix.threescale-cicd' + # Delete the service + - import_role: + name: 'nmasse-itix.threescale-cicd' + tasks_from: 'cleanup' diff --git a/tests/3scale-saas-with-hosted-apicast-with-basePath.yml b/tests/3scale-saas-with-hosted-apicast-with-basePath.yml index 90be8ec..dfb7ff4 100644 --- a/tests/3scale-saas-with-hosted-apicast-with-basePath.yml +++ b/tests/3scale-saas-with-hosted-apicast-with-basePath.yml @@ -5,10 +5,14 @@ gather_facts: no vars: threescale_cicd_openapi_file: '{{ playbook_dir }}/api/echo-api-with-basePath.yaml' - roles: - # Test first deployment - - { role: 'nmasse-itix.threescale-cicd', vars: { 'round': 1 } } + tasks: + # Test a first deployment + - import_role: + name: 'nmasse-itix.threescale-cicd' # Verify idempotence - - { role: 'nmasse-itix.threescale-cicd', vars: { 'round': 2 } } - post_tasks: - - import_tasks: 'cleanup.yaml' + - import_role: + name: 'nmasse-itix.threescale-cicd' + # Delete the service + - import_role: + name: 'nmasse-itix.threescale-cicd' + tasks_from: 'cleanup' diff --git a/tests/inventory.j2 b/tests/inventory.j2 index 57f98a2..e291b14 100644 --- a/tests/inventory.j2 +++ b/tests/inventory.j2 @@ -6,12 +6,4 @@ ansible_connection=local [threescale:vars] threescale_cicd_access_token={{ threescale_inventory.threescale_hosted.access_token }} - -[sso] -{{ threescale_inventory.sso.host }} - -[sso:vars] -realm={{ threescale_inventory.sso.realm }} -client_id={{ threescale_inventory.sso.client_id }} -client_secret={{ threescale_inventory.sso.client_secret }} -scheme=https +threescale_cicd_sso_issuer_endpoint=https://{{ threescale_inventory.sso.client_id }}:{{ threescale_inventory.sso.client_secret }}@{{ threescale_inventory.sso.host }}/auth/realms/{{ threescale_inventory.sso.realm }} diff --git a/vars/main.yml b/vars/main.yml index f29b530..b116f88 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -9,6 +9,35 @@ threescale_cicd_metrics_to_delete: '{{ lookup(''template'', ''metrics_to_delete # The OpenAPI file to be pushed to 3scale as an ActiveDocs threescale_cicd_openapi_rewritten: '{{ lookup(''template'', ''rewritten_openapi.j2'') }}' +# Compute the Keycloak Realm endpoint from the threescale_cicd_sso_issuer_endpoint +threescale_cicd_sso_realm_endpoint: '{{ (threescale_cicd_sso_issuer_endpoint|urlsplit(''scheme'')) ~ ''://'' ~ (threescale_cicd_sso_issuer_endpoint|urlsplit(''hostname'')) ~ (threescale_cicd_sso_issuer_endpoint|urlsplit(''path'')) }}' + +# Compute the Keycloak REST Admin Endpoint from the threescale_cicd_sso_realm_endpoint +threescale_cicd_sso_admin_endpoint: '{{ threescale_cicd_sso_realm_endpoint|replace(''/auth/realms/'', ''/auth/admin/realms/'') }}' + +## +## OpenAPI Specification File parsing +## +threescale_cicd_openapi_file_content: '{{ lookup(''file'', threescale_cicd_openapi_file)|from_json if threescale_cicd_openapi_file_format|upper == ''JSON'' else lookup(''file'', threescale_cicd_openapi_file)|from_yaml }}' +threescale_cicd_openapi_file_version: '{{ threescale_cicd_openapi_file_content.swagger }}' +threescale_cicd_api_default_name: '{{ threescale_cicd_openapi_file_content.info.title|default("API") }}' +threescale_cicd_api_name: '{{ lookup(''template'', ''openapi/service_name.j2'') }}' +threescale_cicd_api_description: '{{ threescale_cicd_openapi_file_content.info.description|default("") }}' +threescale_cicd_api_version: '{{ threescale_cicd_openapi_file_content.info.version|default("0.0.1") }}' +threescale_cicd_api_basepath: '{{ threescale_cicd_openapi_file_content.basePath|default("") }}' +threescale_cicd_api_operations: '{{ lookup(''template'', ''openapi/openapi_operations.j2'') }}' +threescale_cicd_api_version_components: '{{ threescale_cicd_api_version.split(".") }}' +threescale_cicd_api_version_major: '{{ threescale_cicd_api_version_components|first }}' +threescale_cicd_api_security_requirements: '{{ threescale_cicd_openapi_file_content.security|default([]) }}' +threescale_cicd_api_security_definitions: '{{ threescale_cicd_openapi_file_content.securityDefinitions|default({}) }}' +threescale_cicd_api_security_scheme_name: '{{ threescale_cicd_api_security_requirements[0].keys()[0]|default(''none'') }}' +threescale_cicd_api_security_scheme: '{{ threescale_cicd_api_security_definitions[threescale_cicd_api_security_scheme_name] if threescale_cicd_api_security_scheme_name in threescale_cicd_api_security_definitions else {} }}' +threescale_cicd_api_backend_version: '{{ threescale_cicd_backend_version_mapping[threescale_cicd_api_security_scheme.type] }}' +threescale_cicd_backend_version_mapping: + apiKey: '1' + oauth2: 'oidc' +threescale_cicd_openapi_smoketest_path: '{{ threescale_cicd_api_basepath }}{{ threescale_cicd_api_operations[threescale_cicd_openapi_smoketest_operation].path }}' + ## ## ID Lookup Variables ##