Skip to content

Commit

Permalink
Merge arm-processor code into Jacob's branch (#127)
Browse files Browse the repository at this point in the history
* Initial ArmInputTemplate class

* Outline ArmBuildProcessor hierarchy

* Update VNF tests to keep built output for comparison

* Initial ArmInputTemplate class

* Outline ArmBuildProcessor hierarchy

* Update VNF tests to keep built output for comparison

* WIP, for sharing with Jacob

* Incremental update

* Approximately complete ARM processor. No tests, no testing yet.

* Fix artifact store reference

---------

Co-authored-by: Andy Churchard <andy.churchard@metaswitch.com>
  • Loading branch information
jddarby and Andy Churchard authored Dec 14, 2023
1 parent a4b4222 commit 2b6e17c
Show file tree
Hide file tree
Showing 19 changed files with 646 additions and 12 deletions.
136 changes: 136 additions & 0 deletions src/aosm/azext_aosm/build_processors/arm_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from abc import abstractmethod
from dataclasses import dataclass
from typing import Any, Dict, final, List, Tuple

from azext_aosm.build_processors.base_processor import BaseBuildProcessor
from azext_aosm.common.artifact import LocalFileACRArtifact
from azext_aosm.common.local_file_builder import LocalFileBuilder
from azext_aosm.inputs.arm_template_input import ArmTemplateInput

from ..vendored_sdks.models import (
DependsOnProfile,
ResourceElementTemplate,
ReferencedResource,
ManifestArtifactFormat,
NetworkFunctionApplication,
ArmResourceDefinitionResourceElementTemplateDetails,
AzureCoreNetworkFunctionArmTemplateApplication,
AzureCoreArtifactType,
AzureCoreArmTemplateArtifactProfile,
ArmTemplateArtifactProfile,
)


@dataclass
class BaseArmBuildProcessor(BaseBuildProcessor):
"""
Base class for ARM template processors.
This class loosely implements the Template Method pattern to define the steps required
to generate NF applications and RETs from a given ARM template.
The steps are as follows:
- generate_schema
- generate_mappings
- generate_artifact_profile
- generate_nfvi_specific_nf_application
"""

name: str
input_artifact: ArmTemplateInput

def get_artifact_manifest_list(self) -> List[ManifestArtifactFormat]:
"""Get the artifact list."""
return [
ManifestArtifactFormat(
artifact_name=self.input_artifact.artifact_name,
artifact_type=AzureCoreArtifactType.ARM_TEMPLATE,
artifact_version=self.input_artifact.artifact_version,
)
]

@abstractmethod
def get_artifact_details(self) -> Tuple[List[LocalFileACRArtifact], List[LocalFileBuilder]]:
"""Get the artifact details."""
return (
[
LocalFileACRArtifact(
artifact_manifest=ManifestArtifactFormat(
artifact_name=self.input_artifact.artifact_name,
artifact_type=AzureCoreArtifactType.ARM_TEMPLATE,
artifact_version=self.input_artifact.artifact_version,
),
file_path=self.input_artifact.artifact_path,
)
],
[],
)

@final
def generate_nf_application(self) -> NetworkFunctionApplication:
return self.generate_nfvi_specific_nf_application()

# TODO: Should this actually be a cached property?
def generate_schema(self) -> Dict[str, Any]:
return self.input_artifact.get_schema()

def generate_mappings(self) -> Dict[str, str]:
template_parameters = {}
vm_parameters = self.generate_schema()

# TODO: Document that this no longer appends "Image" to the image name supplied by user in input.jsonc
# TODO: Document that the parameters are no longer ordered, and create story to reimplement ordering
for key in vm_parameters:
template_parameters[key] = f"{{deployParameters.{key}}}"

return template_parameters

# @abstractmethod?
def generate_artifact_profile(self) -> AzureCoreArmTemplateArtifactProfile:
artifact_profile = ArmTemplateArtifactProfile(
template_name=self.input_artifact.artifact_name,
template_version=self.input_artifact.artifact_version,
)
return AzureCoreArmTemplateArtifactProfile(
artifact_store=ReferencedResource(id=""),
template_artifact_profile=artifact_profile,
)

