def test_load_err(self, tmp_path): """Gracefully handle missing file, corrupt content etc.""" # Must gracefully handle a corrupt configuration file. fname = tmp_path / "does-not-exist.yaml" _, err = cfgfile.load(fname) assert err # YAML error. fname = tmp_path / "corrupt-yaml.yaml" fname.write_text("[foo") _, err = cfgfile.load(fname) assert err # Does not match the definition of `dtypes.Config`. fname = tmp_path / "invalid-pydantic-schema.yaml" fname.write_text("foo: bar") _, err = cfgfile.load(fname) assert err # YAML file is valid but not a map. This special case is important # because the test function will expand the content as **kwargs. fname = tmp_path / "invalid-pydantic-schema.yaml" fname.write_text("") _, err = cfgfile.load(fname) assert err # Load the sample configuration and corrupt the label selector. Instead # of a list of 2-tuples we make it a list of 3-tuples. cfg = yaml.safe_load(Filepath("tests/support/config.yaml").read_text()) cfg["selectors"]["labels"] = [["foo", "bar", "foobar"]] fout = tmp_path / "corrupt.yaml" fout.write_text(yaml.dump(cfg)) _, err = cfgfile.load(fout) assert err
def test_common_filters(self, tmp_path): """Deal with empty or non-existing `_common_` filter.""" fname_ref = Filepath("tests/support/config.yaml") # ---------------------------------------------------------------------- # Empty _common_ filters. # ---------------------------------------------------------------------- # Clear the "_common_" filter and save the configuration again. ref = yaml.safe_load(fname_ref.read_text()) ref["filters"]["_common_"].clear() fout = tmp_path / "corrupt.yaml" fout.write_text(yaml.dump(ref)) # Load the new configuration. This must succeed and the filters must # match the ones defined in the file because there the "_common_" # filter was empty. cfg, err = cfgfile.load(fout) assert not err and ref["filters"] == cfg.filters # ---------------------------------------------------------------------- # Missing _common_ filters. # ---------------------------------------------------------------------- # Remove the "_common_" filter and save the configuration again. ref = yaml.safe_load(fname_ref.read_text()) del ref["filters"]["_common_"] fout = tmp_path / "valid.yaml" fout.write_text(yaml.dump(ref)) # Load the new configuration. This must succeed and the filters must # match the ones defined in the file because there was no "_common_" # filter to merge. cfg, err = cfgfile.load(fout) assert cfg.filters["_common_"] == [] del cfg.filters["_common_"] assert not err and ref["filters"] == cfg.filters
def test_load_folder_paths(self, tmp_path): """The folder paths must always be relative to the config file.""" fname = tmp_path / ".square.yaml" fname_ref = Filepath("tests/support/config.yaml") # The parsed folder must point to "tmp_path". ref = yaml.safe_load(fname_ref.read_text()) fname.write_text(yaml.dump(ref)) cfg, err = cfgfile.load(fname) assert not err and cfg.folder == tmp_path / "some/path" # The parsed folder must point to "tmp_path/folder". ref = yaml.safe_load(fname_ref.read_text()) ref["folder"] = "my-folder" fname.write_text(yaml.dump(ref)) cfg, err = cfgfile.load(fname) assert not err and cfg.folder == tmp_path / "my-folder" # An absolute path must ignore the position of ".square.yaml". # No idea how to handle this on Windows. if not sys.platform.startswith("win"): ref = yaml.safe_load(fname_ref.read_text()) ref["folder"] = "/absolute/path" fname.write_text(yaml.dump(ref)) cfg, err = cfgfile.load(fname) assert not err and cfg.folder == Filepath("/absolute/path")
def test_boostrap(self): """Verify the constants created during package import.""" assert hasattr(square, "get") assert hasattr(square, "plan") assert hasattr(square, "apply_plan") assert hasattr(square, "show_plan") # Must have loaded the configuration file. cfg, err = cfgfile.load(DEFAULT_CONFIG_FILE) assert not err assert square.DEFAULT_CONFIG == cfg
def test_boostrap(self): """Verify the constants created during package import.""" assert hasattr(square, "get") assert hasattr(square, "plan") assert hasattr(square, "apply_plan") assert hasattr(square, "show_plan") # Must have loaded the default configuration file but wiped the # namespace selector. cfg, err = cfgfile.load(DEFAULT_CONFIG_FILE) assert not err cfg.selectors.namespaces.clear() assert square.DEFAULT_CONFIG == cfg
def test_load(self): """Load and parse configuration file.""" # Load the sample that ships with Square. fname = Filepath("tests/support/config.yaml") cfg, err = cfgfile.load(fname) assert not err and isinstance(cfg, Config) assert cfg.folder == fname.parent.absolute() / "some/path" assert cfg.kubeconfig == Filepath("/path/to/kubeconfig") assert cfg.kubecontext is None assert cfg.priorities == list(DEFAULT_PRIORITIES) assert cfg.selectors.kinds == set(DEFAULT_PRIORITIES) assert cfg.selectors.namespaces == ["default", "kube-system"] assert cfg.selectors.labels == ["app=square"] assert set(cfg.filters.keys()) == { "_common_", "ConfigMap", "Deployment", "HorizontalPodAutoscaler", "Service" }
def fname_param_config(tmp_path) -> Generator[ Tuple[Filepath, types.SimpleNamespace, Config], None, None]: """Parsed command line args to produce the default configuration. The return values are what `parse_commandline_args` would return, as well as what `compile_config` should convert them into. This removes a lot of boiler plate in the tests. """ # Location of our configuration file and dummy Kubeconfig. fname_square = tmp_path / ".square.yaml" fname_kubeconfig = tmp_path / "kubeconfig-dot-square" # Duplicate the default configuration with the new kubeconfig. ref = yaml.safe_load(DEFAULT_CONFIG_FILE.read_text()) assert "kubeconfig" in ref and "kubecontext" in ref ref["kubeconfig"] = str(fname_kubeconfig.absolute()) # We will "abuse" the `kubecontext` field to indicate that this is our mock # ".square.yaml". We will also replace the default path with a custom one # to avoid an ambiguity with the default value of "." for the folder. ref["kubecontext"] = "dot-square" assert ref["folder"] == "." ref["folder"] = "foo/bar" fname_square.write_text(yaml.dump(ref)) del ref # Load the sample configuration. config, err = cfgfile.load(fname_square) assert not err # Point the folder and kubeconfig to temporary versions. config.folder = tmp_path # Ensure the dummy kubeconfig file exists. config.kubeconfig.write_text("") # Override the version because this one is difficult (and pointless) to # compare in tests. config.version = "" params = types.SimpleNamespace( # Not in config file - command line arguments only. parser="get", configfile="", verbosity=9, info=False, no_config=False, # Dummy kubeconfig created by the `config` fixture. kubeconfig=str(config.kubeconfig), # These were not specified on the command line. folder=".", kinds=DEFAULT_PRIORITIES, labels=[], namespaces=["default"], kubecontext=None, groupby=["ns", "label=app", "kind"], priorities=DEFAULT_PRIORITIES, ) cwd = pathlib.Path.cwd() os.chdir(tmp_path) yield fname_square, params, config os.chdir(cwd)