Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Linux distribution detection #988

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

Regrau
Copy link

@Regrau Regrau commented Jul 23, 2022

This will close #983. I took the liberty to reorder the file a little so one does not need to parse the whole file to understand what will happen. As it is now, this will implement both ways of checking for the distribution as discussed. It will check for the distribution and the package manager. If those checks fail it will try to work with the package manager only on a best effort basis.

Now you only need to parse the main function to understand the intent of the file. Addition of new distributions should hopefully be easier as well since one won't need to parse the long if elif else decision tree.

I'd like to simplify the new install_$DISTRO_dependencies functions a little more too but I'd need input on if it is the right course to take.

Signed-off-by: Georg Grauberger <georg.grauberger@gmail.com>
@Regrau Regrau force-pushed the improve-linux-distribution-check branch from 86ff6cf to f90c2a8 Compare July 23, 2022 21:30
Signed-off-by: Georg Grauberger <georg.grauberger@gmail.com>
@Regrau Regrau force-pushed the improve-linux-distribution-check branch from e7d2388 to 8e4b03b Compare July 23, 2022 21:36
if ! grep -q "^user_allow_other" "${fuse_conf}"; then
echo "user_allow_other" >>"${fuse_conf}"
fi
. /etc/os-release
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this code should be more robust against various conditions:

  • What if /etc/os-release doesn't exist?

    E.g. use [ -f /etc/os-release ] && source /etc/os-release ?

  • What if ID or ID_LIKE are not set?

    This would throw an error because we have set -u at the top. Maybe preset the variables to the empty string before sourcing /etc/os-release?

  • What if ID_LIKE lists multiple ids (e.g. rhel fedora instead of just fedora?

    Maybe do some regular expression match? /bin/sh doesn't support this though, and we can't rely on bash. Can we rely on expr being available, so we can do something like expr "$ID_LIKE" : ".*fedora" >/dev/null?

Copy link
Author

@Regrau Regrau Jul 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are absolutely right the code should be made more robust. I implemented all of your suggestions.

As for the availability of expr we should be able to rely on that since it [seems to be part of the standard].(https://pubs.opengroup.org/onlinepubs/9699919799/)

Let me know if you think there is anything else I should add/remove/rework.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix shellcheck errors and remove trailing spaces from commands:

--- pkg/cidata/cidata.TEMPLATE.d/boot/30-install-packages.sh
+++ pkg/cidata/cidata.TEMPLATE.d/boot/30-install-packages.sh
@@ -6,6 +6,7 @@ DISTRIBUTION=""
 INSTALL_IPTABLES=0
 ID_LIKE=""
 ID=""
+# shellcheck source=/dev/null
 [ -f /etc/os-release ] && . /etc/os-release


@@ -15,22 +16,22 @@ main() {
        install_minimal_dependencies
        setup_dns
        # update_fuse_conf has to be called after installing all the packages,
-       # otherwise apt-get fails with conflict
-       update_fuse_conf
+       # otherwise apt-get fails with conflict
+       update_fuse_conf
 }

 determine_distribution() {
-       if command -v apt-get >/dev/null 2>&1 && [ $(expr "$ID_LIKE" : ".*debian") -gt 0 ]; then
+       if command -v apt-get >/dev/null 2>&1 && [ "$(expr "$ID_LIKE" : ".*debian")" -gt 0 ]; then
                DISTRIBUTION="debian_like"
-       elif command -v dnf >/dev/null 2>&1 && [ $(expr "$ID" : ".*fedora") -gt 0 ]; then
+       elif command -v dnf >/dev/null 2>&1 && [ "$(expr "$ID" : ".*fedora")" -gt 0 ]; then
                DISTRIBUTION="redhat_like"
-       elif command -v yum >/dev/null 2>&1 && [ $(expr "$ID_LIKE" : ".*centos") -gt 0 ]; then
+       elif command -v yum >/dev/null 2>&1 && [ "$(expr "$ID_LIKE" : ".*centos")" -gt 0 ]; then
                DISTRIBUTION="centos_like"
-       elif command -v pacman >/dev/null 2>&1 && [ $(expr "$ID_LIKE" : ".*arch") -gt 0 ]; then
+       elif command -v pacman >/dev/null 2>&1 && [ "$(expr "$ID_LIKE" : ".*arch")" -gt 0 ]; then
                DISTRIBUTION="arch_like"
-       elif command -v zypper >/dev/null 2>&1 && [ $(expr "$ID_LIKE" : ".*suse") -gt 0 ]; then
+       elif command -v zypper >/dev/null 2>&1 && [ "$(expr "$ID_LIKE" : ".*suse")" -gt 0 ]; then
                DISTRIBUTION="suse_like"
-       elif command -v apk >/dev/null 2>&1 && [ $(expr "$ID" : ".*alpine") -gt 0 ]; then
+       elif command -v apk >/dev/null 2>&1 && [ "$(expr "$ID" : ".*alpine")" -gt 0 ]; then
                DISTRIBUTION="alpine_like"
        else
                guess_distribution_based_on_package_manager

(the diff above doesn't show the trailing spaces correctly, but you can see the 2 lines that have them).

Signed-off-by: Georg Grauberger <georg.grauberger@gmail.com>
Signed-off-by: Georg Grauberger <georg.grauberger@gmail.com>
Since expr returns 0 in the context of the `:` operator we
have to look for numbers greater than 0. Without this,
the command always evaluates to true which leads to wrong
behaviour.

Signed-off-by: Georg Grauberger <georg.grauberger@gmail.com>
Signed-off-by: Georg Grauberger <georg.grauberger@gmail.com>
Comment on lines 25 to 26
elif command -v dnf >/dev/null 2>&1 && [ $(expr "$ID" : ".*fedora") -gt 0 ]; then
DISTRIBUTION="redhat_like"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this should have tested ID_LIKE instead:

$ nerdctl run --rm -it redhat/ubi8 grep "^ID" /etc/os-release
ID="rhel"
ID_LIKE="fedora"

Since the values for ID and ID_LIKE don't seem to be standardized, I wonder if it wouldn't be best to always check both settings for a whole range of values, something like (untested):

os_like() {
    for candidate in $@; do
        if [ "$ID" = "$candidate" ]; then
            return 0
        fi
        if [ "$(expr "$ID_LIKE" : ".*$candidate")" -gt 0 ]; then
            return 0
        fi
    done
    return 1
}

and then use it like this:

	elif command -v dnf >/dev/null 2>&1 && os_like redhat rhat rhel fedora; then
		DISTRIBUTION="redhat_like"

It will still require special ordering to make sure that centos is checked first, so it is not misidentified as redhat:

$ nerdctl run --rm -it centos grep "^ID" /etc/os-release
ID="centos"
ID_LIKE="rhel fedora"
$ nerdctl run --rm -it rockylinux:9 grep "^ID" /etc/os-release
ID="rocky"
ID_LIKE="rhel centos fedora"
$ nerdctl run --rm -it almalinux:9 grep "^ID" /etc/os-release
ID="almalinux"
ID_LIKE="rhel centos fedora"

It only works right now because the redhat check above is actually broken for anything but fedora itself.

I assume we want to classify almalinux and rockylinux as fedora and not centos, in which case we would have to use [ "$ID" = "centos" ] as a special case at the very top.

There may be more special cases; I don't have time to check all the possible images now. I think it would be great to move the os detection to a separate file (still under cidata), but then add a test script that verifies that is guesses the right setting for a bunch of known images. That way we can add additional images in the future, and adjust the rules, and still be confident that it continues to work for all other images.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For testing I was thinking of something like this:

nerdctl run --rm -v $PWD/pkg/cidata/cidata.TEMPLATE.d/boot:/ci almalinux:9 sh -c "/ci/30-install-packages.sh && echo $DISTRIBUTION"

It obviously doesn't work right now, but should be easy enough to get to work, and then we just need to call it a bunch of times with known images, and check the resulting DISTRIBUTION value.

Copy link
Author

@Regrau Regrau Jul 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented an external function for distribution detection. The functions in the script are part of the salt-bootstrap.sh. They're much more comprehensive than I could've written them.
As both lima and salt-bootstrap are licensed under the apache license 2 there should be no problem license wise.

As this is my first contribution with the code of others I'm not quite sure if there is anything else that I need to do other than adding the license notice in the file. Any help in that regard is much appreciated.

If the general direction is ok I'd polish up this POC over the next week. If not, let me know and I'll look into what else I can do here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is good, but I'll be on vacation and mostly offline for the next 4 weeks, so I hope @AkihiroSuda will provide further guidance. But I think this is all going into the right direction, and being more comprehensive than I originally expected.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, looks too much complicated and also contains a bunch of dead distros like United Linux.

Signed-off-by: Georg Grauberger <georg.grauberger@gmail.com>
Signed-off-by: Georg Grauberger <georg.grauberger@gmail.com>
Signed-off-by: Georg Grauberger <georg.grauberger@gmail.com>
@Regrau Regrau force-pushed the improve-linux-distribution-check branch from 27a01c0 to 5fb05f3 Compare July 30, 2022 22:06
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment to clarify the origin (and the purpose) of this file

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I'm feeling this script is too much complicated.. Just grepping /etc/os-release (lsb-release) might be enough

;;
"TurboLinux")
;;
"UnitedLinux")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dead distro

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Linux distribution detection is unreliable
3 participants