Esempio n. 1
0
    def test_compile_config_dot_square(self, fname_param_config):
        """Folder location.

        This is tricky because it depends on whether or not the user specified
        a config file and whether or not he also specified the "--folder" flag.

        """
        _, param, _ = fname_param_config
        param.configfile = None

        assert Filepath(".square.yaml").exists()

        # User specified "--no-config": use `.square.yaml` if it exists.
        param.no_config = False
        cfg, err = main.compile_config(param)
        assert not err and cfg.kubecontext == "dot-square"

        # User did not specify "--no-config" and`.square.yaml` exists too. This must
        # still use the default configuration.
        param.no_config = True
        cfg, err = main.compile_config(param)
        assert not err and cfg.kubecontext is None

        # User did not specify "--no-config": use `.square.yaml` if it exists.
        Filepath(".square.yaml").rename(".square.yaml.bak")
        assert not Filepath(".square.yaml").exists()
        param.no_config = False
        cfg, err = main.compile_config(param)
        assert not err and cfg.kubecontext is None
Esempio n. 2
0
    def test_compile_config_kinds_clear_existing(self, fname_param_config, tmp_path):
        """Empty list on command line must clear the option."""
        _, param, _ = fname_param_config

        # Use the test configuration for this test (it has non-zero labels).
        param.configfile = str(TEST_CONFIG_FNAME)

        # User did not provide `--labels` or `--namespace` option.
        param.labels = None
        param.namespaces = None

        # Convert the parsed command line into a `Config` structure.
        cfg, err = main.compile_config(param)
        assert not err

        # The defaults must have taken over because the user did not specify
        # new labels etc.
        assert cfg.selectors.labels == ["app=square"]
        assert cfg.selectors.namespaces == ["default", "kube-system"]
        assert cfg.groupby == GroupBy(label="app", order=["ns", "label", "kind"])

        # Pretend the user specified and empty `--labels`, `--groupby` and
        # `--namespaces`. This must clear the respective entries.
        param.labels = []
        param.groupby = []
        param.namespaces = []
        cfg, err = main.compile_config(param)
        assert not err

        # This time, the user supplied arguments must have cleared the
        # respective fields.
        assert cfg.selectors.labels == []
        assert cfg.selectors.namespaces == []
        assert cfg.groupby == GroupBy()
Esempio n. 3
0
    def test_compile_config_default_folder(self, fname_param_config):
        """Folder location.

        This is tricky because it depends on whether or not the user specified
        a config file and whether or not he also specified the "--folder" flag.

        """
        _, param, _ = fname_param_config

        # Config file and no "--folder": config file entry wins.
        param.configfile = str(TEST_CONFIG_FNAME)
        param.folder = None
        cfg, err = main.compile_config(param)
        assert not err
        assert cfg.folder == TEST_CONFIG_FNAME.parent.absolute() / "some/path"

        # Config file and "--folder some/where": `--folder` wins.
        param.configfile = str(TEST_CONFIG_FNAME)
        param.folder = "some/where"
        cfg, err = main.compile_config(param)
        assert not err and cfg.folder == Filepath("some/where")

        # No config file and no "--folder": manifest folder must be CWD.
        param.configfile = None
        param.folder = None
        cfg, err = main.compile_config(param)
        assert not err and cfg.folder == Filepath.cwd() / "foo" / "bar"

        # No config file and "--folder some/where": must point to `some/where`.
        param.configfile = None
        param.folder = "some/where"
        cfg, err = main.compile_config(param)
        assert not err and cfg.folder == Filepath("some/where")
