Skip to content

Commit

Permalink
feat: Incremental deployment system(s) update. Pulls together multipl…
Browse files Browse the repository at this point in the history
…e refreshed components. (#327)
  • Loading branch information
digimaun authored Aug 31, 2024
1 parent a1fa118 commit 2023be6
Show file tree
Hide file tree
Showing 25 changed files with 7,745 additions and 173 deletions.
11 changes: 7 additions & 4 deletions azext_edge/edge/commands_edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .providers.edge_api.orc import ORC_API_V1
from .providers.orchestration.common import (
KubernetesDistroType,
TrustSourceType,
# TODO MqMemoryProfile,
# TODO MqServiceType,
)
Expand Down Expand Up @@ -109,6 +110,7 @@ def init(
cmd,
cluster_name: str,
resource_group_name: str,
schema_registry_resource_id: str,
cluster_namespace: str = DEFAULT_NAMESPACE,
location: Optional[str] = None,
custom_location_name: Optional[str] = None,
Expand All @@ -123,9 +125,10 @@ def init(
dataflow_profile_instances: int = 1,
container_runtime_socket: Optional[str] = None,
kubernetes_distro: str = KubernetesDistroType.k8s.value,
trust_source: str = TrustSourceType.self_signed.value,
enable_fault_tolerance: Optional[bool] = None,
mi_user_assigned_identities: Optional[List[str]] = None,
no_progress: Optional[bool] = None,
no_block: Optional[bool] = None,
context_name: Optional[str] = None,
ensure_latest: Optional[bool] = None,
**kwargs,
Expand Down Expand Up @@ -163,7 +166,6 @@ def init(
work_manager = WorkManager(cmd)
return work_manager.execute_ops_init(
show_progress=not no_progress,
block=not no_block,
pre_flight=not no_pre_flight,
cluster_name=cluster_name,
resource_group_name=resource_group_name,
Expand All @@ -182,6 +184,9 @@ def init(
container_runtime_socket=container_runtime_socket,
kubernetes_distro=kubernetes_distro,
enable_fault_tolerance=enable_fault_tolerance,
trust_source=trust_source,
schema_registry_resource_id=schema_registry_resource_id,
mi_user_assigned_identities=mi_user_assigned_identities,
)

# TODO - @digimaun
Expand All @@ -196,8 +201,6 @@ def init(
# mq_listener_name=str(mq_listener_name),
# mq_authn_name=str(mq_authn_name),
# keyvault_resource_id=keyvault_resource_id,
# template_path=template_path,
# **kwargs,
# )


Expand Down
2 changes: 1 addition & 1 deletion azext_edge/edge/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ class BundleResourceKind(Enum):
PROTOBUF_SERVICE_API_PORT = 9800

# Dataflow constants
DEFAULT_DATAFLOW_PROFILE = "profile"
DEFAULT_DATAFLOW_PROFILE = "default"

# Init Env Control

Expand Down
45 changes: 32 additions & 13 deletions azext_edge/edge/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
KubernetesDistroType,
MqMemoryProfile,
MqServiceType,
TrustSourceType,
)


Expand Down Expand Up @@ -359,15 +360,16 @@ def load_iotops_arguments(self, _):
context.argument(
"location",
options_list=["--location"],
help="The ARM location that will be used for provisioned RPSaaS collateral. "
help="The region that will be used for provisioned resource collateral. "
"If not provided the connected cluster location will be used.",
)
context.argument(
"no_block",
options_list=["--no-block"],
arg_type=get_three_state_flag(),
help="Return immediately after the IoT Operations deployment has started.",
)
# TODO - @digimaun
# context.argument(
# "no_block",
# options_list=["--no-block"],
# arg_type=get_three_state_flag(),
# help="Return immediately after the IoT Operations deployment has started.",
# )
context.argument(
"disable_rsync_rules",
options_list=["--disable-rsync-rules"],
Expand All @@ -380,6 +382,12 @@ def load_iotops_arguments(self, _):
arg_type=get_three_state_flag(),
help="Ensure the latest IoT Ops CLI is being used, raising an error if an upgrade is available.",
)
# Schema Registry
context.argument(
"schema_registry_resource_id",
options_list=["--sr-resource-id"],
help="The schema registry resource Id to use with IoT Operations.",
)
# Akri
context.argument(
"container_runtime_socket",
Expand Down Expand Up @@ -514,12 +522,6 @@ def load_iotops_arguments(self, _):
# "--csi-config can be used one or more times.",
# arg_group="Key Vault CSI Driver",
# )
context.argument(
"template_path",
options_list=["--template-file"],
help="The path to a custom IoT Operations deployment template. Intended for advanced use cases.",
deprecate_info=context.deprecate(hide=True),
)
context.argument(
"dataflow_profile_instances",
type=int,
Expand All @@ -530,8 +532,25 @@ def load_iotops_arguments(self, _):
context.argument(
"enable_fault_tolerance",
arg_type=get_three_state_flag(),
options_list=["--enable-fault-tolerance"],
help="Enable fault tolerance for edge storage accelerator. At least 3 cluster nodes are required.",
)
context.argument(
"mi_user_assigned_identities",
nargs="*",
action="extend",
options_list=["--mi-user-assigned"],
help="Space-separated resource Ids for the desired user managed identities to associate with the instance. "
"Can be used one or more times.",
arg_group="Identity",
)
context.argument(
"trust_source",
arg_type=get_enum_type(TrustSourceType),
options_list=["--trust-source"],
help="Indicates whether a built-in self-signed or user managed trust bundle config should be used.",
arg_group="Trust",
)

with self.argument_context("iot ops delete") as context:
context.argument(
Expand Down
12 changes: 9 additions & 3 deletions azext_edge/edge/providers/orchestration/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,22 @@ class MqServiceType(Enum):


class KubernetesDistroType(Enum):
k3s = "k3s"
k8s = "k8s"
microk8s = "microk8s"
k3s = "K3s"
k8s = "K8s"
microk8s = "MicroK8s"


class TrustSourceType(Enum):
self_signed = "SelfSigned"
customer_managed = "CustomerManaged"


__all__ = [
"MqMode",
"MqMemoryProfile",
"MqServiceType",
"KubernetesDistroType",
"TrustSourceType",
"DEFAULT_X509_CA_VALID_DAYS",
"KEYVAULT_DATAPLANE_API_VERSION",
"KEYVAULT_CLOUD_API_VERSION",
Expand Down
15 changes: 14 additions & 1 deletion azext_edge/edge/providers/orchestration/connected_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Licensed under the MIT License. See License file in the project root for license information.
# ----------------------------------------------------------------------------------------------

from typing import List, Optional, Union
from typing import List, Optional, Union, Dict

from ...util.resource_graph import ResourceGraph

Expand All @@ -24,6 +24,9 @@
| where properties.ExtensionType startswith 'microsoft.iotoperations'
or properties.ExtensionType =~ 'microsoft.deviceregistry.assets'
or properties.ExtensionType =~ 'microsoft.azurekeyvaultsecretsprovider'
or properties.ExtensionType =~ 'microsoft.secretsynccontroller'
or properties.ExtensionType =~ 'microsoft.openservicemesh'
or properties.ExtensionType =~ 'microsoft.edgestorageaccelerator'
| project id, name, apiVersion
""",
"get_aio_custom_locations": """
Expand Down Expand Up @@ -95,6 +98,16 @@ def extensions(self) -> List[dict]:
self.clusters.extensions.list(resource_group_name=self.resource_group_name, cluster_name=self.cluster_name)
)

def get_extensions_by_type(self, *type_names: str) -> Optional[Dict[str, dict]]:
extensions = self.extensions
desired_extension_map = {name.lower(): None for name in type_names}
for extension in extensions:
extension_type = extension["properties"].get("extensionType", "").lower()
if extension_type in desired_extension_map:
desired_extension_map[extension_type] = extension

return desired_extension_map

def get_custom_location_for_namespace(self, namespace: str) -> Optional[dict]:
query = QUERIES["get_custom_location_for_namespace"].format(resource_id=self.resource_id, namespace=namespace)

Expand Down
27 changes: 14 additions & 13 deletions azext_edge/edge/providers/orchestration/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,23 @@
["*", "*/write", "microsoft.authorization/roleassignments/write", "microsoft.authorization/*/write"]
)

ROLE_DEF_FORMAT_STR = "/subscriptions/{subscription_id}/providers/Microsoft.Authorization/roleDefinitions/{role_id}"


# TODO: one-off for time, make generic
def verify_write_permission_against_rg(subscription_id: str, resource_group_name: str):
for permission in get_principal_permissions_for_group(
subscription_id=subscription_id, resource_group_name=resource_group_name
):
permission_dict = permission.as_dict()
action_result = False
negate_action_result = False

for action in permission_dict.get("actions", []):
for action in permission.get("actions", []):
if action.lower() in VALID_PERM_FORMS:
action_result = True
break

for not_action in permission_dict.get("not_actions", []):
for not_action in permission.get("notActions", []):
if not_action.lower() in VALID_PERM_FORMS:
negate_action_result = True
break
Expand All @@ -50,7 +51,7 @@ def verify_write_permission_against_rg(subscription_id: str, resource_group_name
)


def get_principal_permissions_for_group(subscription_id: str, resource_group_name: str) -> Iterable:
def get_principal_permissions_for_group(subscription_id: str, resource_group_name: str) -> Iterable[dict]:
authz_client = get_authz_client(subscription_id=subscription_id)
return authz_client.permissions.list_for_resource_group(resource_group_name)

Expand All @@ -72,16 +73,17 @@ def apply_role_assignment(self, scope: str, principal_id: str, role_def_id: str)
scope=scope, filter=f"principalId eq '{principal_id}'"
)
for role_assignment in role_assignments_iter:
role_assignment_dict = role_assignment.as_dict()
if role_assignment_dict["role_definition_id"] == role_def_id:
if role_assignment["properties"]["roleDefinitionId"] == role_def_id:
return

return self.authz_client.role_assignments.create(
scope=scope,
role_assignment_name=str(uuid4()),
parameters={
"role_definition_id": role_def_id,
"principal_id": principal_id,
"properties": {
"roleDefinitionId": role_def_id,
"principalId": principal_id,
}
},
)

Expand All @@ -102,8 +104,7 @@ def can_apply_role_assignment(
)
action_allowed = None
for permission in permissions:
permission_dict = permission.as_dict()
action_result = self._calculate_action(permission_dict=permission_dict, valid_permissions=VALID_PERM_FORMS)
action_result = self._calculate_action(permission=permission, valid_permissions=VALID_PERM_FORMS)

if action_result == PermissionState.ActionAllowed and action_allowed is not False:
action_allowed = True
Expand All @@ -128,16 +129,16 @@ def _get_principal_permissions_for_resource(
resource_name=resource_name,
)

def _calculate_action(self, permission_dict: dict, valid_permissions: frozenset) -> PermissionState:
def _calculate_action(self, permission: dict, valid_permissions: frozenset) -> PermissionState:
action_result = False
negate_action_result = False

for action in permission_dict.get("actions", []):
for action in permission.get("actions", []):
if action.lower() in valid_permissions:
action_result = True
break

for not_action in permission_dict.get("not_actions", []):
for not_action in permission.get("notActions", []):
if not_action.lower() in valid_permissions:
negate_action_result = True
break
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@
from rich.console import Console

from ....util.az_client import (
get_authz_client,
get_registry_mgmt_client,
get_storage_mgmt_client,
parse_resource_id,
wait_for_terminal_state,
)
from ....util.common import should_continue_prompt
from ....util.queryable import Queryable
from ..permissions import PermissionManager
from ..permissions import PermissionManager, ROLE_DEF_FORMAT_STR

logger = get_logger(__name__)

Expand All @@ -33,7 +32,6 @@
)

STORAGE_BLOB_DATA_CONTRIBUTOR_ROLE_ID = "ba92f5b4-2d11-453d-a403-e96b0029c9fe"
ROLE_DEF_FORMAT_STR = "/subscriptions/{subscription_id}/providers/Microsoft.Authorization/roleDefinitions/{role_id}"


def get_user_msg_warn_ra(prefix: str, principal_id: str, scope: str):
Expand All @@ -52,9 +50,6 @@ def __init__(self, cmd):
self.registry_mgmt_client = get_registry_mgmt_client(
subscription_id=self.default_subscription_id,
)
self.authz_client = get_authz_client(
subscription_id=self.default_subscription_id,
)
self.ops: "SchemaRegistriesOperations" = self.registry_mgmt_client.schema_registries

def create(
Expand Down
Loading

0 comments on commit 2023be6

Please sign in to comment.