示例#1
0
    def get_actions_for_missing_state(self) -> Sequence[DAction]:
        config = self.info.config

        actions: MutableSequence[DAction] = [
            DAction(name=f"create-project",
                    description=
                    f"Create GCP project '{self.info.config['project_id']}'")
        ]

        if 'billing_account_id' in config:
            desired_billing_account = config['billing_account_id']
            actions.append(
                DAction(name='set-billing-account',
                        description=
                        f"Set billing account to '{desired_billing_account}'"))

        if 'apis' in config:
            apis = config['apis']
            if 'disabled' in apis:
                for api_name in apis['disabled']:
                    actions.append(
                        DAction(name=f"disable-api-{api_name}",
                                description=f"Disable API '{api_name}'",
                                args=['disable_api', api_name]))
            if 'enabled' in apis:
                for api_name in apis['enabled']:
                    actions.append(
                        DAction(name=f"enable-api-{api_name}",
                                description=f"Enable API '{api_name}'",
                                args=['enable_api', api_name]))
        return actions
示例#2
0
    def get_actions_for_discovered_state(self,
                                         state: dict) -> Sequence[DAction]:
        actions: MutableSequence[DAction] = []
        config = self.info.config

        # update project organization if requested to (and if necessary)
        if 'organization_id' in config:
            actual_parent: dict = state['parent'] if 'parent' in state else {}
            actual_parent_id: int = int(
                actual_parent['id']) if 'id' in actual_parent else None
            desired_parent_id: int = config['organization_id']
            if desired_parent_id != actual_parent_id:
                actions.append(
                    DAction(
                        name='set-parent',
                        description=f"Set organization to '{desired_parent_id}'"
                    ))

        # update project billing account if requested to (and if necessary)
        if 'billing_account_id' in config and config[
                'billing_account_id'] != state['billing_account_id']:
            actions.append(
                DAction(
                    name='set-billing-account',
                    description=
                    f"Set billing account to '{config['billing_account_id']}'")
            )

        # enable/disable project APIs if requested to (and if necessary)
        if 'apis' in config:
            apis = config['apis']

            # fetch currently enabled project APIs
            actual_enabled_api_names: Sequence[str] = sorted(
                state['apis']['enabled'])
            if 'disabled' in apis:
                # disable APIs that are currently enabled, but user requested them to be disabled
                for api_name in [
                        api_name for api_name in apis['disabled']
                        if api_name in actual_enabled_api_names
                ]:
                    actions.append(
                        DAction(name=f"disable-api-{api_name}",
                                description=f"Disable API '{api_name}'",
                                args=['disable_api', api_name]))

            if 'enabled' in apis:
                # enable APIs that are currently not enabled, but user requested them to be enabled
                for api_name in [
                        api_name for api_name in apis['enabled']
                        if api_name not in actual_enabled_api_names
                ]:
                    actions.append(
                        DAction(name=f"enable-api-{api_name}",
                                description=f"Enable API '{api_name}'",
                                args=['enable_api', api_name]))

        return actions
示例#3
0
 def get_actions_for_discovered_state(self, state: dict) -> Sequence[DAction]:
     actions: MutableSequence[DAction] = []
     if 'display_name' in self.info.config and self.info.config['display_name'] != state['displayName']:
         sa_email = self.info.config["email"]
         actions.append(DAction(name=f"update-display-name",
                                description=f"Update display name of service account '{sa_email}'",
                                args=["update_display_name", state['etag']]))
     return actions
示例#4
0
 def get_actions_for_missing_state(self) -> Sequence[DAction]:
     return [
         DAction(name=a['name'],
                 description=a['description'],
                 image=a['image'],
                 entrypoint=a['entrypoint'],
                 args=a['args']) for a in missing_state_actions or []
     ]
示例#5
0
 def get_actions_for_missing_state(self) -> Sequence[DAction]:
     manifest = self.info.config['manifest']
     metadata = manifest['metadata']
     return [
         DAction(name='create',
                 description=
                 f"Create {manifest['kind'].lower()} '{metadata['name']}'")
     ]
示例#6
0
 def get_actions_for_discovered_state(self,
                                      state: dict) -> Sequence[DAction]:
     return [
         DAction(name=a['name'],
                 description=a['description'],
                 image=a['image'],
                 entrypoint=a['entrypoint'],
                 args=a['args']) for a in existing_state_actions or []
     ]
