diff --git a/Dockerfile b/Dockerfile index 7de359693..5301e0ce5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,17 @@ FROM golang:1.7-alpine WORKDIR /go/src/app -ENV ALLOW_CONTAINER_ROOT=1 COPY . /go/src/app RUN \ apk add --no-cache git && \ go-wrapper download && \ - go-wrapper install && \ + go-wrapper install -ldflags "$(go run buildscripts/gen-ldflags.go)" && \ mkdir -p /export/docker && \ cp /go/src/app/docs/Docker.md /export/docker/ && \ rm -rf /go/pkg /go/src && \ apk del git EXPOSE 9000 -ENTRYPOINT ["go-wrapper", "run", "server"] +ENTRYPOINT ["go-wrapper", "run"] VOLUME ["/export"] -CMD ["/export"] diff --git a/README.md b/README.md index 117fcd9a5..1d414ebf0 100644 --- a/README.md +++ b/README.md @@ -35,107 +35,47 @@ $ go get -u github.com/minio/minio In the examples below, Minio serves the contents of the ``Photos`` directory as an object store. ### Docker Container + ```sh $ docker pull minio/minio -$ docker run -p 9000:9000 minio/minio ~/Photos - +$ docker run -p 9000:9000 minio/minio server /export ``` + Please visit Minio Docker quickstart guide for more [here](https://docs.minio.io/docs/minio-docker-quickstart-guide) ### GNU/Linux - ```sh + +```sh $ chmod +x minio $ ./minio --help $ ./minio server ~/Photos - -Endpoint: http://10.0.0.10:9000 http://127.0.0.1:9000 http://172.17.0.1:9000 -AccessKey: USWUXHGYZQYFYFFIT3RE -SecretKey: MOJRH0mkL1IPauahWITSVvyDrQbEEIwljvmxdq03 -Region: us-east-1 - -Browser Access: - http://10.0.0.10:9000 http://127.0.0.1:9000 http://172.17.0.1:9000 - -Command-line Access: https://docs.minio.io/docs/minio-client-quickstart-guide - $ mc config host add myminio http://10.0.0.10:9000 USWUXHGYZQYFYFFIT3RE MOJRH0mkL1IPauahWITSVvyDrQbEEIwljvmxdq03 - -Object API (Amazon S3 compatible): - Go: https://docs.minio.io/docs/golang-client-quickstart-guide - Java: https://docs.minio.io/docs/java-client-quickstart-guide - Python: https://docs.minio.io/docs/python-client-quickstart-guide - JavaScript: https://docs.minio.io/docs/javascript-client-quickstart-guide - +... ``` ### OS X - ```sh + +```sh $ chmod 755 minio $ ./minio --help $ ./minio server ~/Photos - -Endpoint: http://10.0.0.10:9000 http://127.0.0.1:9000 http://172.17.0.1:9000 -AccessKey: USWUXHGYZQYFYFFIT3RE -SecretKey: MOJRH0mkL1IPauahWITSVvyDrQbEEIwljvmxdq03 -Region: us-east-1 - -Browser Access: - http://10.0.0.10:9000 http://127.0.0.1:9000 http://172.17.0.1:9000 - -Command-line Access: https://docs.minio.io/docs/minio-client-quickstart-guide - $ mc config host add myminio http://10.0.0.10:9000 USWUXHGYZQYFYFFIT3RE MOJRH0mkL1IPauahWITSVvyDrQbEEIwljvmxdq03 - -Object API (Amazon S3 compatible): - Go: https://docs.minio.io/docs/golang-client-quickstart-guide - Java: https://docs.minio.io/docs/java-client-quickstart-guide - Python: https://docs.minio.io/docs/python-client-quickstart-guide - JavaScript: https://docs.minio.io/docs/javascript-client-quickstart-guide +... ``` ### Microsoft Windows + ```sh C:\Users\Username\Downloads> minio.exe --help C:\Users\Username\Downloads> minio.exe server D:\Photos - -Endpoint: http://10.0.0.10:9000 http://127.0.0.1:9000 http://172.17.0.1:9000 -AccessKey: USWUXHGYZQYFYFFIT3RE -SecretKey: MOJRH0mkL1IPauahWITSVvyDrQbEEIwljvmxdq03 -Region: us-east-1 - -Browser Access: - http://10.0.0.10:9000 http://127.0.0.1:9000 http://172.17.0.1:9000 - -Command-line Access: https://docs.minio.io/docs/minio-client-quickstart-guide - $ mc.exe config host add myminio http://10.0.0.10:9000 USWUXHGYZQYFYFFIT3RE MOJRH0mkL1IPauahWITSVvyDrQbEEIwljvmxdq03 - -Object API (Amazon S3 compatible): - Go: https://docs.minio.io/docs/golang-client-quickstart-guide - Java: https://docs.minio.io/docs/java-client-quickstart-guide - Python: https://docs.minio.io/docs/python-client-quickstart-guide - JavaScript: https://docs.minio.io/docs/javascript-client-quickstart-guide +... ``` ### FreeBSD + ```sh $ chmod 755 minio $ ./minio --help $ ./minio server ~/Photos - -Endpoint: http://10.0.0.10:9000 http://127.0.0.1:9000 http://172.17.0.1:9000 -AccessKey: USWUXHGYZQYFYFFIT3RE -SecretKey: MOJRH0mkL1IPauahWITSVvyDrQbEEIwljvmxdq03 -Region: us-east-1 - -Browser Access: - http://10.0.0.10:9000 http://127.0.0.1:9000 http://172.17.0.1:9000 - -Command-line Access: https://docs.minio.io/docs/minio-client-quickstart-guide - $ mc config host add myminio http://10.0.0.10:9000 USWUXHGYZQYFYFFIT3RE MOJRH0mkL1IPauahWITSVvyDrQbEEIwljvmxdq03 - -Object API (Amazon S3 compatible): - Go: https://docs.minio.io/docs/golang-client-quickstart-guide - Java: https://docs.minio.io/docs/java-client-quickstart-guide - Python: https://docs.minio.io/docs/python-client-quickstart-guide - JavaScript: https://docs.minio.io/docs/javascript-client-quickstart-guide +... ``` Please visit official zfs FreeBSD guide for more details [here](https://www.freebsd.org/doc/handbook/zfs-quickstart.html) @@ -164,6 +104,5 @@ For more examples please navigate to [Minio Client Complete Guide](https://docs. - [Use `s3cmd` with Minio Server](https://docs.minio.io/docs/s3cmd-with-minio) - [Use `minio-go` SDK with Minio Server](https://docs.minio.io/docs/golang-client-quickstart-guide) - ## 6. Contribute to Minio Project Please follow Minio [Contributor's Guide](https://github.com/minio/minio/blob/master/CONTRIBUTING.md) diff --git a/cmd/main.go b/cmd/main.go index e607dce40..a5571479d 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -22,6 +22,7 @@ import ( "os" "sort" "strings" + "time" "github.com/minio/cli" "github.com/minio/mc/pkg/console" @@ -156,7 +157,6 @@ func checkMainSyntax(c *cli.Context) { func Main() { app := registerApp() app.Before = func(c *cli.Context) error { - configDir := c.GlobalString("config-dir") if configDir == "" { fatalIf(errors.New("Config directory is empty"), "Unable to get config file.") @@ -185,11 +185,11 @@ func Main() { // Do not print update messages, if quiet flag is set. if !globalQuiet { - if strings.HasPrefix(Version, "RELEASE.") { - updateMsg, _, err := getReleaseUpdate(minioUpdateStableURL) + if strings.HasPrefix(ReleaseTag, "RELEASE.") && c.Args().Get(0) != "update" { + updateMsg, _, err := getReleaseUpdate(minioUpdateStableURL, 1*time.Second) if err != nil { - // Ignore any errors during getReleaseUpdate() because - // the internet might not be available. + // Ignore any errors during getReleaseUpdate(), possibly + // because of network errors. return nil } console.Println(updateMsg) diff --git a/cmd/update-main.go b/cmd/update-main.go index bebf1bd2f..f8983aa98 100644 --- a/cmd/update-main.go +++ b/cmd/update-main.go @@ -17,10 +17,13 @@ package cmd import ( + "bytes" "encoding/json" "errors" + "fmt" "io/ioutil" "net/http" + "os" "runtime" "strings" "time" @@ -110,7 +113,8 @@ func parseReleaseData(data string) (time.Time, error) { if releaseDateSplits[0] != "minio" { return time.Time{}, (errors.New("Update data malformed, missing minio tag")) } - // "OFFICIAL" tag is still kept for backward compatibility, we should remove this for the next release. + // "OFFICIAL" tag is still kept for backward compatibility. + // We should remove this for the next release. if releaseDateSplits[1] != "RELEASE" && releaseDateSplits[1] != "OFFICIAL" { return time.Time{}, (errors.New("Update data malformed, missing RELEASE tag")) } @@ -128,8 +132,28 @@ func parseReleaseData(data string) (time.Time, error) { return parsedDate, nil } +// User Agent should always following the below style. +// Please open an issue to discuss any new changes here. +// +// Minio (OS; ARCH) APP/VER APP/VER +var ( + userAgentSuffix = "Minio/" + Version + " " + "Minio/" + ReleaseTag + " " + "Minio/" + CommitID + userAgentPrefix = "Minio (" + runtime.GOOS + "; " + runtime.GOARCH + ") " + userAgent = userAgentPrefix + userAgentSuffix +) + +// Check if the operating system is a docker container. +func isDocker() bool { + cgroup, err := ioutil.ReadFile("/proc/self/cgroup") + if err != nil && os.IsNotExist(err) { + return false + } + fatalIf(err, "Unable to read `cgroup` file.") + return bytes.Contains(cgroup, []byte("docker")) +} + // verify updates for releases. -func getReleaseUpdate(updateURL string) (updateMsg updateMessage, errMsg string, err error) { +func getReleaseUpdate(updateURL string, duration time.Duration) (updateMsg updateMessage, errMsg string, err error) { // Construct a new update url. newUpdateURLPrefix := updateURL + "/" + runtime.GOOS + "-" + runtime.GOARCH newUpdateURL := newUpdateURLPrefix + "/minio.shasum" @@ -153,7 +177,7 @@ func getReleaseUpdate(updateURL string) (updateMsg updateMessage, errMsg string, // Instantiate a new client with 3 sec timeout. client := &http.Client{ - Timeout: 3 * time.Second, + Timeout: duration, } // Parse current minio version into RFC3339. @@ -170,23 +194,32 @@ func getReleaseUpdate(updateURL string) (updateMsg updateMessage, errMsg string, return } + // Initialize new request. + req, err := http.NewRequest("GET", newUpdateURL, nil) + if err != nil { + return + } + + // Set user agent. + req.Header.Set("User-Agent", userAgent+" "+fmt.Sprintf("Docker/%t", isDocker())) + // Fetch new update. - data, err := client.Get(newUpdateURL) + resp, err := client.Do(req) if err != nil { return } // Verify if we have a valid http response i.e http.StatusOK. - if data != nil { - if data.StatusCode != http.StatusOK { + if resp != nil { + if resp.StatusCode != http.StatusOK { errMsg = "Failed to retrieve update notice." - err = errors.New("http status : " + data.Status) + err = errors.New("http status : " + resp.Status) return } } // Read the response body. - updateBody, err := ioutil.ReadAll(data.Body) + updateBody, err := ioutil.ReadAll(resp.Body) if err != nil { errMsg = "Failed to retrieve update notice. Please try again later." return @@ -226,10 +259,11 @@ func mainUpdate(ctx *cli.Context) { var updateMsg updateMessage var errMsg string var err error + var secs = time.Second * 3 if ctx.Bool("experimental") { - updateMsg, errMsg, err = getReleaseUpdate(minioUpdateExperimentalURL) + updateMsg, errMsg, err = getReleaseUpdate(minioUpdateExperimentalURL, secs) } else { - updateMsg, errMsg, err = getReleaseUpdate(minioUpdateStableURL) + updateMsg, errMsg, err = getReleaseUpdate(minioUpdateStableURL, secs) } fatalIf(err, errMsg) console.Println(updateMsg) diff --git a/cmd/update-main_nix_test.go b/cmd/update-main_nix_test.go new file mode 100644 index 000000000..885fc5b58 --- /dev/null +++ b/cmd/update-main_nix_test.go @@ -0,0 +1,112 @@ +// +build linux darwin dragonfly freebsd netbsd openbsd + +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * 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 cmd + +import ( + "fmt" + "net/http" + "net/http/httptest" + "reflect" + "runtime" + "testing" + "time" +) + +// Validate when release versions are properly set. +func TestReleaseUpdateVersion(t *testing.T) { + Version = "2016-10-06T00:08:32Z" + ReleaseTag = "RELEASE.2016-10-06T00-08-32Z" + CommitID = "d1c38ba8f0b3aecdf9b932c087dd65c21eebac33" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "fbe246edbd382902db9a4035df7dce8cb441357d minio.RELEASE.2016-10-07T01-16-39Z") + })) + userAgentSuffix = "Minio/" + Version + " " + "Minio/" + ReleaseTag + " " + "Minio/" + CommitID + userAgentPrefix = "Minio (" + runtime.GOOS + "; " + runtime.GOARCH + ") " + userAgent = userAgentPrefix + userAgentSuffix + defer ts.Close() + testCases := []struct { + updateURL string + updateMsg updateMessage + errMsg string + shouldPass bool + }{ + { + updateURL: ts.URL, + updateMsg: updateMessage{ + Download: ts.URL + "/" + runtime.GOOS + "-" + runtime.GOARCH + "/minio", + Version: "2016-10-06T00:08:32Z", + Update: true, + }, + errMsg: "", + shouldPass: true, + }, + } + + // Validates all the errors reported. + for i, testCase := range testCases { + updateMsg, errMsg, err := getReleaseUpdate(testCase.updateURL, time.Second*1) + if testCase.shouldPass && err != nil { + t.Errorf("Test %d: Unable to fetch release update %s", i+1, err) + } + if errMsg != testCase.errMsg { + t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.errMsg, errMsg) + } + if !reflect.DeepEqual(updateMsg, testCase.updateMsg) { + t.Errorf("Test %d: Expected %#v, got %#v", i+1, testCase.updateMsg, updateMsg) + } + } +} + +func TestReleaseUpdate(t *testing.T) { + Version = "DEVELOPMENT.GOGET" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, client") + })) + defer ts.Close() + testCases := []struct { + updateURL string + updateMsg updateMessage + errMsg string + shouldPass bool + }{ + { + updateURL: ts.URL, + updateMsg: updateMessage{ + Download: ts.URL + "/" + runtime.GOOS + "-" + runtime.GOARCH + "/minio", + Version: "DEVELOPMENT.GOGET", + }, + errMsg: "Unable to parse version string as time.", + shouldPass: false, + }, + } + + // Validates all the errors reported. + for i, testCase := range testCases { + updateMsg, errMsg, err := getReleaseUpdate(testCase.updateURL, time.Second*1) + if testCase.shouldPass && err != nil { + t.Errorf("Test %d: Unable to fetch release update %s", i+1, err) + } + if errMsg != testCase.errMsg { + t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.errMsg, errMsg) + } + if !reflect.DeepEqual(updateMsg, testCase.updateMsg) { + t.Errorf("Test %d: Expected %#v, got %#v", i+1, testCase.updateMsg, updateMsg) + } + } +} diff --git a/cmd/update-main_windows_test.go b/cmd/update-main_windows_test.go new file mode 100644 index 000000000..32d213af4 --- /dev/null +++ b/cmd/update-main_windows_test.go @@ -0,0 +1,112 @@ +// +build windows + +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * 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 cmd + +import ( + "fmt" + "net/http" + "net/http/httptest" + "reflect" + "runtime" + "testing" + "time" +) + +// Validate when release versions are properly set. +func TestReleaseUpdateVersion(t *testing.T) { + Version = "2016-10-06T00:08:32Z" + ReleaseTag = "RELEASE.2016-10-06T00-08-32Z" + CommitID = "d1c38ba8f0b3aecdf9b932c087dd65c21eebac33" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "fbe246edbd382902db9a4035df7dce8cb441357d minio.RELEASE.2016-10-07T01-16-39Z") + })) + userAgentSuffix = "Minio/" + Version + " " + "Minio/" + ReleaseTag + " " + "Minio/" + CommitID + userAgentPrefix = "Minio (" + runtime.GOOS + "; " + runtime.GOARCH + ") " + userAgent = userAgentPrefix + userAgentSuffix + defer ts.Close() + testCases := []struct { + updateURL string + updateMsg updateMessage + errMsg string + shouldPass bool + }{ + { + updateURL: ts.URL, + updateMsg: updateMessage{ + Download: ts.URL + "/" + runtime.GOOS + "-" + runtime.GOARCH + "/minio.exe", + Version: "2016-10-06T00:08:32Z", + Update: true, + }, + errMsg: "", + shouldPass: true, + }, + } + + // Validates all the errors reported. + for i, testCase := range testCases { + updateMsg, errMsg, err := getReleaseUpdate(testCase.updateURL, time.Second*1) + if testCase.shouldPass && err != nil { + t.Errorf("Test %d: Unable to fetch release update %s", i+1, err) + } + if errMsg != testCase.errMsg { + t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.errMsg, errMsg) + } + if !reflect.DeepEqual(updateMsg, testCase.updateMsg) { + t.Errorf("Test %d: Expected %#v, got %#v", i+1, testCase.updateMsg, updateMsg) + } + } +} + +func TestReleaseUpdate(t *testing.T) { + Version = "DEVELOPMENT.GOGET" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, client") + })) + defer ts.Close() + testCases := []struct { + updateURL string + updateMsg updateMessage + errMsg string + shouldPass bool + }{ + { + updateURL: ts.URL, + updateMsg: updateMessage{ + Download: ts.URL + "/" + runtime.GOOS + "-" + runtime.GOARCH + "/minio.exe", + Version: "DEVELOPMENT.GOGET", + }, + errMsg: "Unable to parse version string as time.", + shouldPass: false, + }, + } + + // Validates all the errors reported. + for i, testCase := range testCases { + updateMsg, errMsg, err := getReleaseUpdate(testCase.updateURL, time.Second*1) + if testCase.shouldPass && err != nil { + t.Errorf("Test %d: Unable to fetch release update %s", i+1, err) + } + if errMsg != testCase.errMsg { + t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.errMsg, errMsg) + } + if !reflect.DeepEqual(updateMsg, testCase.updateMsg) { + t.Errorf("Test %d: Expected %#v, got %#v", i+1, testCase.updateMsg, updateMsg) + } + } +} diff --git a/docs/Docker.md b/docs/Docker.md index c03ca3c0f..27824079a 100644 --- a/docs/Docker.md +++ b/docs/Docker.md @@ -1,6 +1,5 @@ # Minio Docker Quickstart Guide [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/minio/minio?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - ## 1. Test Minio Docker Container Minio generates new access and secret keys each time you run this command. Container state is lost after you end this session. This mode is only intended for testing purpose.