// +build ignore /* * MinIO Cloud Storage, (C) 2019 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 main import ( "bufio" "crypto/tls" "errors" "fmt" "log" "net/http" "net/url" "os" "os/exec" "strings" "time" xhttp "github.com/minio/minio/cmd/http" ) const ( initGraceTime = 300 healthPath = "/minio/health/live" timeout = time.Duration(30 * time.Second) tcp = "tcp" anyIPv6 = ":::" anyIPv4 = "0.0.0.0" ) // returns container boot time by finding // modtime of /proc/1 directory func getStartTime() time.Time { di, err := os.Stat("/proc/1") if err != nil { // Cant stat proc dir successfully, exit with error log.Fatalln(err) } return di.ModTime() } // Returns the ip:port of the MinIO process // running in the container func findEndpoint() (string, error) { cmd := exec.Command("netstat", "-ntlp") stdout, err := cmd.StdoutPipe() if err != nil { // error getting stdout pipe return "", err } if err = cmd.Start(); err != nil { // error executing the command. return "", err } // split netstat output in rows scanner := bufio.NewScanner(stdout) scanner.Split(bufio.ScanLines) // MinIO works on TCP and it is supposed to be // the only process listening on a port on any IP address // (on ::: or 0.0.0.0) inside container. // Since MinIO may run as non-root user, we can // not depend on the PID/Program name column // of netstat output for scanner.Scan() { line := scanner.Text() if strings.Contains(line, tcp) && (strings.Contains(line, anyIPv6) || strings.Contains(line, anyIPv4)) { newLine := strings.Replace(line, anyIPv4, "127.0.0.1:", 1) if strings.Contains(line, anyIPv6) { newLine = strings.Replace(line, anyIPv6, "::1:", 1) } fields := strings.Fields(newLine) // index 3 in the row has the Local address // find the last index of ":" - address will // have port number after this index i := strings.LastIndex(fields[3], ":") // split address and port addr := fields[3][:i] port := fields[3][i+1:] // add surrounding [] for ip6 address if strings.Count(addr, ":") > 0 { addr = strings.Join([]string{"[", addr, "]"}, "") } // wait for cmd to complete before return if err = cmd.Wait(); err != nil { return "", err } // return joint address and port return strings.Join([]string{addr, port}, ":"), nil } } if err = scanner.Err(); err != nil { return "", err } if err = cmd.Wait(); err != nil { // command failed to run return "", err } // minio process not found, exit with error return "", errors.New("no minio process found") } func main() { startTime := getStartTime() // In distributed environment like Swarm, traffic is routed // to a container only when it reports a `healthy` status. So, we exit // with 0 to ensure healthy status till distributed MinIO starts (120s). // Refer: https://github.com/moby/moby/pull/28938#issuecomment-301753272 if (time.Now().Sub(startTime) / time.Second) > initGraceTime { endPoint, err := findEndpoint() if err != nil { log.Fatalln(err) } u, err := url.Parse(fmt.Sprintf("http://%s%s", endPoint, healthPath)) if err != nil { // Could not parse URL successfully log.Fatalln(err) } // MinIO server may be using self-signed or CA certificates. To avoid // making Docker setup complicated, we skip verifying certificates here. // This is because, following request tests for health status within // containerized environment, i.e. requests are always made to the MinIO // server running on the same host. tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := &http.Client{Transport: tr, Timeout: timeout} resp, err := client.Get(u.String()) if err != nil { // GET failed exit log.Fatalln(err) } if resp.StatusCode == http.StatusOK { // Drain any response. xhttp.DrainBody(resp.Body) // exit with success os.Exit(0) } // Drain any response. xhttp.DrainBody(resp.Body) // 400 response may mean sever is configured with https if resp.StatusCode == http.StatusBadRequest { // Try with https u.Scheme = "https" resp, err = client.Get(u.String()) if err != nil { // GET failed exit log.Fatalln(err) } if resp.StatusCode == http.StatusOK { // Drain any response. xhttp.DrainBody(resp.Body) // exit with success os.Exit(0) } // Drain any response. xhttp.DrainBody(resp.Body) } // Execution reaching here means none of // the success cases were satisfied os.Exit(1) } os.Exit(0) }