diff --git a/cmd/erasure-zones.go b/cmd/erasure-zones.go index 8b53c204e..586e557a0 100644 --- a/cmd/erasure-zones.go +++ b/cmd/erasure-zones.go @@ -2088,6 +2088,14 @@ func (z *erasureZones) Health(ctx context.Context, opts HealthOptions) HealthRes } } + // when maintenance is not specified we don't have + // to look at the healing side of the code. + if !opts.Maintenance { + return HealthResult{ + Healthy: true, + } + } + // check if local disks are being healed, if they are being healed // we need to tell healthy status as 'false' so that this server // is not taken down for maintenance diff --git a/pkg/env/web_env.go b/pkg/env/web_env.go index fd2d2d8cb..2adcd41a6 100644 --- a/pkg/env/web_env.go +++ b/pkg/env/web_env.go @@ -29,6 +29,7 @@ import ( "net/url" "os" "regexp" + "strings" "time" "github.com/dgrijalva/jwt-go" @@ -48,40 +49,16 @@ func RegisterGlobalCAs(CAs *x509.CertPool) { globalRootCAs = CAs } -func isValidEnvScheme(scheme string) bool { - switch scheme { - case webEnvScheme: - fallthrough - case webEnvSchemeSecure: - return true - } - return false -} - var ( hostKeys = regexp.MustCompile("^(https?://)(.*?):(.*?)@(.*?)$") ) -func fetchEnvHTTP(envKey string, u *url.URL) (string, error) { - switch u.Scheme { - case webEnvScheme: - u.Scheme = "http" - case webEnvSchemeSecure: - u.Scheme = "https" - } - - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - defer cancel() - - var ( - username, password string - ) - - envURL := u.String() +func fetchHTTPConstituentParts(u *url.URL) (username string, password string, envURL string, err error) { + envURL = u.String() if hostKeys.MatchString(envURL) { parts := hostKeys.FindStringSubmatch(envURL) if len(parts) != 5 { - return "", errors.New("invalid arguments") + return "", "", "", errors.New("invalid arguments") } username = parts[2] password = parts[3] @@ -92,6 +69,31 @@ func fetchEnvHTTP(envKey string, u *url.URL) (string, error) { username = u.User.Username() password, _ = u.User.Password() } + return username, password, envURL, nil +} + +func getEnvValueFromHTTP(urlStr, envKey string) (string, error) { + u, err := url.Parse(urlStr) + if err != nil { + return "", err + } + + switch u.Scheme { + case webEnvScheme: + u.Scheme = "http" + case webEnvSchemeSecure: + u.Scheme = "https" + default: + return "", errors.New("invalid arguments") + } + + username, password, envURL, err := fetchHTTPConstituentParts(u) + if err != nil { + return "", err + } + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodGet, envURL+"?key="+envKey, nil) if err != nil { @@ -161,19 +163,21 @@ func Environ() []string { // to fetch ENV values for the env value from a remote server. func LookupEnv(key string) (string, bool) { v, ok := os.LookupEnv(key) - if ok { - u, err := url.Parse(v) - if err != nil { - return v, true - } - if !isValidEnvScheme(u.Scheme) { - return v, true - } - v, err = fetchEnvHTTP(key, u) + if ok && strings.HasPrefix(v, webEnvScheme) { + // If env value starts with `env*://` + // continue to parse and fetch from remote + var err error + v, err = getEnvValueFromHTTP(strings.TrimSpace(v), key) if err != nil { - return "", false + // fallback to cached value if-any. + return os.LookupEnv("_" + key) } + // Set the ENV value to _env value, + // this value is a fallback in-case of + // server restarts when webhook server + // is down. + os.Setenv("_"+key, v) return v, true } - return "", false + return v, ok } diff --git a/pkg/env/web_env_test.go b/pkg/env/web_env_test.go new file mode 100644 index 000000000..79903f83f --- /dev/null +++ b/pkg/env/web_env_test.go @@ -0,0 +1,81 @@ +/* + * MinIO Cloud Storage, (C) 2020 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 env + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/gorilla/mux" +) + +func GetenvHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + if vars["namespace"] != "default" { + http.Error(w, "namespace not found", http.StatusNotFound) + return + } + if vars["name"] != "minio" { + http.Error(w, "tenant not found", http.StatusNotFound) + return + } + if vars["key"] != "MINIO_ARGS" { + http.Error(w, "key not found", http.StatusNotFound) + return + } + w.Write([]byte("http://127.0.0.{1..4}:9000/data{1...4}")) + w.(http.Flusher).Flush() +} + +func startTestServer(t *testing.T) *httptest.Server { + router := mux.NewRouter().SkipClean(true).UseEncodedPath() + router.Methods(http.MethodGet). + Path("/webhook/v1/getenv/{namespace}/{name}"). + HandlerFunc(GetenvHandler).Queries("key", "{key:.*}") + + ts := httptest.NewServer(router) + t.Cleanup(func() { + ts.Close() + }) + + return ts +} + +func TestWebEnv(t *testing.T) { + ts := startTestServer(t) + + u, err := url.Parse(ts.URL) + if err != nil { + t.Fatal(err) + } + + v, err := getEnvValueFromHTTP( + fmt.Sprintf("env://minio:minio123@%s/webhook/v1/getenv/default/minio", + u.Host), + "MINIO_ARGS") + if err != nil { + t.Fatal(err) + } + + if v != "http://127.0.0.{1..4}:9000/data{1...4}" { + t.Fatalf("Unexpected value %s", v) + } +}