@abstractmethod
def generate_nfvi_specific_nf_application(self):
pass

def generate_resource_element_template(self) -> ResourceElementTemplate:
return ArmResourceDefinitionResourceElementTemplateDetails()


class AzureCoreArmBuildProcessor(BaseArmBuildProcessor):
"""
This class represents an ARM template processor for Azure Core.
"""

def generate_nfvi_specific_nf_application(
self,
) -> AzureCoreNetworkFunctionArmTemplateApplication:
return AzureCoreNetworkFunctionArmTemplateApplication(
name=self.name,
depends_on_profile=DependsOnProfile(),
artifact_type=AzureCoreArtifactType.ARM_TEMPLATE,
artifact_profile=self.generate_artifact_profile(),
deploy_parameters_mapping_rule_profile=self.generate_mappings(),
)


class NexusArmBuildProcessor(BaseArmBuildProcessor):
"""
Not implemented yet. This class represents a processor for generating ARM templates specific to Nexus.
"""

def generate_nfvi_specific_nf_application(self):
return NotImplementedError
2 changes: 1 addition & 1 deletion src/aosm/azext_aosm/build_processors/base_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
# --------------------------------------------------------------------------------------------

from abc import ABC, abstractmethod
from typing import List, Tuple
from dataclasses import dataclass
from typing import List, Tuple
from azext_aosm.inputs.base_input import BaseInput
from azext_aosm.common.artifact import BaseArtifact
from azext_aosm.common.local_file_builder import LocalFileBuilder
Expand Down
8 changes: 4 additions & 4 deletions src/aosm/azext_aosm/inputs/arm_template_input.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
from dataclasses import dataclass
from pathlib import Path
import json
from pathlib import Path
from typing import Any, Dict

from azext_aosm.inputs.base_input import BaseInput

@dataclass
class ArmTemplateInput(BaseInput):

template_path: Path

def get_defaults(self):
# TODO: Implement this
pass
return self.default_config

def get_schema(self) -> Dict[str, Any]:
# For ARM templates, the schema is defined by the parameters section
with open(self.template_path, "r", encoding="utf-8") as _file:
with open(self.artifact_path, "r", encoding="utf-8") as _file:
data = json.load(_file)
if "parameters" in data:
parameters: Dict[str, Any] = data["parameters"]
Expand Down
18 changes: 18 additions & 0 deletions src/aosm/azext_aosm/tests/latest/mock_vnf/input_with_filepath.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"publisher_name": "jamie-mobile-publisher",
"publisher_resource_group_name": "Jamie-publisher",
"nf_name": "ubuntu-vm",
"version": "1.0.0",
"acr_artifact_store_name": "ubuntu-acr",
"location": "eastus",
"blob_artifact_store_name": "ubuntu-blob-store",
"image_name_parameter": "imageName",
"arm_template": {
"file_path": "ubuntu-template.json",
"version": "1.0.0"
},
"vhd": {
"file_path": "livecd.ubuntu-cpc.azure.vhd",
"version": "1-0-0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"publisher_name": "jamie-mobile-publisher",
"publisher_resource_group_name": "Jamie-publisher",
"nf_name": "ubuntu-vm",
"version": "1.0.0",
"acr_artifact_store_name": "ubuntu-acr",
"location": "eastus",
"blob_artifact_store_name": "ubuntu-blob-store",
"image_name_parameter": "imageName",
"arm_template": {
"file_path": "ubuntu-template.json",
"version": "1.0.0"
},
"vhd": {
"blob_sas_url": "https://a/dummy/sas-url",
"version": "1-0-0",
"image_disk_size_GB": 30,
"image_hyper_v_generation": "V1",
"image_api_version": "2023-03-01"
}
}
12 changes: 12 additions & 0 deletions src/aosm/azext_aosm/tests/latest/test_arm_input_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from pathlib import Path
import unittest

from azext_aosm.template_parsers.arm_parser import ArmParser