示例#7
0
    def get_actions_for_missing_state(self) -> Sequence[DAction]:
        actions: MutableSequence[DAction] = []
        cfg: dict = self.info.config

        enabled_apis = self.svc.find_gcp_project_enabled_apis(project_id=cfg['project_id'])
        if enabled_apis is None \
                or 'sqladmin.googleapis.com' not in enabled_apis \
                or 'sql-component.googleapis.com' not in enabled_apis:
            # if the SQL Admin API is not enabled, there can be no SQL instances; we will, however, have to enable
            # that API for the project later on.
            actions.append(DAction(name='enable-sql-apis',
                                   description=f"Enable Cloud SQL APIs for project '{cfg['project_id']}'"))

        actions.append(DAction(name=f"create-sql-instance", description=f"Create SQL instance '{cfg['name']}'"))

        # no need to evaluate scripts, since instance does not yet exist; the create action will do it once it creates
        # the instance successfully

        return actions
示例#8
0
    def get_actions_for_discovered_state(self, state: dict) -> Sequence[DAction]:
        actions: MutableSequence[DAction] = []

        differences: list = collect_differences(desired=self.info.config['manifest'], actual=state)
        if 'apiVersion' in differences:
            differences.remove('apiVersion')
        if 'kind' in differences:
            differences.remove('kind')
        if differences:
            if self.info.verbose:
                print("Found state differences: ", file=sys.stderr)
                pprint(differences, stream=sys.stderr)
            kind: str = self.info.config['manifest']['kind']
            name: str = self.info.config['manifest']['metadata']['name']
            actions.append(DAction(name='update', description=f"Update {kind.lower()} '{name}'", args=['update']))

        return actions
示例#9
0
    def get_actions_for_discovered_state(self,
                                         state: dict) -> Sequence[DAction]:
        if 'bindings' not in state:
            raise Exception(
                f"illegal state: IAM policy could not be fetched! (permissions problem, or missing project?)"
            )

        update_needed: bool = False
        final_bindings: List[dict] = deepcopy(state['bindings'])
        for desired_binding in self.info.config['bindings']:
            desired_role: str = desired_binding['role']
            desired_members: Sequence[str] = desired_binding['members']
            role_found: bool = False
            for actual_binding in final_bindings:
                if desired_role == actual_binding['role']:
                    role_found = True
                    actual_members: List[str] = actual_binding['members']
                    missing_members = [
                        member for member in desired_members
                        if member not in actual_members
                    ]
                    if len(missing_members) > 0:
                        print(
                            f"Subjects {missing_members} missing from role '{desired_role}'",
                            file=sys.stderr)
                        update_needed = True

            if not role_found:
                print(f"No policy for role '{desired_role}' was found",
                      file=sys.stderr)
                update_needed = True

        if update_needed:
            return [
                DAction(name=f"update-policy",
                        description=f"Update IAM policy",
                        args=["update_policy", state['etag']])
            ]
        else:
            return []
示例#10
0
 def get_actions_for_missing_state(self) -> Sequence[DAction]:
     sa_email = self.info.config["email"]
     return [DAction(name=f"create-service-account", description=f"Create service account '{sa_email}'")]
 def get_actions_for_missing_state(self) -> Sequence[DAction]:
     type = "global" if 'region' not in self.info.config else "regional"
     return [DAction(name=f"create", description=f"Create {type} IP address '{self.info.config['name']}'")]
