Repository: certbot/certbot

Issue:
Apache default default
So in `certbot_apache/constants.py` we have this code:

```
    os_info = util.get_os_info()
    try:
        constants = CLI_DEFAULTS[os_info[0].lower()]
    except KeyError:
        constants = CLI_DEFAULTS["debian"]
```

Basically, if you're OS isn't one we we expect, we assume it's Debian. Is this the correct behavior? With Debian, we assume there are `sites-available`/`sites-enabled`, `mods-available`/`mods-enabled`/, `a2enmod`/`a2dismod`, etc.

Perhaps if we can't recognize your OS, we should choose defaults with `handle_mods` and `handle_sites` set to `False` by default. I created this issue in response to #3302.

@joohoi, IIRC you wrote most of this code. What do you think?



Discussion:
You are right. defaulting to debian configuration is & was in place from the time we moved from debian / ubuntu only to multi-distro support to keep the original behavior as fallback for some possible corner cases. 

Debian / Ubuntu plays nicely along with distribution detection, so it should be safe to move to more widely working defaults.


Resolving patch:
diff --git a/certbot-apache/certbot_apache/constants.py b/certbot-apache/certbot_apache/constants.py
--- a/certbot-apache/certbot_apache/constants.py
+++ b/certbot-apache/certbot_apache/constants.py
@@ -2,7 +2,23 @@
 import pkg_resources
 from certbot import util
 
-
+CLI_DEFAULTS_DEFAULT = dict(
+    server_root="/etc/apache2",
+    vhost_root="/etc/apache2/sites-available",
+    vhost_files="*",
+    version_cmd=['apache2ctl', '-v'],
+    define_cmd=['apache2ctl', '-t', '-D', 'DUMP_RUN_CFG'],
+    restart_cmd=['apache2ctl', 'graceful'],
+    conftest_cmd=['apache2ctl', 'configtest'],
+    enmod=None,
+    dismod=None,
+    le_vhost_ext="-le-ssl.conf",
+    handle_mods=False,
+    handle_sites=False,
+    challenge_location="/etc/apache2",
+    MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
+        "certbot_apache", "options-ssl-apache.conf")
+)
 CLI_DEFAULTS_DEBIAN = dict(
     server_root="/etc/apache2",
     vhost_root="/etc/apache2/sites-available",
@@ -71,7 +87,25 @@
     MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
         "certbot_apache", "options-ssl-apache.conf")
 )
+CLI_DEFAULTS_SUSE = dict(
+    server_root="/etc/apache2",
+    vhost_root="/etc/apache2/vhosts.d",
+    vhost_files="*.conf",
+    version_cmd=['apache2ctl', '-v'],
+    define_cmd=['apache2ctl', '-t', '-D', 'DUMP_RUN_CFG'],
+    restart_cmd=['apache2ctl', 'graceful'],
+    conftest_cmd=['apache2ctl', 'configtest'],
+    enmod="a2enmod",
+    dismod="a2dismod",
+    le_vhost_ext="-le-ssl.conf",
+    handle_mods=False,
+    handle_sites=False,
+    challenge_location="/etc/apache2/vhosts.d",
+    MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
+        "certbot_apache", "options-ssl-apache.conf")
+)
 CLI_DEFAULTS = {
+    "default": CLI_DEFAULTS_DEFAULT,
     "debian": CLI_DEFAULTS_DEBIAN,
     "ubuntu": CLI_DEFAULTS_DEBIAN,
     "centos": CLI_DEFAULTS_CENTOS,
@@ -83,6 +117,8 @@
     "gentoo": CLI_DEFAULTS_GENTOO,
     "gentoo base system": CLI_DEFAULTS_GENTOO,
     "darwin": CLI_DEFAULTS_DARWIN,
+    "opensuse": CLI_DEFAULTS_SUSE,
+    "suse": CLI_DEFAULTS_SUSE,
 }
 """CLI defaults."""
 
@@ -115,13 +151,36 @@
 
 
 def os_constant(key):
