Пример #1
0
 def __init__(self, config: Config) -> None:
     self._config = config
     self._data_store = DataStoreRunner(config.data_store_exec,
                                        config.temp_dir)
     self._discovery_map = DiscoveryMapRunner(config.discovery_map_exec,
                                              config.temp_dir)
     os.makedirs(config.envoy_config_dir, exist_ok=True)
Пример #2
0
 def __init__(self, config: Config) -> None:
     self._config = config
     self._data_store = DataStoreRunner(config.data_store_exec,
                                        config.temp_dir)
     self._discovery_map = DiscoveryMapRunner(config.discovery_map_exec,
                                              config.temp_dir)
     self._gen_file = os.path.join(self._config.temp_dir,
                                   'generated-discovery-map.json')
     self._old_file = os.path.join(self._config.temp_dir,
                                   'last-discovery-map.json')
Пример #3
0
def push_document(config: Config, document_type: str, data: Dict[str,
                                                                 Any]) -> int:
    """Push the document contents to the data store"""
    temp_dir = tempfile.mkdtemp()
    try:
        runner = DataStoreRunner(config.data_store_exec, temp_dir,
                                 config.config_env)
        runner.commit_document(cast(DocumentName, document_type), data)
        return 0
    finally:
        shutil.rmtree(temp_dir)
Пример #4
0
def pull_document(config: Config,
                  document_type: str) -> Optional[Dict[str, Any]]:
    """Pull the document."""
    temp_dir = tempfile.mkdtemp()
    try:
        runner = DataStoreRunner(config.data_store_exec, temp_dir,
                                 config.config_env)
        runner.max_retry_count = 1
        return runner.fetch_document(cast(DocumentName, document_type))
    except ExtensionPointTooManyRetries:
        log.warning("No existing document.")
        return None
    finally:
        shutil.rmtree(temp_dir)
Пример #5
0
class GenerateGatewayConfiguration(Generator):
    """Manages the gateway configuration generation."""
    __slots__ = (
        '_config',
        '_data_store',
        '_discovery_map',
    )

    def __init__(self, config: Config) -> None:
        self._config = config
        self._data_store = DataStoreRunner(config.data_store_exec,
                                           config.temp_dir)
        self._discovery_map = DiscoveryMapRunner(config.discovery_map_exec,
                                                 config.temp_dir)
        os.makedirs(config.envoy_config_dir, exist_ok=True)

    def generate_file(self, listen_port: int, admin_port: int) -> int:
        """Runs the generation process."""
        try:
            log.debug("Fetching discovery map")
            discovery_map = self._discovery_map.get_mesh()
            mapping = create_gateway_proxy_input(
                discovery_map,
                self._config.namespace,
                listen_port,
                admin_port,
            )
            if isinstance(mapping, int):
                log.warning("Could not create mapping.")
                return mapping
            for purpose, template in self.get_templates().items():
                log.debug("Rendering template {purpose}", purpose=purpose)
                rendered = pystache.render(template, mapping)
                generate_envoy_file(self._config, purpose, rendered)
            return 0
        except (ExtensionPointRuntimeError,
                ExtensionPointTooManyRetries) as err:
            print("[nightjar-standalone] File construction generated error: " +
                  repr(err))
            return 1

    def get_templates(self) -> Dict[str, str]:
        """Get the right templates for this mode (purpose -> template)."""
        log.debug("Fetching templates")
        all_templates = self._data_store.fetch_document('templates')
        default_templates: Dict[str, str] = {}
        namespace_templates: Dict[str, str] = {}
        for gateway_template in all_templates['gateway-templates']:
            namespace = gateway_template['namespace']
            purpose = gateway_template['purpose']
            if namespace == self._config.namespace:
                namespace_templates[purpose] = gateway_template['template']
            elif namespace == TEMPLATE_DEFAULT:
                default_templates[purpose] = gateway_template['template']
        return namespace_templates or default_templates
