/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
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.
*/

package distros

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"

	"k8s.io/klog"
)

// FindDistribution identifies the distribution on which we are running
// We will likely remove this when everything is containerized
func FindDistribution(rootfs string) (Distribution, error) {
	// Ubuntu has /etc/lsb-release (and /etc/debian_version)
	lsbRelease, err := ioutil.ReadFile(path.Join(rootfs, "etc/lsb-release"))
	if err == nil {
		for _, line := range strings.Split(string(lsbRelease), "\n") {
			line = strings.TrimSpace(line)
			if line == "DISTRIB_CODENAME=xenial" {
				return DistributionXenial, nil
			} else if line == "DISTRIB_CODENAME=bionic" {
				klog.Warningf("bionic is not fully supported nor tested for Kops and Kubernetes")
				klog.Warningf("this should only be used for testing purposes.")
				return DistributionBionic, nil
			}
		}
	} else if !os.IsNotExist(err) {
		klog.Warningf("error reading /etc/lsb-release: %v", err)
	}

	// Debian has /etc/debian_version
	debianVersionBytes, err := ioutil.ReadFile(path.Join(rootfs, "etc/debian_version"))
	if err == nil {
		debianVersion := strings.TrimSpace(string(debianVersionBytes))
		if strings.HasPrefix(debianVersion, "8.") {
			return DistributionJessie, nil
		} else if strings.HasPrefix(debianVersion, "9.") {
			return DistributionDebian9, nil
		} else if strings.HasPrefix(debianVersion, "10.") {
			return DistributionDebian10, nil
		} else {
			return "", fmt.Errorf("unhandled debian version %q", debianVersion)
		}
	} else if !os.IsNotExist(err) {
		klog.Warningf("error reading /etc/debian_version: %v", err)
	}

	// Redhat has /etc/redhat-release
	// Centos has /etc/centos-release
	redhatRelease, err := ioutil.ReadFile(path.Join(rootfs, "etc/redhat-release"))
	if err == nil {
		for _, line := range strings.Split(string(redhatRelease), "\n") {
			line = strings.TrimSpace(line)
			if strings.HasPrefix(line, "Red Hat Enterprise Linux Server release 7.") {
				return DistributionRhel7, nil
			}
			if strings.HasPrefix(line, "CentOS Linux release 7.") {
				return DistributionCentos7, nil
			}
			if strings.HasPrefix(line, "Red Hat Enterprise Linux release 8.") {
				return DistributionRhel8, nil
			}
			if strings.HasPrefix(line, "CentOS Linux release 8.") {
				return DistributionCentos8, nil
			}
		}
		klog.Warningf("unhandled redhat-release info %q", string(lsbRelease))
	} else if !os.IsNotExist(err) {
		klog.Warningf("error reading /etc/redhat-release: %v", err)
	}

	// CoreOS uses /usr/lib/os-release
	// Flatcar uses /usr/lib/os-release
	usrLibOsRelease, err := ioutil.ReadFile(path.Join(rootfs, "usr/lib/os-release"))
	if err == nil {
		for _, line := range strings.Split(string(usrLibOsRelease), "\n") {
			line = strings.TrimSpace(line)
			if line == "ID=coreos" {
				return DistributionCoreOS, nil
			} else if line == "ID=flatcar" {
				return DistributionFlatcar, nil
			}
		}
		klog.Warningf("unhandled os-release info %q", string(usrLibOsRelease))
	} else if !os.IsNotExist(err) {
		klog.Warningf("error reading /usr/lib/os-release: %v", err)
	}

	// ContainerOS, Amazon Linux 2 uses /etc/os-release
	osRelease, err := ioutil.ReadFile(path.Join(rootfs, "etc/os-release"))
	if err == nil {
		for _, line := range strings.Split(string(osRelease), "\n") {
			line = strings.TrimSpace(line)
			if line == "ID=cos" {
				return DistributionContainerOS, nil
			}
			if strings.HasPrefix(line, "PRETTY_NAME=\"Amazon Linux 2") {
				return DistributionCentos7, nil
			}
		}
		klog.Warningf("unhandled /etc/os-release info %q", string(osRelease))
	} else if !os.IsNotExist(err) {
		klog.Warningf("error reading /etc/os-release: %v", err)
	}

	klog.Warningf("could not determine distro")
	klog.Warningf("  /etc/lsb-release: %q", string(lsbRelease))
	klog.Warningf("  /etc/debian_version: %q", string(debianVersionBytes))
	klog.Warningf("  /etc/redhat-release: %q", string(redhatRelease))
	klog.Warningf("  /usr/lib/os-release: %q", string(usrLibOsRelease))
	klog.Warningf("  /etc/os-release: %q", string(osRelease))

	return "", fmt.Errorf("cannot identify distro")
}