Esempio n. 4
0
    def test_parse_commandline_get_grouping(self, tmp_path):
        """GET supports file hierarchy options."""
        kubeconfig = tmp_path / "kubeconfig.yaml"
        kubeconfig.write_text("")
        base_cmd = ("square.py", "get", "all",
                    "--kubeconfig", str(tmp_path / "kubeconfig.yaml"))

        # ----------------------------------------------------------------------
        # Default file system hierarchy.
        # ----------------------------------------------------------------------
        with mock.patch("sys.argv", base_cmd):
            param = main.parse_commandline_args()
            assert param.groupby is None

        # ----------------------------------------------------------------------
        # User defined file system hierarchy.
        # ----------------------------------------------------------------------
        cmd = ("--groupby", "ns", "kind")
        with mock.patch("sys.argv", base_cmd + cmd):
            param = main.parse_commandline_args()
            assert param.groupby == ["ns", "kind"]

        cfg, err = main.compile_config(param)
        assert not err
        assert cfg.groupby == GroupBy(label="", order=["ns", "kind"])

        # ----------------------------------------------------------------------
        # Include a label into the hierarchy and use "ns" twice.
        # ----------------------------------------------------------------------
        cmd = ("--groupby", "ns", "label=foo", "ns")
        with mock.patch("sys.argv", base_cmd + cmd):
            param = main.parse_commandline_args()
            assert param.groupby == ["ns", "label=foo", "ns"]

        cfg, err = main.compile_config(param)
        assert not err
        assert cfg.groupby == GroupBy(label="foo", order=["ns", "label", "ns"])

        # ----------------------------------------------------------------------
        # The label resource, unlike "ns" or "kind", can only be specified
        # at most once.
        # ----------------------------------------------------------------------
        cmd = ("--groupby", "ns", "label=foo", "label=bar")
        with mock.patch("sys.argv", base_cmd + cmd):
            param = main.parse_commandline_args()
            assert param.groupby == ["ns", "label=foo", "label=bar"]

        expected = Config(
            folder=Filepath(""),
            kubeconfig=Filepath(""),
            kubecontext=None,
            selectors=Selectors(set(), [], []),
            groupby=GroupBy("", []),
            priorities=[],
        )
        assert main.compile_config(param) == (expected, True)
Esempio n. 5
0
    def test_compile_config_kinds(self, fname_param_config):
        """Parse resource kinds."""
        # Specify `Service` twice.
        _, param, ref_config = fname_param_config
        param.kinds = ["Service", "Deploy", "Service"]
        cfg, err = main.compile_config(param)
        assert not err
        assert cfg.selectors.kinds == {"Service", "Deploy"}

        # An empty resource list must use the defaults.
        param.kinds = None
        cfg, err = main.compile_config(param)
        assert not err
        assert cfg.selectors.kinds == ref_config.selectors.kinds
Esempio n. 6
0
    def test_compile_hierarchy_ok(self, fname_param_config):
        """Parse the `--groupby` argument."""
        _, param, _ = fname_param_config

        err_resp = Config(
            folder=Filepath(""),
            kubeconfig=Filepath(""),
            kubecontext=None,
            selectors=Selectors(set(), [], []),
            groupby=GroupBy("", []),
            priorities=[],
        ), True

        # ----------------------------------------------------------------------
        # Default hierarchy.
        # ----------------------------------------------------------------------
        for cmd in ["apply", "get", "plan"]:
            param.parser = cmd
            ret, err = main.compile_config(param)
            assert not err
            assert ret.groupby == GroupBy(label="app", order=["ns", "label", "kind"])
            del cmd, ret, err

        # ----------------------------------------------------------------------
        # User defined hierarchy with a valid label.
        # ----------------------------------------------------------------------
        param.parser = "get"
        param.groupby = ("ns", "kind", "label=app", "ns")
        ret, err = main.compile_config(param)
        assert not err
        assert ret.groupby == GroupBy(label="app", order=["ns", "kind", "label", "ns"])

        # ----------------------------------------------------------------------
        # User defined hierarchy with invalid labels.
        # ----------------------------------------------------------------------
        param.parser = "get"
        invalid_labels = ["label", "label=", "label=foo=bar"]
        for label in invalid_labels:
            param.groupby = ("ns", "kind", label, "ns")
            assert main.compile_config(param) == err_resp

        # ----------------------------------------------------------------------
        # User defined hierarchy with invalid resource types.
        # ----------------------------------------------------------------------
        param.parser = "get"
        param.groupby = ("ns", "unknown")
        assert main.compile_config(param) == err_resp
Esempio n. 7
0
 def test_compile_config_missing_k8s_credentials(self, fname_param_config):
     """Gracefully abort if kubeconfig does not exist"""
     _, param, _ = fname_param_config
     param.kubeconfig += "does-not-exist"
     assert main.compile_config(param) == (
         Config(
             folder=Filepath(""),
             kubeconfig=Filepath(""),
             kubecontext=None,
             selectors=Selectors(set(), [], []),
             groupby=GroupBy("", []),
             priorities=[],
         ), True)
