Add support for DCOS host detection, improve Docker detection. (#4525)

isDocker was currently reading from `/proc/cgroup` file. But
this file alone is rather not conclusive evidence. Docker
internally has `.dockerenv` as a special file which we should
use instead.

Fixes #4456
This commit is contained in:
Harshavardhana 2017-06-12 17:33:21 -07:00 committed by Dee Koder
parent 6f4862659f
commit b8463a738c
3 changed files with 122 additions and 53 deletions

View file

@ -138,11 +138,11 @@ func newApp(name string) *cli.App {
// Main main for minio server.
func Main(args []string) {
name := filepath.Base(args[0])
app := newApp(name)
// Set the minio app name.
appName := filepath.Base(args[0])
// Run the app - exit on error.
if err := app.Run(args); err != nil {
if err := newApp(appName).Run(args); err != nil {
os.Exit(1)
}
}

View file

@ -17,7 +17,6 @@
package cmd
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
@ -98,26 +97,41 @@ func GetCurrentReleaseTime() (releaseTime time.Time, err error) {
return getCurrentReleaseTime(Version, os.Args[0])
}
func isDocker(cgroupFile string) (bool, error) {
cgroup, err := ioutil.ReadFile(cgroupFile)
if os.IsNotExist(err) {
err = nil
// Check if we are indeed inside docker.
// https://github.com/moby/moby/blob/master/daemon/initlayer/setup_unix.go#L25
//
// "/.dockerenv": "file",
//
func isDocker(dockerEnvFile string) (ok bool, err error) {
_, err = os.Stat(dockerEnvFile)
if err != nil {
if os.IsNotExist(err) {
err = nil
}
return false, err
}
return bytes.Contains(cgroup, []byte("docker")), err
return true, nil
}
// IsDocker - returns if the environment minio is running
// is docker or not.
func IsDocker() bool {
found, err := isDocker("/proc/self/cgroup")
fatalIf(err, "Error in docker check.")
found, err := isDocker("/.dockerenv")
// We don't need to fail for this check, log
// an error and return false.
errorIf(err, "Error in docker check.")
return found
}
// IsKubernetes returns if the environment minio is
// running is kubernetes or not.
// IsDCOS returns true if minio is running in DCOS.
func IsDCOS() bool {
// http://mesos.apache.org/documentation/latest/docker-containerizer/
// Mesos docker containerizer sets this value
return os.Getenv("MESOS_CONTAINER_NAME") != ""
}
// IsKubernetes returns true if minio is running in kubernetes.
func IsKubernetes() bool {
// Kubernetes env used to validate if we are
// indeed running inside a kubernetes pod
@ -139,7 +153,7 @@ func IsSourceBuild() bool {
// DO NOT CHANGE USER AGENT STYLE.
// The style should be
//
// Minio (<OS>; <ARCH>[; kubernetes][; docker][; source]) Minio/<VERSION> Minio/<RELEASE-TAG> Minio/<COMMIT-ID>
// Minio (<OS>; <ARCH>[; dcos][; kubernetes][; docker][; source]) Minio/<VERSION> Minio/<RELEASE-TAG> Minio/<COMMIT-ID> [Minio/univers-<PACKAGE_NAME>]
//
// For any change here should be discussed by openning an issue at https://github.com/minio/minio/issues.
func getUserAgent(mode string) string {
@ -147,6 +161,9 @@ func getUserAgent(mode string) string {
if mode != "" {
userAgent += "; " + mode
}
if IsDCOS() {
userAgent += "; dcos"
}
if IsKubernetes() {
userAgent += "; kubernetes"
}
@ -156,8 +173,15 @@ func getUserAgent(mode string) string {
if IsSourceBuild() {
userAgent += "; source"
}
userAgent += ") " + " Minio/" + Version + " Minio/" + ReleaseTag + " Minio/" + CommitID
userAgent += ") Minio/" + Version + " Minio/" + ReleaseTag + " Minio/" + CommitID
if IsDCOS() {
universePkgVersion := os.Getenv("MARATHON_APP_LABEL_DCOS_PACKAGE_VERSION")
// On DC/OS environment try to the get universe package version.
if universePkgVersion != "" {
userAgent += " Minio/" + "universe-" + universePkgVersion
}
}
return userAgent
}
@ -241,10 +265,21 @@ func getLatestReleaseTime(timeout time.Duration, mode string) (releaseTime time.
return parseReleaseData(data)
}
// Kubernetes deploy doc link.
const kubernetesDeploymentDoc = "https://docs.minio.io/docs/deploy-minio-on-kubernetes"
const (
// Kubernetes deployment doc link.
kubernetesDeploymentDoc = "https://docs.minio.io/docs/deploy-minio-on-kubernetes"
// Mesos deployment doc link.
mesosDeploymentDoc = "https://docs.minio.io/docs/deploy-minio-on-dc-os"
)
func getDownloadURL(buildDate time.Time) (downloadURL string) {
// Check if we are in DCOS environment, return
// deployment guide for update procedures.
if IsDCOS() {
return mesosDeploymentDoc
}
// Check if we are in kubernetes environment, return
// deployment guide for update procedures.
if IsKubernetes() {

View file

@ -44,12 +44,18 @@ func TestDownloadURL(t *testing.T) {
}
os.Setenv("KUBERNETES_SERVICE_HOST", "10.11.148.5")
defer os.Unsetenv("KUBERNETES_SERVICE_HOST")
durl = getDownloadURL(minioVersion1)
if durl != kubernetesDeploymentDoc {
t.Errorf("Expected %s, got %s", kubernetesDeploymentDoc, durl)
}
os.Unsetenv("KUBERNETES_SERVICE_HOST")
os.Setenv("MESOS_CONTAINER_NAME", "mesos-1111")
durl = getDownloadURL(minioVersion1)
if durl != mesosDeploymentDoc {
t.Errorf("Expected %s, got %s", mesosDeploymentDoc, durl)
}
os.Unsetenv("MESOS_CONTAINER_NAME")
}
func TestGetCurrentReleaseTime(t *testing.T) {
@ -158,6 +164,63 @@ func TestGetCurrentReleaseTime(t *testing.T) {
}
}
// Tests user agent string.
func TestUserAgent(t *testing.T) {
testCases := []struct {
envName string
envValue string
mode string
expectedStr string
}{
{
envName: "",
envValue: "",
mode: globalMinioModeFS,
expectedStr: fmt.Sprintf("Minio (%s; %s; %s; source) Minio/DEVELOPMENT.GOGET Minio/DEVELOPMENT.GOGET Minio/DEVELOPMENT.GOGET", runtime.GOOS, runtime.GOARCH, globalMinioModeFS),
},
{
envName: "MESOS_CONTAINER_NAME",
envValue: "mesos-11111",
mode: globalMinioModeXL,
expectedStr: fmt.Sprintf("Minio (%s; %s; %s; %s; source) Minio/DEVELOPMENT.GOGET Minio/DEVELOPMENT.GOGET Minio/DEVELOPMENT.GOGET Minio/universe-%s", runtime.GOOS, runtime.GOARCH, globalMinioModeXL, "dcos", "mesos-1111"),
},
{
envName: "KUBERNETES_SERVICE_HOST",
envValue: "10.11.148.5",
mode: globalMinioModeXL,
expectedStr: fmt.Sprintf("Minio (%s; %s; %s; %s; source) Minio/DEVELOPMENT.GOGET Minio/DEVELOPMENT.GOGET Minio/DEVELOPMENT.GOGET", runtime.GOOS, runtime.GOARCH, globalMinioModeXL, "kubernetes"),
},
}
for i, testCase := range testCases {
os.Setenv(testCase.envName, testCase.envValue)
if testCase.envName == "MESOS_CONTAINER_NAME" {
os.Setenv("MARATHON_APP_LABEL_DCOS_PACKAGE_VERSION", "mesos-1111")
}
str := getUserAgent(testCase.mode)
if str != testCase.expectedStr {
t.Errorf("Test %d: expected: %s, got: %s", i+1, testCase.expectedStr, str)
}
os.Unsetenv("MARATHON_APP_LABEL_DCOS_PACKAGE_VERSION")
os.Unsetenv(testCase.envName)
}
}
// Tests if the environment we are running is in DCOS.
func TestIsDCOS(t *testing.T) {
os.Setenv("MESOS_CONTAINER_NAME", "mesos-1111")
dcos := IsDCOS()
if !dcos {
t.Fatalf("Expected %t, got %t", true, dcos)
}
os.Unsetenv("MESOS_CONTAINER_NAME")
dcos = IsDCOS()
if dcos {
t.Fatalf("Expected %t, got %t", false, dcos)
}
}
// Tests if the environment we are running is in kubernetes.
func TestIsKubernetes(t *testing.T) {
os.Setenv("KUBERNETES_SERVICE_HOST", "10.11.148.5")
@ -188,36 +251,8 @@ func TestIsDocker(t *testing.T) {
return tmpfile.Name()
}
filename1 := createTempFile(`11:pids:/user.slice/user-1000.slice/user@1000.service
10:blkio:/
9:hugetlb:/
8:perf_event:/
7:cpuset:/
6:devices:/user.slice
5:net_cls,net_prio:/
4:cpu,cpuacct:/
3:memory:/user/bala/0
2:freezer:/user/bala/0
1:name=systemd:/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service
`)
defer os.Remove(filename1)
filename2 := createTempFile(`14:name=systemd:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
13:pids:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
12:hugetlb:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
11:net_prio:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
10:perf_event:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
9:net_cls:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
8:freezer:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
7:devices:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
6:memory:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
5:blkio:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
4:cpuacct:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
3:cpu:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
2:cpuset:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
1:name=openrc:/docker
`)
defer os.Remove(filename2)
filename := createTempFile("")
defer os.Remove(filename)
testCases := []struct {
filename string
@ -226,8 +261,7 @@ func TestIsDocker(t *testing.T) {
}{
{"", false, nil},
{"/tmp/non-existing-file", false, nil},
{filename1, false, nil},
{filename2, true, nil},
{filename, true, nil},
}
if runtime.GOOS == "linux" {
@ -235,7 +269,7 @@ func TestIsDocker(t *testing.T) {
filename string
expectedResult bool
expectedErr error
}{"/proc/1/cwd", false, fmt.Errorf("open /proc/1/cwd: permission denied")})
}{"/proc/1/cwd", false, errors.New("stat /proc/1/cwd: permission denied")})
}
for _, testCase := range testCases {