You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
5.0 KiB
181 lines
5.0 KiB
// +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)
|
|
}
|
|
|