Esempio n. 8
0
    def test_compile_config_basic(self, fname_param_config):
        """Verify that our config and command line args fixtures match."""
        _, param, ref_config = fname_param_config

        # Convert the parsed command line `param` to a `Config` structure.
        out, err = main.compile_config(param)
        assert not err

        # The parsed `Config` must match our `ref_config` fixture except for
        # the folder and kubeconfig because the fixture explicitly overrode
        # those to point to a temporary location.
        out.folder, out.kubeconfig = ref_config.folder, ref_config.kubeconfig
        assert out == ref_config
Esempio n. 9
0
 def test_compile_config_missing_config_file(self, fname_param_config):
     """Abort if the config file is missing or invalid."""
     _, param, _ = fname_param_config
     param.configfile = Filepath("/does/not/exist.yaml")
     _, err = main.compile_config(param)
     assert err
Esempio n. 10
0
    def test_compile_config_kubeconfig(self, fname_param_config, tmp_path):
        """Which kubeconfig to use.

        The tricky part here is when to use the KUBECONFIG env var. With
        Square, it will *only* try to use KUBECONFIG if neither `--config` nor
        `--kubeconfig` was specified.

        """
        # Unpack the fixture: path to valid ".square.yaml", command line
        # parameters and an already parsed `Config`.
        fname_config, param, ref_config = fname_param_config
        ref_data = yaml.safe_load(fname_config.read_text())

        cwd = fname_config.parent
        fname_config_dotsquare = fname_config
        fname_config_custom = cwd / "customconfig.yaml"

        fname_kubeconfig_dotsquare = cwd / "kubeconfig-dotsquare"
        fname_kubeconfig_custom = cwd / "kubeconfig-custom"
        fname_kubeconfig_commandline = cwd / "kubeconfig-commandline"
        fname_kubeconfig_envvar = cwd / "kubeconfig-envvar"

        def reset():
            """Reset the files in the temporary folder."""
            data = copy.deepcopy(ref_data)

            # Write ".square".
            data["kubeconfig"] = str(fname_kubeconfig_dotsquare)
            fname_config_dotsquare.write_text(yaml.dump(data))

            # Write "customconfig.yaml"
            data["kubeconfig"] = str(fname_kubeconfig_custom)
            fname_config_custom.write_text(yaml.dump(data))

            # Create empty files for all Kubeconfigs.
            fname_kubeconfig_dotsquare.write_text("")
            fname_kubeconfig_custom.write_text("")
            fname_kubeconfig_commandline.write_text("")
            fname_kubeconfig_envvar.write_text("")

            del data

        # Create dummy kubeconfig for when we want to simulate `--kubeconfig`.
        kubeconfig_file = tmp_path / "kubeconfig-commandline"
        kubeconfig_file.write_text("")

        for envvar in [True, False]:
            new_env = os.environ.copy()
            if envvar:
                new_env["KUBECONFIG"] = str(fname_kubeconfig_envvar)
            else:
                new_env.pop("KUBECONFIG", None)

            with mock.patch.dict("os.environ", values=new_env, clear=True):
                reset()

                # .square: true, custom: true, --kubeconf: true -> --kubeconf
                param.configfile = str(fname_config_custom)
                param.kubeconfig = str(fname_kubeconfig_commandline)
                cfg, err = main.compile_config(param)
                assert not err and cfg.kubeconfig == fname_kubeconfig_commandline

                # .square: true, custom: true, --kubeconf: false -> custom
                param.configfile = str(fname_config_custom)
                param.kubeconfig = None
                cfg, err = main.compile_config(param)
                assert not err and cfg.kubeconfig == fname_kubeconfig_custom

                # .square: true, custom: false, --kubeconf: true -> --kubeconf
                param.configfile = None
                param.kubeconfig = str(fname_kubeconfig_commandline)
                cfg, err = main.compile_config(param)
                assert not err and cfg.kubeconfig == fname_kubeconfig_commandline

                # .square: true, custom: false, --kubeconf: false -> dotsquare
                param.configfile = None
                param.kubeconfig = None
                cfg, err = main.compile_config(param)
                assert not err and cfg.kubeconfig == fname_kubeconfig_dotsquare

                # ------------------------------------------------------------------

                reset()
                fname_config_dotsquare.unlink()
                assert not fname_config_dotsquare.exists()

                # .square: false, custom: true, --kubeconf: true -> --kubeconf
                param.configfile = str(fname_config_custom)
                param.kubeconfig = str(fname_kubeconfig_commandline)
                cfg, err = main.compile_config(param)
                assert not err and cfg.kubeconfig == fname_kubeconfig_commandline

                # .square: false, custom: true, --kubeconf: false -> custom
                param.configfile = str(fname_config_custom)
                param.kubeconfig = None
                cfg, err = main.compile_config(param)
                assert not err and cfg.kubeconfig == fname_kubeconfig_custom

                # .square: false, custom: false, --kubeconf: true -> --kubeconf
                param.configfile = None
                param.kubeconfig = str(fname_kubeconfig_commandline)
                cfg, err = main.compile_config(param)
                assert not err and cfg.kubeconfig == fname_kubeconfig_commandline

                # .square: false, custom: false, --kubeconf: false -> env var.
                param.configfile = None
                param.kubeconfig = None
                cfg, err = main.compile_config(param)

                if envvar:
                    assert not err and cfg.kubeconfig == Filepath(os.getenv("KUBECONFIG"))
                else:
                    assert err