示例#12
0
    def get_actions_for_discovered_state(self, state: dict) -> Sequence[DAction]:
        actions: MutableSequence[DAction] = []
        actual = state
        actual_settings = actual['settings']
        cfg = self.info.config

        # validate instance is RUNNING
        if actual['state'] != "RUNNABLE":
            raise Exception(f"illegal state: instance exists, but not running ('{actual['state']}')")

        # validate instance region
        zone = cfg['zone']
        region = region_from_zone(zone)
        if actual['region'] != region:
            raise Exception(
                f"illegal config: SQL instance is in region '{actual['region']}' instead of '{region}'. "
                f"Unfortunately, changing SQL instances regions is not allowed in Google Cloud SQL.")

        # validate instance preferred zone
        if actual_settings['locationPreference']['zone'] != zone:
            actions.append(DAction(name='update-zone',
                                   description=f"Update SQL instance preferred zone to '{zone}'"))

        # validate instance machine type
        machine_type = cfg['machine-type']
        if actual_settings['tier'] != machine_type:
            actions.append(DAction(name='update-machine-type',
                                   description=f"Update SQL instance machine type to '{machine_type}'"))

        # validate backup configuration
        if 'backup' in cfg:
            desired_backup: dict = cfg['backup']
            if desired_backup['enabled']:
                # Verify that actual backup configuration IS enabled:

                if 'backupConfiguration' not in actual_settings:
                    actions.append(DAction(name='update-backup', description=f"Enable SQL instance backups"))
                else:
                    actual_backup = actual_settings['backupConfiguration']
                    if not actual_backup['enabled'] or not actual_backup['binaryLogEnabled']:
                        actions.append(DAction(name='update-backup',
                                               description=f"Enable SQL instance backup/binary-logging"))
                    elif 'time' in desired_backup:
                        desired_time = desired_backup['time']
                        if desired_time != actual_backup['startTime']:
                            actions.append(
                                DAction(name='update-backup',
                                        description=f"Update SQL instance backup schedule to '{desired_time}'"))
            elif 'time' in desired_backup:
                raise Exception(f"illegal config: cannot specify backup time when backup is disabled")
            elif 'backupConfiguration' in actual_settings:
                # Verify that actual backup configuration IS NOT enabled:
                actual_backup = actual_settings['backupConfiguration']
                if actual_backup['enabled'] or actual_backup['binaryLogEnabled']:
                    actions.append(DAction(name='update-backup',
                                           description=f"Disable SQL instance backups/binary-logging"))

        # validate data-disk size
        if "data-disk-size-gb" in cfg:
            desired_data_disk_size_gb: int = cfg["data-disk-size-gb"]
            actual_disk_size: int = int(actual_settings['dataDiskSizeGb'])
            if actual_disk_size != desired_data_disk_size_gb:
                if desired_data_disk_size_gb < actual_disk_size:
                    raise Exception(
                        f"illegal config: cannot reduce disk size from {actual_disk_size}gb to "
                        f"{desired_data_disk_size_gb}gb (not allowed by Cloud SQL APIs).")
                else:
                    actions.append(
                        DAction(name='update-data-disk-size',
                                description=f"Update SQL instance data disk size from {actual_disk_size}gb to "
                                            f"{desired_data_disk_size_gb}gb"))

        # validate data-disk type
        if "data-disk-type" in cfg:
            desired_data_disk_type: str = cfg["data-disk-type"]
            if actual_settings['dataDiskType'] != desired_data_disk_type:
                actions.append(DAction(name='update-data-disk-type',
                                       description=f"Update SQL instance data disk type to '{desired_data_disk_type}'"))

        # validate MySQL flags
        if 'flags' in cfg:
            desired_flags: dict = cfg['flags']
            actual_flags = actual_settings['databaseFlags'] if 'databaseFlags' in actual_settings else []
            if actual_flags != desired_flags:
                actions.append(DAction(name='update-flags', description=f"Update SQL instance flags"))

        # validate SSL connections requirement
        if "require-ssl" in cfg:
            desired_require_ssl: bool = cfg["require-ssl"]
            if actual_settings['ipConfiguration']['requireSsl'] != desired_require_ssl:
                actions.append(
                    DAction(name='update-require-ssl',
                            description=f"Update SQL instance to {'' if desired_require_ssl else 'not '}require "
                                        f"SSL connections"))

        # validate authorized networks
        if "authorized-networks" in cfg:
            desired_authorized_networks: list = cfg["authorized-networks"]

            # validate network names are unique
            names = set()
            for desired_network in desired_authorized_networks:
                if desired_network['name'] in names:
                    raise Exception(f"illegal config: network '{desired_network['name']}' defined more than once")
                else:
                    names.add(desired_network['name'])

            actual_auth_networks = actual_settings['ipConfiguration']['authorizedNetworks']
            if len(actual_auth_networks) != len(desired_authorized_networks):
                actions.append(
                    DAction(name='update-authorized-networks', description=f"Update SQL instance authorized networks"))
            else:
                # validate each network
                for desired_network in desired_authorized_networks:
                    try:
                        actual_network = next(n for n in actual_auth_networks if n['name'] == desired_network['name'])
                        desired_expiry = desired_network['expirationTime'] \
                            if 'expirationTime' in desired_network else None
                        actual_expiry = actual_network['expirationTime'] if 'expirationTime' in actual_network else None
                        if desired_network['value'] != actual_network['value'] or desired_expiry != actual_expiry:
                            actions.append(DAction(name='update-authorized-networks',
                                                   description=f"Update SQL instance authorized networks "
                                                               f"(found stale network: {desired_network['name']})"))
                            break
                    except StopIteration:
                        actions.append(DAction(name='update-authorized-networks',
                                               description=f"Update SQL instance authorized networks"))
                        break

        # validate maintenance window
        if "maintenance" in cfg:
            desired_maintenance: dict = cfg["maintenance"]
            actual_maintenance_window = actual_settings['maintenanceWindow']
            if desired_maintenance is None:
                actions.append(DAction(name='update-maintenance-window',
                                       description=f"Disable SQL instance maintenance window"))
            else:
                desired_day = desired_maintenance['day']
                desired_hour: int = desired_maintenance['hour']
                if desired_day != actual_maintenance_window['day'] or desired_hour != actual_maintenance_window['hour']:
                    actions.append(DAction(name='update-maintenance-window',
                                           description=f"Update SQL instance maintenance window"))

        # validate storage auto-resize
        if "storage-auto-resize" in cfg:
            desired_storage_auto_resize: dict = cfg["storage-auto-resize"]
            if not desired_storage_auto_resize['enabled'] and 'limit' in desired_storage_auto_resize:
                raise Exception(f"illegal config: cannot specify storage auto-resize limit when it's disabled")
            elif desired_storage_auto_resize['enabled'] != actual_settings['storageAutoResize']:
                raise Exception(f"illegal config: currently it's impossible to switch storage auto-resize "
                                f"(Google APIs seem to reject this change)")
            elif 'limit' in desired_storage_auto_resize \
                    and desired_storage_auto_resize['limit'] != int(actual_settings['storageAutoResizeLimit']):
                actions.append(DAction(name='update-storage-auto-resize',
                                       description=f"Update SQL instance storage auto-resizing"))

        # validate labels
        if "labels" in cfg:
            desired_labels: dict = cfg["labels"]
            actual_labels: dict = actual_settings['userLabels'] if 'userLabels' in actual_settings else {}
            if len(desired_labels.keys()) != len(actual_labels.keys()):
                actions.append(DAction(name='update-labels', description=f"Update SQL instance user-labels"))
            else:
                for key, value in desired_labels.items():
                    if key not in actual_labels or value != actual_labels[key]:
                        actions.append(DAction(name='update-labels', description=f"Update SQL instance user-labels"))
                        break

        # create missing users
        if 'users' in cfg:
            actual_users: list = actual['users']
            for user in cfg['users']:
                found: bool = False
                for actual_user in actual_users:
                    if user['name'] == actual_user['name']:
                        found: bool = True
                        break
                if not found:
                    actions.append(DAction(name='add-user',
                                           description=f"Create new user '{user['name']}'",
                                           args=["add_user", user['name']]))

        # check for scripts that need to be executed
        if "scripts" in cfg:
            evaluator: ScriptEvaluator = ScriptEvaluator(svc=self.svc,
                                                         project_id=cfg['project_id'],
                                                         instance_name=cfg['name'],
                                                         root_password=cfg['root-password'],
                                                         zone=zone,
                                                         scripts_data=self.info.config['scripts'],
                                                         context=cfg['scripts_ctx'] if 'scripts_ctx' in cfg else {})
            with evaluator as evaluator:
                for script in evaluator.get_scripts_to_execute():
                    actions.append(
                        DAction(name='execute-script',
                                description=f"Execute '{script.name}' SQL scripts",
                                args=['execute_scripts', script.name]))

        return actions
