def main(_args: Sequence[str]) -> int: """Main program.""" config = create_configuration() if not config.is_valid(): warning("Configuration is invalid. Cannot start.") return 1 generator = create_generator(config) envoy = create_envoy_handler(config) while True: if os.path.exists(config.trigger_stop_file): warning("Stopping due to existence of stop trigger file.") envoy.stop_envoy() return 0 debug('Generating envoy files.') res = generator.generate_file(config.envoy_listen_port, config.envoy_admin_port) if os.path.isfile(config.envoy_config_file): debug('Starting envoy.') envoy.start_if_not_running() if res != 0: warning("Envoy configuration generator returned {code}", code=res) if config.exit_on_generation_failure: warning("Stopping due to exit-on-failure.") envoy.stop_envoy() return res time.sleep(config.failure_sleep) else: time.sleep(config.refresh_time)
def push_template(config: Config) -> int: """Push a piece of the template into the templates document.""" if not config.purpose: log.warning( 'When pushing a template, the `purpose` argument is required.') return 3 if config.filename == '-': source_data = sys.stdin.read() elif not config.filename or not os.path.isfile(config.filename): log.warning('No such local file `{file}`', file=config.filename) return 4 else: with open(config.filename, 'r') as f: source_data = f.read() log.debug("Pulling current templates") templates = pull_document( config, TEMPLATES_DOCUMENT) or create_initial_templates_document() if config.category == 'gateway': update_gateway_template(templates, source_data, config.namespace, config.purpose) elif config.category == 'service': update_service_template( templates, source_data, config.namespace, config.service, config.color, config.purpose, ) else: log.warning('Unknown or unset category `{cat}`', cat=config.category) return 5 push_document(config, TEMPLATES_DOCUMENT, templates) return 0
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
def main(_args: Sequence[str]) -> int: """Main program.""" config = create_configuration() generator = create_generator(config) while True: if os.path.exists(config.trigger_stop_file): warning("Stopping due to existence of stop trigger file.") return 0 debug('Generating new discovery map.') res = generator.update_discovery_map() if res != 0: warning("Envoy configuration generator returned {code}", code=res) if config.exit_on_generation_failure: warning("Stopping due to exit-on-failure.") return res time.sleep(config.failure_sleep) else: time.sleep(config.refresh_time)
def generate_envoy_file(config: Config, file_name: str, contents: str) -> None: """Performs the correct construction of the envoy file. To properly support envoy dynamic configurations, the file must be created in a temporary file, then replaced via a *move* operation. Due to potential issues around move, the source file must be in the same directory as the target file (due to cross-file system move issues).""" # Note: this is a bit memory heavy, with the contents of source and target present # at the same time. out_dir = config.envoy_config_dir target_file = os.path.join(out_dir, file_name) # First, check if the file needs to be updated. That means the contents are different. if os.path.isfile(target_file): with open(target_file, 'r') as f: current_contents = f.read() if current_contents == contents: log.debug( "Contents of {file_name} are the same; not updating.", file_name=file_name) return gen_fd, gen_filename = tempfile.mkstemp(prefix=file_name, dir=out_dir, text=False) os.write(gen_fd, contents.encode('utf-8', errors='replace')) os.close(gen_fd) os.replace(gen_filename, target_file) log.log('INFO', "Generated configuration file {file_name}", file_name=file_name) log.debug('. . . . . . . . . . . . . . . . . .') log.debug_raw(contents) log.debug('. . . . . . . . . . . . . . . . . .')
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 start_if_not_running(self) -> None: """Start envoy if it is not running.""" if not self.is_alive(): debug('Starting {cmd}', cmd=self._config.envoy_cmd) self._proc = subprocess.Popen(self._cmd_args())