Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
41b6cb2
APM-1733 Resolve missing attribute error
HenryIwanejko Jan 18, 2021
b9519db
APM-1733 Update var name
HenryIwanejko Jan 18, 2021
97b7dc9
APM-1733 Readd snapshot var
HenryIwanejko Jan 18, 2021
81b4430
Merge branch 'master' into APM-1733-fix-apidoc-deploy
HenryIwanejko Jan 19, 2021
cd97883
Merge pull request #214 from NHSDigital/APM-1733-fix-apidoc-deploy
HenryIwanejko Jan 19, 2021
4456404
AMP-1730 First stab at schema unit testing
strutt Jan 19, 2021
90dc019
APM-1730 Move roles relying nhsd.apigee inside collection
strutt Jan 19, 2021
6a748cf
APM-1162 Debug print github status url
Jan 19, 2021
039eb09
Merge pull request #217 from NHSDigital/apm-1162-print-github-status-url
Jan 19, 2021
546de03
Bug fix UUID return
strutt Jan 20, 2021
97bff2f
APM-1740 Refactor apigee+meta args to single Manifest
strutt Jan 20, 2021
75554b8
APM-1753 fix default pipeline args for PRs
strutt Jan 21, 2021
6f76d6f
Merge pull request #218 from NHSDigital/APM-1753-fix-pr-pipeline-defa…
acarriedev Jan 21, 2021
a77c914
Switch to ansible-test style testing
strutt Jan 21, 2021
69a877e
APM-1162 Improve deploy-api-pipelines script
strutt Jan 22, 2021
90df4df
Merge pull request #221 from NHSDigital/APM-1162-deploy-api-pipelines…
Jan 22, 2021
9be0f22
Merge branch 'master' into APM-1740-manifest-schema
strutt Jan 22, 2021
255d312
APM-1701 Set up products default scopes
Valentinolucas Jan 22, 2021
c0f6c9b
APM-1701 fix typo
Valentinolucas Jan 22, 2021
0f4f4f1
Merge pull request #220 from NHSDigital/APM-1740-manifest-schema
strutt Jan 22, 2021
99fd80f
Merge branch 'master' of github.com:NHSDigital/api-management-utils i…
Valentinolucas Jan 22, 2021
f040f8d
APM-1701 Change default scope names
Valentinolucas Jan 22, 2021
995eae1
Merge pull request #223 from NHSDigital/APM-1701-Setup-products-defau…
Jan 26, 2021
0ac20e0
APM-1162 Explicitly set dependsOn for manual approval stage
strutt Jan 26, 2021
37db798
Merge pull request #224 from NHSDigital/APM-1162-debug-default-releas…
Jan 26, 2021
80cb0bc
APM-1701 remove dev as deploy environment
Valentinolucas Jan 26, 2021
12ce470
Merge pull request #225 from NHSDigital/APM-1701-remove-dev-as-deploy…
Jan 26, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ jobs:
run: poetry install
if: steps.cache.outputs.cache-hit != 'true'

- name: Test ansible collection
env:
PYTHONPATH: collections
working-directory: ansible
run: poetry run pytest
- name: Unit test ansible collection
working-directory: ansible/collections/ansible_collections/nhsd/apigee
run: poetry run ansible-test units --python=3.8

- name: Integration test ansible collection
working-directory: ansible/collections/ansible_collections/nhsd/apigee
run: poetry run ansible-test integration --python=3.8
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ def run(self, tmp=None, task_vars=None):
args, errors = self.validate_args(ApplyPullRequestNamespace)
if errors:
return errors
return {"apigee": args.apigee.dict(), "changed": False}
return {"manifest": args.manifest.dict(), "changed": False}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ def run(self, tmp=None, task_vars=None):
args, errors = self.validate_args(ValidateManifest)
if errors:
return errors
return {"apigee": args.apigee.dict(), "changed": False}
return {"manifest": args.manifest.dict(), "changed": False}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import pydantic

from ansible_collections.nhsd.apigee.plugins.module_utils.models.manifest.apigee import ManifestApigee
from ansible_collections.nhsd.apigee.plugins.module_utils.models.manifest.meta import ManifestMeta
from ansible_collections.nhsd.apigee.plugins.module_utils.models.manifest.manifest import Manifest