示例#13
0
    def get_actions_for_discovered_state(self, state: dict) -> Sequence[DAction]:
        cluster_name = self.info.config['name']
        actions: MutableSequence[DAction] = []
        actual_cluster = state

        # validate cluster is RUNNING
        if actual_cluster['status'] != "RUNNING":
            raise Exception(f"Cluster exists, but not running ('{actual_cluster['status']}')")

        # validate cluster primary zone & locations
        desired_cluster_zone = self.info.config['zone']
        actual_cluster_locations: Sequence[str] = actual_cluster['locations']
        if [desired_cluster_zone] != actual_cluster_locations:
            raise Exception(
                f"Cluster locations are {actual_cluster_locations} instead of {[desired_cluster_zone]}. "
                f"Updating this is not allowed in GKE APIs unfortunately.")

        # validate cluster master version & cluster node pools version
        actual_cluster_master_version: str = actual_cluster["currentMasterVersion"]
        actual_cluster_node_version: str = actual_cluster["currentNodeVersion"]
        desired_version = self.info.config['version']
        if desired_version != actual_cluster_master_version:
            actions.append(
                DAction(name='update-cluster-master-version',
                        description=f"Update master version for cluster '{cluster_name}'"))
        if desired_version != actual_cluster_node_version:
            raise Exception(
                f"Cluster node version is '{actual_cluster_node_version}' instead of '{desired_version}'. "
                f"Updating this is not allowed in GKE APIs unfortunately.")

        # ensure master authorized networks is disabled
        if 'masterAuthorizedNetworksConfig' in actual_cluster:
            if 'enabled' in actual_cluster['masterAuthorizedNetworksConfig']:
                if actual_cluster['masterAuthorizedNetworksConfig']['enabled']:
                    actions.append(
                        DAction(name='disable-master-authorized-networks',
                                description=f"Disable master authorized networks for cluster '{cluster_name}'"))

        # ensure that legacy ABAC is disabled
        if 'legacyAbac' in actual_cluster:
            if 'enabled' in actual_cluster['legacyAbac']:
                if actual_cluster['legacyAbac']['enabled']:
                    actions.append(
                        DAction(name='disable-legacy-abac',
                                description=f"Disable legacy ABAC for cluster '{cluster_name}'"))

        # ensure monitoring service is set to GKE's monitoring service
        actual_monitoring_service = \
            actual_cluster["monitoringService"] if "monitoringService" in actual_cluster else None
        if actual_monitoring_service != "monitoring.googleapis.com":
            actions.append(
                DAction(name='enable-monitoring-service',
                        description=f"Enable GCP monitoring for cluster '{cluster_name}'"))

        # ensure logging service is set to GKE's logging service
        actual_logging_service = \
            actual_cluster["loggingService"] if "loggingService" in actual_cluster else None
        if actual_logging_service != "logging.googleapis.com":
            actions.append(
                DAction(name='enable-logging-service',
                        description=f"Enable GCP logging for cluster '{cluster_name}'"))

        # infer actual addons status
        actual_addons: dict = actual_cluster["addonsConfig"] if "addonsConfig" in actual_cluster else {}

        # ensure HTTP load-balancing addon is ENABLED
        http_lb_addon: dict = actual_addons["httpLoadBalancing"] if "httpLoadBalancing" in actual_addons else {}
        if 'disabled' in http_lb_addon and http_lb_addon["disabled"]:
            actions.append(
                DAction(name='enable-http-load-balancer-addon',
                        description=f"Enable HTTP load-balancing addon for cluster '{cluster_name}'",
                        args=['set_addon_status', 'httpLoadBalancing', 'enabled']))

        # ensure Kubernetes Dashboard addon is DISABLED
        k8s_dashboard_addon = actual_addons["kubernetesDashboard"] if "kubernetesDashboard" in actual_addons else {}
        if "disabled" in k8s_dashboard_addon and not k8s_dashboard_addon["disabled"]:
            actions.append(
                DAction(name='disable-k8s-dashboard-addon',
                        description=f"Disable legacy Kubernetes Dashboard addon for cluster '{cluster_name}'",
                        args=['set_addon_status', 'kubernetesDashboard', 'disabled']))

        # ensure Horizontal Pod Auto-scaling addon is ENABLED
        horiz_pod_auto_scaling_addon = \
            actual_addons["horizontalPodAutoscaling"] if "horizontalPodAutoscaling" in actual_addons else {}
        if "disabled" in horiz_pod_auto_scaling_addon and horiz_pod_auto_scaling_addon["disabled"]:
            actions.append(
                DAction(name='enable-k8s-horiz-pod-auto-scaling-addon',
                        description=f"Enable horizontal Pod auto-scaling addon for cluster '{cluster_name}'",
                        args=['set_addon_status', 'horizontalPodAutoscaling', 'enabled']))

        # ensure alpha features are DISABLED
        if 'enableKubernetesAlpha' in actual_cluster and actual_cluster["enableKubernetesAlpha"]:
            raise Exception(f"Cluster alpha features are enabled instead of disabled. "
                            f"Updating this is not allowed in GKE APIs unfortunately.")

        # validate node pools state
        desired_node_pools: Sequence[dict] = self.info.config['node_pools']
        for pool in desired_node_pools:
            pool_name = pool['name']
            actual_pool = self.svc.get_gke_cluster_node_pool(project_id=self.info.config['project_id'],
                                                             zone=desired_cluster_zone,
                                                             name=self.info.config['name'],
                                                             pool_name=pool_name)
            if actual_pool is None:
                actions.append(DAction(name='create-node-pool',
                                       description=f"Create node pool '{pool_name}' in cluster '{cluster_name}'",
                                       args=['create_node_pool', pool_name]))
                continue

            # ensure the node pool is RUNNING
            if actual_pool['status'] != "RUNNING":
                raise Exception(f"Node pool '{pool_name}' exists, but not running ('{actual_pool['status']}')")

            # validate node pool version
            if desired_version != actual_pool["version"]:
                actions.append(
                    DAction(name='update-node-pool-version',
                            description=f"Update version of node pool '{pool_name}' in cluster '{cluster_name}'",
                            args=['update_node_pool_version', pool_name]))

            # infer node pool management features
            management: dict = actual_pool["management"] if "management" in actual_pool else {}

            # ensure auto-repair is ENABLED
            if "autoRepair" not in management or not management["autoRepair"]:
                actions.append(DAction(
                    name='enable-node-pool-autorepair',
                    description=f"Enable auto-repair for node pool '{pool_name}' in cluster '{cluster_name}'",
                    args=['enable_node_pool_autorepair', pool_name]))

            # ensure auto-upgrade is DISABLED
            if "autoUpgrade" in management and management["autoUpgrade"]:
                actions.append(DAction(
                    name='disable-node-pool-autoupgrade',
                    description=f"Disable auto-upgrade for node pool '{pool_name}' in cluster '{cluster_name}'",
                    args=['disable_node_pool_autoupgrade', pool_name]))

            # validate auto-scaling
            desired_pool_min_size: int = pool['min_size'] if 'min_size' in pool else 1
            desired_pool_max_size: int = pool['max_size'] if 'max_size' in pool else desired_pool_min_size
            actual_autoscaling: dict = actual_pool["autoscaling"] if "autoscaling" in actual_pool else {}
            actual_autoscaling_enabled: bool = "enabled" in actual_autoscaling and actual_autoscaling["enabled"]
            actual_autoscaling_min_size: int = actual_autoscaling["minNodeCount"] \
                if "minNodeCount" in actual_autoscaling else None
            actual_autoscaling_max_size: int = actual_autoscaling["maxNodeCount"] \
                if "maxNodeCount" in actual_autoscaling else None
            if not actual_autoscaling_enabled \
                    or actual_autoscaling_min_size != desired_pool_min_size \
                    or actual_autoscaling_max_size != desired_pool_max_size:
                actions.append(
                    DAction(
                        name='configure-node-pool-autoscaling',
                        description=f"Configure auto-scaling of node pool '{pool_name}' in cluster '{cluster_name}'",
                        args=['configure_node_pool_autoscaling',
                              pool_name,
                              str(desired_pool_min_size),
                              str(desired_pool_max_size)]))

            # infer node VM configuration
            pool_cfg: dict = actual_pool['config'] if 'config' in actual_pool else {}

            # validate node pool service account
            if 'service_account' in pool:
                desired_service_account: str = pool['service_account']
                actual_service_account: str = pool_cfg[
                    'serviceAccount'] if 'serviceAccount' in pool_cfg else 'default'
                if desired_service_account != actual_service_account:
                    raise Exception(
                        f"Node pool '{pool_name}' service account is '{actual_service_account}' instead of "
                        f"'{desired_service_account}' (updating the service account is not allowed in GKE APIs)")

            # validate node pool OAuth scopes
            if 'oauth_scopes' in pool:
                desired_oauth_scopes: Sequence[str] = pool['oauth_scopes']
                actual_oauth_scopes: Sequence[str] = pool_cfg["oauthScopes"] if 'oauthScopes' in pool_cfg else []
                if desired_oauth_scopes != actual_oauth_scopes:
                    raise Exception(
                        f"Node pool '{pool_name}' OAuth scopes are {actual_oauth_scopes} instead of "
                        f"{desired_oauth_scopes} (updating OAuth scopes is not allowed in GKE APIs unfortunately)")

            # validate node pool preemptible usage
            if 'preemptible' in pool:
                desired_preemptible: bool = pool['preemptible']
                actual_preemptible: bool = pool_cfg['preemptible'] if 'preemptible' in pool_cfg else False
                if desired_preemptible != actual_preemptible:
                    raise Exception(f"GKE node pools APIs do not allow enabling/disabling preemptibles usage mode "
                                    f"(required for node pool '{pool_name}' in cluster '{cluster_name}')")

            # validate machine type
            if 'machine_type' in pool:
                desired_machine_type: str = pool['machine_type']
                actual_machine_type: str = pool_cfg["machineType"] if "machineType" in pool_cfg else 'n1-standard-1'
                if desired_machine_type != actual_machine_type:
                    raise Exception(
                        f"Node pool '{pool_name}' uses '{actual_machine_type}' instead of '{desired_machine_type}'. "
                        f"Updating this is not allowed in GKE APIs unfortunately.")

            # validate machine disk type
            if 'disk_size_gb' in pool:
                desired_disk_size_gb: int = pool['disk_size_gb']
                actual_disk_size_gb: int = pool_cfg["diskSizeGb"] if "diskSizeGb" in pool_cfg else 100
                if desired_disk_size_gb != actual_disk_size_gb:
                    raise Exception(
                        f"Node pool '{pool_name}' allocates {actual_disk_size_gb}GB disk space instead of "
                        f"{desired_disk_size_gb}GB. Updating this is not allowed in GKE APIs unfortunately.")

            # validate network tags
            if 'tags' in pool:
                desired_tags: Sequence[str] = pool['tags']
                actual_tags: Sequence[str] = pool_cfg["tags"] if "tags" in pool_cfg else []
                if desired_tags != actual_tags:
                    raise Exception(
                        f"Node pool '{pool_name}' network tags are '{actual_tags}' instead of '{desired_tags}'. "
                        f"Updating this is not allowed in GKE APIs unfortunately.")

            # validate GCE metadata
            if 'metadata' in pool:
                desired_metadata: Mapping[str, str] = pool['metadata']
                actual_metadata: Mapping[str, str] = pool_cfg["metadata"] if "metadata" in pool_cfg else {}
                if desired_metadata != actual_metadata:
                    raise Exception(
                        f"Node pool '{pool_name}' GCE metadata is '{actual_metadata}' instead of '{desired_metadata}'. "
                        f"Updating this is not allowed in GKE APIs unfortunately.")

            # validate Kubernetes labels
            if 'labels' in pool:
                desired_labels: Mapping[str, str] = pool['labels']
                actual_labels: Mapping[str, str] = pool_cfg["labels"] if "labels" in pool_cfg else {}
                if desired_labels != actual_labels:
                    raise Exception(
                        f"Node pool '{pool_name}' Kubernetes labels are '{actual_labels}' instead of "
                        f"'{desired_labels}'. Updating this is not allowed in GKE APIs unfortunately.")

        if not actions:
            # if no actions returned, we are VALID - create authentication for dependant resources
            self.authenticate(properties=state)

        return actions
示例#14
0
 def get_actions_for_missing_state(self) -> Sequence[DAction]:
     return [DAction(name=f"create-cluster", description=f"Create cluster '{self.info.config['name']}'")]