Пример #6
0
class GenerateDataImpl(GenerateData):
    """Manages the gateway configuration generation."""
    __slots__ = (
        '_config',
        '_data_store',
        '_discovery_map',
        '_gen_file',
        '_old_file',
    )

    def __init__(self, config: Config) -> None:
        self._config = config
        self._data_store = DataStoreRunner(config.data_store_exec,
                                           config.temp_dir)
        self._discovery_map = DiscoveryMapRunner(config.discovery_map_exec,
                                                 config.temp_dir)
        self._gen_file = os.path.join(self._config.temp_dir,
                                      'generated-discovery-map.json')
        self._old_file = os.path.join(self._config.temp_dir,
                                      'last-discovery-map.json')

    def update_discovery_map(self) -> int:
        """Generate the new discovery map, and, if it is different than the old one, commit it."""
        ret = self.generate_discovery_map()
        if ret != 0:
            return ret
        if self.is_generated_map_different():
            ret = self.commit_discovery_map()
        return ret

    def generate_discovery_map(self) -> int:
        """Runs the generation process."""
        try:
            discovery_map = self._discovery_map.get_mesh()
        except ExtensionPointRuntimeError as err:
            print("[nightjar-central] Failed to create the discovery map: " +
                  repr(err))
            return 1
        with open(self._gen_file, 'w') as f:
            json.dump(discovery_map, f)
        return 0

    def is_generated_map_different(self) -> bool:
        """Check if the generated discovery map is different than the last committed one."""
        if not os.path.isfile(self._gen_file):
            # No generated file, so there's nothing to commit
            return False
        if not os.path.isfile(self._old_file):
            # No old version, so it's definitely new.
            return True
        # Simple comparison.  This requires loading both files into memory.  It
        # shouldn't be a problem for this container.
        with open(self._gen_file, 'r') as f:
            new_contents = json.load(f)
        with open(self._old_file, 'r') as f:
            old_contents = json.load(f)
        return cast(bool, new_contents != old_contents)

    def commit_discovery_map(self) -> int:
        """Push the just-generated discovery map to the data store."""
        if os.path.isfile(self._gen_file):
            with open(self._gen_file, 'r') as f:
                data = json.load(f)

            try:
                self._data_store.commit_document('discovery-map', data)
            except (ExtensionPointRuntimeError,
                    ExtensionPointTooManyRetries) as err:
                print("[nightjar_central] " + str(err))
                return 1

            os.replace(self._gen_file, self._old_file)
        return 0
Пример #7
0
class GenerateServiceConfiguration(Generator):
    """Manages the service configuration generation."""
    __slots__ = (
        '_config',
        '_data_store',
        '_discovery_map',
    )

    def __init__(self, config: Config) -> None:
        self._config = config
        self._data_store = DataStoreRunner(config.data_store_exec,
                                           config.temp_dir)
        self._discovery_map = DiscoveryMapRunner(config.discovery_map_exec,
                                                 config.temp_dir)
        os.makedirs(config.envoy_config_dir, exist_ok=True)

    def generate_file(self, listen_port: int, admin_port: int) -> int:
        """Runs the generation process."""
        discovery_map = self._discovery_map.get_mesh()
        mapping = create_service_color_proxy_input(
            discovery_map,
            self._config.namespace,
            self._config.service,
            self._config.color,
            listen_port,
            admin_port,
        )
        if isinstance(mapping, int):
            log.warning("Could not generate mapping.")
            return mapping
        for purpose, template in self.get_templates().items():
            rendered = pystache.render(template, mapping)
            generate_envoy_file(self._config, purpose, rendered)
        return 0

    def get_templates(self) -> Dict[str, str]:
        """Get the right templates for this mode (purpose -> template)."""
        all_templates = self._data_store.fetch_document('templates')
        possible_templates: Dict[Tuple[
            Optional[str], Optional[str], Optional[str]], Dict[str, str], ] = {
                # Ensure defaults are always present...
                (TEMPLATE_DEFAULT, TEMPLATE_DEFAULT, TEMPLATE_DEFAULT): {},
            }
        for service_template in all_templates['service-templates']:
            namespace = service_template['namespace']
            service = service_template['service']
            color = service_template['color']
            if self.is_possible_match(namespace, service, color):
                purpose = service_template['purpose']
                key = (
                    namespace,
                    service,
                    color,
                )
                if key not in possible_templates:
                    possible_templates[key] = {}
                possible_templates[key][purpose] = service_template['template']
        return possible_templates[self.get_best_match(
            possible_templates.keys())]

    def is_possible_match(self, namespace: str, service: str,
                          color: str) -> bool:
        """Are the given arguments possible matches?"""
        return (namespace in (TEMPLATE_DEFAULT, self._config.namespace)
                and service in (TEMPLATE_DEFAULT, self._config.service)
                and color in (TEMPLATE_DEFAULT, self._config.color))

    def get_best_match(
        self,
        keys: Iterable[Tuple[Optional[str], Optional[str], Optional[str]]],
    ) -> Tuple[Optional[str], Optional[str], Optional[str]]:
        """Finds the best match for the keys to this configuration."""
        best: Tuple[Optional[str], Optional[str], Optional[str]] = (
            TEMPLATE_DEFAULT,
            TEMPLATE_DEFAULT,
            TEMPLATE_DEFAULT,
        )
        best_key = 0
        for key in keys:
            match = self.get_key_match(key)
            if best_key < match:
                best = key
                best_key = match
        return best

    def get_key_match(
            self, key: Tuple[Optional[str], Optional[str],
                             Optional[str]]) -> int:
        """Get the key match number.  Higher is better.  Only works for default or exact match."""
        return ((5 if key[0] == self._config.namespace else 0) +
                (3 if key[1] == self._config.service else 0) +
                (1 if key[2] == self._config.color else 0))