code: def _validate_config(config):
      """Validate the StreamAlert configuration contains a valid structure.
  
      Checks for `logs.json`:
          - each log has a schema and parser declared
      Checks for `cluster.json` data_sources:
          - the sources contains either kinesis or s3 keys
          - each sources has a list of logs declared
  
      Args:
          config (dict): The loaded configuration dictionary
  
      Raises:
          ConfigError: Raised if any config validation errors occur
      """
      # Check the log declarations
      if TopLevelConfigKeys.LOGS in config:
          for log, attrs in config[TopLevelConfigKeys.LOGS].items():
              if "schema" not in attrs:
                  raise ConfigError(
                      "The 'schema' is missing for {}".format(log)
                  )
  
              if "parser" not in attrs:
                  raise ConfigError(
                      "The 'parser' is missing for {}".format(log)
                  )
  
      # Check if the defined sources are supported and report any invalid entries
      if TopLevelConfigKeys.CLUSTERS in config:
          # Used to track duplicate sources in separate cluster config files
          existing_sources = set()
          for cluster_name, cluster_attrs in config[
              TopLevelConfigKeys.CLUSTERS
          ].items():
              if "data_sources" not in cluster_attrs:
                  raise ConfigError(
                      "'data_sources' missing for cluster {}".format(
                          cluster_name
                      )
                  )
              _validate_sources(
                  cluster_name,
                  cluster_attrs["data_sources"],
                  existing_sources,
              )
  
              for func in CLUSTERED_FUNCTIONS:
                  config_name = "{}_config".format(func)
                  if config_name in cluster_attrs:
                      continue
  
                  error = "'{}' is missing in the '{}' cluster".format(
                      config_name, cluster_name
                  )
  
                  modules = cluster_attrs.get("modules", {})
                  old_format = None
                  for key in {"streamalert", "stream_alert"}:
                      if key in modules:
                          old_format = key
  
                  if old_format:
                      error += (
                          ". The usage of the '{}' within 'modules' has been deprecated and '{}'"
                          "should be included as a top level key"
                      ).format(old_format, config_name)
  
                  raise ConfigError(error)
  
      if TopLevelConfigKeys.THREAT_INTEL in config:
          if TopLevelConfigKeys.NORMALIZED_TYPES not in config:
              raise ConfigError(
                  "Normalized types must also be loaded with IOC types"
              )
  
          if (
              "normalized_ioc_types"
              not in config[TopLevelConfigKeys.THREAT_INTEL]
          ):
              raise ConfigError(
                  "Normalized IOC types must be defined for threat intelligence"
              )
  
          normalized_ioc_types = config[
              TopLevelConfigKeys.THREAT_INTEL
          ]["normalized_ioc_types"]
  
          for ioc_type, normalized_keys in normalized_ioc_types.items():
              for normalized_key in normalized_keys:
                  if not any(
                      normalized_key in set(log_keys)
                      for log_keys in list(
                          config[
                              TopLevelConfigKeys.NORMALIZED_TYPES
                          ].values()
                      )
                  ):
                      raise ConfigError(
                          "IOC key '{}' within IOC type '{}' must be defined for at least "
                          "one log type in normalized types".format(
                              normalized_key, ioc_type
                          )
                      )
  
imports: from collections import defaultdict, OrderedDict
  import json
  import os
  import re
  from streamalert.shared import CLUSTERED_FUNCTIONS
  from streamalert.shared.exceptions import ConfigError
  from streamalert.shared.logger import get_logger
label_desc: Use image node_id 10 for calling a global function with example usage: _validate_config(config) without return types