func releaseInfoSource() (distroReleaseInfo, error) {
	// LSB
	if ok, err := util.PathExists(lsbReleasePath); err != nil {
		return nil, err
	} else if ok {
		return lsbReleaseInfo{}, nil
	}

	// RedHat/CentOS. Checking for CentOS first as CentOS contains
	// both centos-release and redhat-release and this path-existence
	// check makes it look like RHEL.
	if ok, err := util.PathExists(centosReleasePath); err != nil {
		return nil, err
	} else if ok {
		return centosReleaseInfo{centosReleasePath, CentosID}, nil
	}
	if ok, err := util.PathExists(redhatReleasePath); err != nil {
		return nil, err
	} else if ok {
		return centosReleaseInfo{redhatReleasePath, RhelID}, nil
	}

	// Unknown
	return nil, fmt.Errorf("could not determine distro")
}
// rewriteOpts uses the specified dockeropts editor to modify the existing cfgFile
// (if not exists, it creates the cfg file and its directory) with specified args.
// If nothing is changed, this will return false.
func rewriteOpts(e dockeropts.Editor, cfgFile string, args string) (restartNeeded bool, err error) {
	in, err := ioutil.ReadFile(cfgFile)
	if err != nil {
		return false, fmt.Errorf("error reading %s: %v", cfgFile, err)
	}

	out, err := e.ChangeOpts(string(in), args)
	if err != nil {
		return false, fmt.Errorf("error updating settings at %s: %v", cfgFile, err)
	}

	// check if existing config file needs an update
	if ok, _ := util.PathExists(cfgFile); ok {
		existing, err := ioutil.ReadFile(cfgFile)
		if err != nil {
			return false, fmt.Errorf("error reading %s: %v", cfgFile, err)
		}

		// no need to update config or restart service if goal config is already there
		if string(existing) == out {
			return false, nil
		}
	}

	if err := ioutil.WriteFile(cfgFile, []byte(out), 0644); err != nil {
		return false, fmt.Errorf("error writing to %s: %v", cfgFile, err)
	}
	return true, nil
}
func (c CoreOSDriver) UpdateDockerArgs(args string) (bool, error) {
	const dropInDir = "/run/systemd/system/docker.service.d"
	const dropInFile = "10-docker-extension.conf"
	filePath := filepath.Join(dropInDir, dropInFile)

	config := fmt.Sprintf(`[Service]
Environment="DOCKER_OPTS=%s"`, args)

	// check if config file exists and needs an update
	if ok, _ := util.PathExists(filePath); ok {
		existing, err := ioutil.ReadFile(filePath)
		if err != nil {
			return false, fmt.Errorf("error reading %s: %v", filePath, err)
		}

		// no need to update config or restart service if goal config is already there
		if string(existing) == config {
			return false, nil
		}
	}

	if err := os.MkdirAll(dropInDir, 0755); err != nil {
		return false, fmt.Errorf("error creating %s dir: %v", dropInDir, err)
	}
	err := ioutil.WriteFile(filePath, []byte(config), 0644)
	log.Println("Written systemd service drop-in to disk.")
	return true, err
}
// installCompose download docker-compose and saves to the specified path if it
// is not already installed.
func installCompose(path string) error {
	// Check if already installed at path.
	if ok, err := util.PathExists(path); err != nil {
		return err
	} else if ok {
		log.Printf("docker-compose is already installed at %s", path)
		return nil
	}

	// Create dir if not exists
	dir := filepath.Dir(path)
	ok, err := util.PathExists(dir)
	if err != nil {
		return err
	} else if !ok {
		if err := os.MkdirAll(dir, 755); err != nil {
			return err
		}
	}

	log.Printf("Downloading compose from %s", composeUrl)
	resp, err := http.Get(composeUrl)
	if err != nil {
		return fmt.Errorf("error downloading docker-compose: %v", err)
	}
	if resp.StatusCode/100 != 2 {
		return fmt.Errorf("response status code from %s: %s", composeUrl, resp.Status)
	}
	defer resp.Body.Close()

	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0777)
	if err != nil {
		return fmt.Errorf("error creating %s: %v", path, err)
	}

	defer f.Close()
	if _, err := io.Copy(f, resp.Body); err != nil {
		return fmt.Errorf("failed to save response body to %s: %v", path, err)
	}
	return nil
}
// installDockerCerts saves the configured certs to the specified dir
// if and only if the certs are not already placed there. If no certs
// are provided  or some certs already exist, nothing is written.
func installDockerCerts(s DockerHandlerSettings, dstDir string) error {
	m := []struct {
		src string
		dst string
	}{
		{s.Certs.CABase64, filepath.Join(dstDir, dockerCaCert)},
		{s.Certs.ServerCertBase64, filepath.Join(dstDir, dockerSrvCert)},
		{s.Certs.ServerKeyBase64, filepath.Join(dstDir, dockerSrvKey)},
	}

	// Check if certs are provided
	for _, v := range m {
		if len(v.src) == 0 {
			log.Printf("Docker certificate %s is not provided in the extension settings, skipping docker certs installation", v.dst)
			return nil
		}
	}

	// Check the target directory, if not create
	if ok, err := util.PathExists(dstDir); err != nil {
		return fmt.Errorf("error checking cert dir: %v", err)
	} else if !ok {
		if err := os.MkdirAll(dstDir, 0755); err != nil {
			return err
		}
	}

	// Write the certs
	for _, v := range m {
		// Decode base64
		in := strings.TrimSpace(v.src)
		f, err := base64.StdEncoding.DecodeString(in)
		if err != nil {
			// Fallback to original file input
			f = []byte(in)
		}

		if err := ioutil.WriteFile(v.dst, f, 0600); err != nil {
			return fmt.Errorf("error writing certificate: %v", err)
		}
	}
	return nil
}