class ArmParserTest(unittest.TestCase):
here = Path(__file__).parent
arm_parser = ArmParser(here / "mock_vnf/ubuntu-template.json")

assert arm_parser.get_defaults() == None
assert arm_parser.get_schema() == {'location': {'type': 'string', 'defaultValue': '[resourceGroup().location]'}, 'subnetName': {'type': 'string'}, 'ubuntuVmName': {'type': 'string', 'defaultValue': 'ubuntu-vm'}, 'virtualNetworkId': {'type': 'string'}, 'sshPublicKeyAdmin': {'type': 'string'}, 'imageName': {'type': 'string'}}
18 changes: 11 additions & 7 deletions src/aosm/azext_aosm/tests/latest/test_vnf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from azext_aosm.custom import build_definition, generate_definition_config

mock_vnf_folder = ((Path(__file__).parent) / "mock_vnf").resolve()
vnf_output_directory = ((Path(__file__).parent) / "vnf_output").resolve()

INPUT_WITH_SAS_VHD_PARAMS = {
"imageName": "ubuntu-vmImage",
Expand Down Expand Up @@ -42,26 +43,29 @@ def test_generate_config(self):
finally:
os.chdir(starting_directory)

def test_build(self):
"""Test building an NFDV for a VNF."""
def test_build_with_filepath(self):
"""Test building an NFDV for a VNF using a filepath."""
starting_directory = os.getcwd()
with TemporaryDirectory() as test_dir:
os.chdir(test_dir)
os.chdir(vnf_output_directory / "ubuntu_with_filepath")

try:
build_definition(
"vnf", str(mock_vnf_folder / "input_with_fp.json")
"vnf", str(mock_vnf_folder / "input_with_filepath.json") # TODO: add force to ensure we overwrite
)
assert os.path.exists("nfd-bicep-ubuntu-template")
finally:
os.chdir(starting_directory)

def test_build_with_sas_token(self):
"""Test building an NFDV for a VNF using a filepath."""
starting_directory = os.getcwd()
with TemporaryDirectory() as test_dir:
os.chdir(test_dir)
os.chdir(vnf_output_directory / "ubuntu_with_sas_token")

try:
build_definition(
"vnf", str(mock_vnf_folder / "input_with_sas.json")
"vnf", str(mock_vnf_folder / "input_with_sas_token.json")
)
assert os.path.exists("nfd-bicep-ubuntu-template")
print(os.listdir("nfd-bicep-ubuntu-template"))
Expand All @@ -81,7 +85,7 @@ def test_build_with_ordered_params(self):
try:
build_definition(
"vnf",
str(mock_vnf_folder / "input_with_fp.json"),
str(mock_vnf_folder / "input_with_filepath.json"),
order_params=True,
)
assert os.path.exists("nfd-bicep-ubuntu-template")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"location": "{deployParameters.location}",
"subnetName": "{deployParameters.subnetName}",
"ubuntuVmName": "{deployParameters.ubuntuVmName}",
"virtualNetworkId": "{deployParameters.virtualNetworkId}",
"sshPublicKeyAdmin": "{deployParameters.sshPublicKeyAdmin}",
"imageName": "ubuntu-vmImage"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"imageName": "ubuntu-vmImage"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://json-schema.org/draft-07/schema#",
"title": "DeployParametersSchema",
"type": "object",
"properties": {
"location": {
"type": "string"
},
"subnetName": {
"type": "string"
},
"ubuntuVmName": {
"type": "string"
},
"virtualNetworkId": {
"type": "string"
},
"sshPublicKeyAdmin": {
"type": "string"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# The following parameters are optional as they have default values.
# If you do not wish to expose them in the NFD, find and remove them from both
# deploymentParameters.json and templateParameters.json (and vhdParameters.json if
they are there)
# You can re-run the build command with the --order-params flag to order those
# files with the optional parameters at the end of the file, and with the
# --interactive flag to interactively choose y/n for each parameter to expose.

{
"location": {
"type": "string"
},
"ubuntuVmName": {
"type": "string"
}
}
Loading

0 comments on commit 2b6e17c

Please sign in to comment.