Esempio n. 11
0
    def test_compile_config_kinds_merge_file(self, config, tmp_path):
        """Merge configuration from file and command line."""
        # Dummy file.
        kubeconfig_override = tmp_path / "kubeconfig"
        kubeconfig_override.write_text("")

        # ---------------------------------------------------------------------
        # Override nothing on the command line except for `kubeconfig` because
        # it must point to a valid file.
        # ---------------------------------------------------------------------
        param = types.SimpleNamespace(
            configfile=Filepath("tests/support/config.yaml"),

            # Must override this and point it to a dummy file or
            # `compile_config` will complain it does not exist.
            kubeconfig=str(kubeconfig_override),

            # User did not specify anything else.
            kubecontext=None,
            folder=None,
            groupby=None,
            kinds=None,
            labels=None,
            namespaces=None,
            priorities=None,
        )

        # Translate command line arguments into `Config`.
        cfg, err = main.compile_config(param)
        assert not err

        assert cfg.folder == Filepath("tests/support").absolute() / "some/path"
        assert cfg.kubeconfig == kubeconfig_override
        assert cfg.kubecontext is None
        assert cfg.priorities == list(DEFAULT_PRIORITIES)
        assert cfg.selectors == Selectors(
            kinds=set(DEFAULT_PRIORITIES),
            namespaces=["default", "kube-system"],
            labels=["app=square"],
        )
        assert cfg.groupby == GroupBy(label="app", order=["ns", "label", "kind"])
        assert set(cfg.filters.keys()) == {
            "_common_", "ConfigMap", "Deployment", "HorizontalPodAutoscaler", "Service"
        }

        # ---------------------------------------------------------------------
        # Override everything on the command line.
        # ---------------------------------------------------------------------
        param = types.SimpleNamespace(
            folder="folder-override",
            kinds=["Deployment", "Namespace"],
            labels=["app=square", "foo=bar"],
            namespaces=["default", "kube-system"],
            kubeconfig=str(kubeconfig_override),
            kubecontext="kubecontext-override",
            groupby=["kind", "label=foo", "ns"],
            priorities=["Namespace", "Deployment"],
            configfile=Filepath("tests/support/config.yaml"),
        )

        # Translate command line arguments into `Config`.
        cfg, err = main.compile_config(param)
        assert not err

        assert cfg.folder == Filepath(param.folder)
        assert cfg.kubeconfig == kubeconfig_override
        assert cfg.kubecontext == "kubecontext-override"
        assert cfg.priorities == ["Namespace", "Deployment"]
        assert cfg.selectors == Selectors(
            kinds={"Namespace", "Deployment"},
            namespaces=["default", "kube-system"],
            labels=["app=square", "foo=bar"],
        )
        assert cfg.groupby == GroupBy(label="foo", order=["kind", "label", "ns"])
        assert set(cfg.filters.keys()) == {
            "_common_", "ConfigMap", "Deployment", "HorizontalPodAutoscaler", "Service"
        }