-    """Get a constant value for operating system
+    """
+    Get a constant value for operating system
+
     :param key: name of cli constant
     :return: value of constant for active os
     """
+
     os_info = util.get_os_info()
     try:
         constants = CLI_DEFAULTS[os_info[0].lower()]
     except KeyError:
-        constants = CLI_DEFAULTS["debian"]
+        constants = os_like_constants()
+        if not constants:
+            constants = CLI_DEFAULTS["default"]
     return constants[key]
+
+
+def os_like_constants():
+    """
+    Try to get constants for distribution with
+    similar layout and configuration, indicated by
+    /etc/os-release variable "LIKE"
+
+    :returns: Constants dictionary
+    :rtype: `dict`
+    """
+
+    os_like = util.get_systemd_os_like()
+    if os_like:
+        for os_name in os_like:
+            if os_name in CLI_DEFAULTS.keys():
+                return CLI_DEFAULTS[os_name]
+    return {}
diff --git a/certbot/util.py b/certbot/util.py
--- a/certbot/util.py
+++ b/certbot/util.py
@@ -268,6 +268,19 @@ def get_systemd_os_info(filepath="/etc/os-release"):
     return (os_name, os_version)
 
 
+def get_systemd_os_like(filepath="/etc/os-release"):
+    """
+    Get a list of strings that indicate the distribution likeness to
+    other distributions.
+
+    :param str filepath: File path of os-release file
+    :returns: List of distribution acronyms
+    :rtype: `list` of `str`
+    """
+
+    return _get_systemd_os_release_var("ID_LIKE", filepath).split(" ")
+
+
 def _get_systemd_os_release_var(varname, filepath="/etc/os-release"):
     """
     Get single value from systemd /etc/os-release


Test patch:
diff --git a/certbot-apache/certbot_apache/tests/constants_test.py b/certbot-apache/certbot_apache/tests/constants_test.py
--- a/certbot-apache/certbot_apache/tests/constants_test.py
+++ b/certbot-apache/certbot_apache/tests/constants_test.py
@@ -25,3 +25,20 @@ def test_get_default_value(self, os_info):
         os_info.return_value = ('Nonexistent Linux', '', '')
         self.assertEqual(constants.os_constant("vhost_root"),
                          "/etc/apache2/sites-available")
+
+    @mock.patch("certbot.util.get_os_info")
+    def test_get_default_constants(self, os_info):
+        os_info.return_value = ('Nonexistent Linux', '', '')
+        with mock.patch("certbot.util.get_systemd_os_like") as os_like:
+            # Get defaults
+            os_like.return_value = False
+            c_hm = constants.os_constant("handle_mods")
+            c_sr = constants.os_constant("server_root")
+            self.assertFalse(c_hm)
+            self.assertEqual(c_sr, "/etc/apache2")
+            # Use darwin as like test target
+            os_like.return_value = ["something", "nonexistent", "darwin"]
+            d_vr = constants.os_constant("vhost_root")
+            d_em = constants.os_constant("enmod")
+            self.assertFalse(d_em)
+            self.assertEqual(d_vr, "/etc/apache2/other")
diff --git a/certbot/tests/testdata/os-release b/certbot/tests/testdata/os-release
--- a/certbot/tests/testdata/os-release
+++ b/certbot/tests/testdata/os-release
@@ -1,7 +1,7 @@
 NAME="SystemdOS"
 VERSION="42.42.42 LTS, Unreal"
 ID=systemdos
-ID_LIKE=debian
+ID_LIKE="something nonexistent debian"
 VERSION_ID="42"
 HOME_URL="http://www.example.com/"
 SUPPORT_URL="http://help.example.com/"
diff --git a/certbot/tests/util_test.py b/certbot/tests/util_test.py
--- a/certbot/tests/util_test.py
+++ b/certbot/tests/util_test.py
@@ -359,6 +359,15 @@ def test_systemd_os_release(self):
         with mock.patch('os.path.isfile', return_value=False):
             self.assertEqual(get_systemd_os_info(), ("", ""))
 
+    def test_systemd_os_release_like(self):
+        from certbot.util import get_systemd_os_like
+
+        with mock.patch('os.path.isfile', return_value=True):
+            id_likes = get_systemd_os_like(test_util.vector_path(
+                "os-release"))
+            self.assertEqual(len(id_likes), 3)
+            self.assertTrue("debian" in id_likes)
+
     @mock.patch("certbot.util.subprocess.Popen")
     def test_non_systemd_os_info(self, popen_mock):
         from certbot.util import (get_os_info, get_python_os_info,