class ApplyPullRequestNamespace(pydantic.BaseModel):
Expand All @@ -25,28 +24,26 @@ class ApplyPullRequestNamespace(pydantic.BaseModel):

:param pull_request: A string like 'pr-1234' for pull request
number 1234.
:param meta: The 'meta' item your manifest.yml.
:param apigee: The 'apigee' item from your manifest.yml
:param manifest: The content of your manifest.yml.
"""

pull_request: pydantic.constr(regex=r"^pr-[0-9]+$") # i.e. 'pr-1234'
meta: ManifestMeta
apigee: ManifestApigee
manifest: Manifest

@pydantic.validator("apigee")
def apply_namespace(cls, apigee, values):
api_name = values["meta"].api.name
@pydantic.validator("manifest")
def apply_namespace(cls, manifest, values):
api_name = manifest.meta.api.name

apigee.environments = [
env for env in apigee.environments if env.name.startswith("internal-dev")
manifest.apigee.environments = [
env for env in manifest.apigee.environments if env.name.startswith("internal-dev")
]

# here we want:
# canary-api-internal-dev -> canary-api-pr-1234
# canary-api-internal-dev-sandbox -> canary-api-pr-1234-sandbox
old = "internal-dev"
new = values["pull_request"]
for env in apigee.environments:
for env in manifest.apigee.environments:
for product in env.products:
product.name = product.name.replace(old, new, 1)
product.proxies = [
Expand All @@ -64,4 +61,4 @@ def apply_namespace(cls, apigee, values):
)
entry.specId = entry.specId.replace(old, new, 1)

return apigee
return manifest
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import os
import re
import pydantic
from ansible_collections.nhsd.apigee.plugins.module_utils.models.manifest.apigee import (
ManifestApigee,
)
from ansible_collections.nhsd.apigee.plugins.module_utils.models.manifest.meta import (
ManifestMeta,
from ansible_collections.nhsd.apigee.plugins.module_utils.models.manifest.manifest import (
Manifest,
)


Expand Down Expand Up @@ -33,15 +30,15 @@ def correct_namespace(name, api_name, env_name) -> bool:


class ValidateManifest(pydantic.BaseModel):
meta: ManifestMeta
service_name: str = ""
dist_dir: pydantic.DirectoryPath = ""
apigee: ManifestApigee
manifest: Manifest
service_name: str = ""

@pydantic.validator("service_name")
def check_service_name(cls, service_name, values):
if service_name:
meta = values.get("meta")
manifest = values.get("manifest")
meta = manifest.meta
if not meta:
return
api_name = meta.api.name
Expand All @@ -50,25 +47,27 @@ def check_service_name(cls, service_name, values):
f"pipeline defined SERVICE_NAME ('{service_name}') does not begin with manifest defined meta.api.name ('{api_name}')"
)

@pydantic.validator("apigee", pre=True)
def prepend_dist_dir_to_spec_paths(cls, apigee, values):
@pydantic.validator("manifest", pre=True)
def prepend_dist_dir_to_spec_paths(cls, manifest, values):
dist_dir = values.get("dist_dir")
if dist_dir:
for env_dict in apigee["environments"]:
for spec_dict in env_dict["specs"]:
path = spec_dict.get("path")
if path is not None:
spec_dict["path"] = os.path.join(dist_dir, path)
return apigee
print(dist_dir)
if not dist_dir:
return manifest
apigee = manifest["apigee"]
for env_dict in apigee["environments"]:
for spec_dict in env_dict["specs"]:
path = spec_dict.get("path")
if path is not None:
spec_dict["path"] = os.path.join(dist_dir, path)
return manifest

@pydantic.validator("apigee")
def check_namespacing(cls, apigee, values):
meta = values.get("meta")
if not meta:
@pydantic.validator("manifest")
def check_namespacing(cls, manifest, values):
if not manifest.meta:
return
api_name = meta.api.name
api_name = manifest.meta.api.name

for env in apigee.environments:
for env in manifest.apigee.environments:
if env is None:
continue
for product in env.products:
Expand All @@ -81,4 +80,4 @@ def check_namespacing(cls, apigee, values):
raise ValueError(
f"{spec.name} does not conform to namespace for {api_name}-*{env.name}"
)
return apigee
return manifest
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class ApigeeSpec(pydantic.BaseModel):

@pydantic.validator("content", always=True)
def load_content(cls, content, values):
if content is not None:
return content
path = values.get("path")
if not path: # When path does not validate.
return None
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import pydantic

from ansible_collections.nhsd.apigee.plugins.module_utils.models.manifest.apigee import ManifestApigee
from ansible_collections.nhsd.apigee.plugins.module_utils.models.manifest.meta import ManifestMeta


class Manifest(pydantic.BaseModel):
apigee: ManifestApigee
meta: ManifestMeta
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import typing
import pydantic

SCHEMA_VERSION = "1.0.0"


class ManifestMetaApi(pydantic.BaseModel):
name: pydantic.constr(regex=r"^[a-z]+(-[a-z]+)*$")
id: pydantic.UUID4

def dict(self, **kwargs):
native = super().dict(**kwargs)
native.update({"id": str(native["id"])})
return native


class ManifestMeta(pydantic.BaseModel):
schema_version: pydantic.constr(regex=r"[0-9]+(\.[0-9]+){0,2}")
Expand All @@ -14,15 +20,15 @@ class ManifestMeta(pydantic.BaseModel):
@pydantic.validator("schema_version")
def validate_schema_version(cls, schema_version):
semantic_parts = schema_version.split(".")

MAJOR, MINOR, PATCH = [int(x) for x in SCHEMA_VERSION.split(".")]
major = int(semantic_parts[0])
minor = 0 if len(semantic_parts) < 2 else int(semantic_parts[1])
patch = 0 if len(semantic_parts) < 3 else int(semantic_parts[2])

if major != 1:
raise ValueError(f"Invalid major version {major} for schema_version")
if minor != 0:
raise ValueError(f"Invalid minor version {minor} for major schema_version {major}")
if patch != 0:
raise ValueError(f"Invalid patch version {patch} for major/minor schema_version {major}.{minor}")
# minor = int(MAJOR) if len(semantic_parts) < 2 else int(semantic_parts[1])
# patch = int(PATCH) if len(semantic_parts) < 3 else int(semantic_parts[2])

if major != MAJOR:
raise ValueError(
f"Current schema version is {SCHEMA_VERSION}. All minor and patch changes are backwards compatible."
)

return schema_version
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
- name: ensure valid APIGEE_ORGANIZATION
fail:
msg: Invalid APIGEE_ORGANIZATION
when: APIGEE_ORGANIZATION not in APIGEE_ORGANIZATIONS

- name: ensure valid APIGEE_ENVIRONMENT
fail:
msg: Invalid APIGEE_ENVIRONMENT
when: APIGEE_ENVIRONMENT not in APIGEE_ENVIRONMENTS[APIGEE_ORGANIZATION]

# NOTE: Once the manifest handles proxies this can be removed!
# it must remain (for now) to handle the frequent case where
# multile Azure pipeline stages deploy to the same Apigee
# environment.
- name: ensure SERVICE_NAME
fail:
msg: Invalid SERVICE_NAME
when: not SERVICE_NAME

- name: load manifest
set_fact:
manifest: "{{ lookup('file', (DIST_DIR, 'manifest.yml') | path_join) | from_yaml }}"

- name: validate manifest
nhsd.apigee.validate_manifest:
service_name: "{{ SERVICE_NAME }}"
dist_dir: "{{ DIST_DIR }}"
manifest: "{{ manifest }}"
register: validated_manifest

- name: apply pr namespace
nhsd.apigee.apply_pull_request_namespace:
manifest: "{{ validated_manifest['manifest'] }}"
pull_request: "{{ PULL_REQUEST }}"
register: pr_manifest
when: PULL_REQUEST != ""

- name: select regular or pr manifest
set_fact:
manifest: "{{ pr_manifest['manifest'] | default(validated_manifest['manifest']) }}"

- name: select environment
set_fact:
apigee_environment: "{{ manifest.apigee.environments | selectattr('name', '==', APIGEE_ENVIRONMENT) | list | first }}"

- name: deploy apigee products
nhsd.apigee.deploy_product:
product: "{{ item }}"
organization: "{{ APIGEE_ORGANIZATION }}"
access_token: "{{ APIGEE_ACCESS_TOKEN }}"
loop: "{{ apigee_environment.products }}"
# Since proxies are uploaded and deployed per-Azure pipeline
# stage, this prevents a race condition on the first deployment,
# since products can only be created if they reference proxies
# that exist. This safety check can be removed when the
# manifest manages proxies too.
when: item.name | regex_search('^' + SERVICE_NAME + '-' + PULL_REQUEST | default(APIGEE_ENVIRONMENT))

- name: deploy apigee specs
nhsd.apigee.deploy_spec:
spec: "{{ item }}"
organization: "{{ APIGEE_ORGANIZATION }}"
access_token: "{{ APIGEE_ACCESS_TOKEN }}"
loop: "{{ apigee_environment.specs }}"

- name: deploy api catalog
nhsd.apigee.deploy_api_catalog_item:
api_catalog_item: "{{ item }}"
organization: "{{ APIGEE_ORGANIZATION }}"
access_token: "{{ APIGEE_ACCESS_TOKEN }}"
loop: "{{ apigee_environment.api_catalog }}"
# See comment on product loop control. (I believe APIDocs
# actually can reference products that don't exist. But this is
# cleaner.)
when: item.edgeAPIProductName | regex_search('^' + SERVICE_NAME + '-' + PULL_REQUEST | default(APIGEE_ENVIRONMENT))
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
APIGEE_ORGANIZATIONS: [nhsd-nonprod, nhsd-prod]
APIGEE_ENVIRONMENTS:
nhsd-nonprod: [internal-dev, internal-dev-sandbox, internal-qa, internal-qa-sandbox, ref]
nhsd-prod: [dev, sandbox, int, prod]

APIGEE_ENVIRONMENT: "{{ lookup('env','APIGEE_ENVIRONMENT') }}"
APIGEE_ORGANIZATION: "{{ lookup('env', 'APIGEE_ORGANIZATION') }}"
APIGEE_ACCESS_TOKEN: "{{ lookup('env', 'APIGEE_ACCESS_TOKEN') }}"
SERVICE_NAME: "{{ lookup('env', 'SERVICE_NAME') }}"
PULL_REQUEST: "{{ lookup('env', 'PULL_REQUEST') }}"
DIST_DIR: "{{ lookup('env', 'DIST_DIR') }}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
- name: set full path to manifest file
set_fact:
manifest_template: "{{ (DIST_DIR, 'manifest_template.yml') | path_join }}"
manifest_file: "{{ (DIST_DIR, 'manifest.yml') | path_join }}"

- name: load template streams
set_fact:
template_streams: "{{ lookup('file', manifest_template ).split('\n---\n') | list }}"

- name: Set template vars
set_fact:
"{{ item.key }}": "{{ item.value }}"
loop: "{{ template_streams | first | from_yaml | dict2items }}"

- name: template manifest
set_fact:
manifest: "{{ lookup('template', manifest_template) | from_yaml_all | list | last }}"

- name: validate templated manifest
nhsd.apigee.validate_manifest:
manifest: "{{ manifest }}"
service_name: "{{ SERVICE_NAME }}"
dist_dir: "{{ DIST_DIR }}"

- name: write template to file
copy:
content: "{{ manifest | to_nice_yaml(indent=2) }}"
dest: "{{ manifest_file }}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SERVICE_NAME: "{{ lookup('env', 'SERVICE_NAME')}}"
DIST_DIR: "{{ lookup('env', 'DIST_DIR') }}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

- name: check DIST_DIR
fail:
msg: "DIST_DIR not defined"
when: DIST_DIR is not defined

- name: set full path to manifest file
set_fact:
manifest_file: "{{ (DIST_DIR, 'manifest.yml') | path_join }}"

- name: load manifest
set_fact:
manifest: "{{ lookup('file', manifest_file ) | from_yaml }}"

- name: validate manifest
nhsd.apigee.validate_manifest:
manifest: "{{ manifest }}"
dist_dir: "{{ DIST_DIR }}"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DIST_DIR: "{{ lookup('env', 'DIST_DIR') }}"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Loading