mirror of
https://github.com/minio/minio.git
synced 2025-04-22 11:26:36 -04:00
Support looking up environment remotely (#10215)
adds a feature where we can fetch the MinIO command-line remotely, this is primarily meant to add some stateless nature to the MinIO deployment in k8s environments, MinIO operator would run a webhook service endpoint which can be used to fetch any environment value in a generalized approach.
This commit is contained in:
parent
adcaa6f9de
commit
77509ce391
@ -31,11 +31,12 @@ const (
|
|||||||
EnvDomain = "MINIO_DOMAIN"
|
EnvDomain = "MINIO_DOMAIN"
|
||||||
EnvRegionName = "MINIO_REGION_NAME"
|
EnvRegionName = "MINIO_REGION_NAME"
|
||||||
EnvPublicIPs = "MINIO_PUBLIC_IPS"
|
EnvPublicIPs = "MINIO_PUBLIC_IPS"
|
||||||
EnvEndpoints = "MINIO_ENDPOINTS"
|
|
||||||
EnvFSOSync = "MINIO_FS_OSYNC"
|
EnvFSOSync = "MINIO_FS_OSYNC"
|
||||||
|
EnvArgs = "MINIO_ARGS"
|
||||||
|
|
||||||
EnvUpdate = "MINIO_UPDATE"
|
EnvUpdate = "MINIO_UPDATE"
|
||||||
|
|
||||||
|
EnvEndpoints = "MINIO_ENDPOINTS" // legacy
|
||||||
EnvWorm = "MINIO_WORM" // legacy
|
EnvWorm = "MINIO_WORM" // legacy
|
||||||
EnvRegion = "MINIO_REGION" // legacy
|
EnvRegion = "MINIO_REGION" // legacy
|
||||||
)
|
)
|
||||||
|
@ -172,6 +172,18 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
|||||||
// Handle common command args.
|
// Handle common command args.
|
||||||
handleCommonCmdArgs(ctx)
|
handleCommonCmdArgs(ctx)
|
||||||
|
|
||||||
|
// Check and load TLS certificates.
|
||||||
|
var err error
|
||||||
|
globalPublicCerts, globalTLSCerts, globalIsSSL, err = getTLSConfig()
|
||||||
|
logger.FatalIf(err, "Invalid TLS certificate file")
|
||||||
|
|
||||||
|
// Check and load Root CAs.
|
||||||
|
globalRootCAs, err = config.GetRootCAs(globalCertsCADir.Get())
|
||||||
|
logger.FatalIf(err, "Failed to read root CAs (%v)", err)
|
||||||
|
|
||||||
|
// Register root CAs for remote ENVs
|
||||||
|
env.RegisterGlobalCAs(globalRootCAs)
|
||||||
|
|
||||||
// Initialize all help
|
// Initialize all help
|
||||||
initHelp()
|
initHelp()
|
||||||
|
|
||||||
@ -184,15 +196,6 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
|||||||
// To avoid this error situation we check for port availability.
|
// To avoid this error situation we check for port availability.
|
||||||
logger.FatalIf(checkPortAvailability(globalMinioHost, globalMinioPort), "Unable to start the gateway")
|
logger.FatalIf(checkPortAvailability(globalMinioHost, globalMinioPort), "Unable to start the gateway")
|
||||||
|
|
||||||
// Check and load TLS certificates.
|
|
||||||
var err error
|
|
||||||
globalPublicCerts, globalTLSCerts, globalIsSSL, err = getTLSConfig()
|
|
||||||
logger.FatalIf(err, "Invalid TLS certificate file")
|
|
||||||
|
|
||||||
// Check and load Root CAs.
|
|
||||||
globalRootCAs, err = config.GetRootCAs(globalCertsCADir.Get())
|
|
||||||
logger.FatalIf(err, "Failed to read root CAs (%v)", err)
|
|
||||||
|
|
||||||
globalMinioEndpoint = func() string {
|
globalMinioEndpoint = func() string {
|
||||||
host := globalMinioHost
|
host := globalMinioHost
|
||||||
if host == "" {
|
if host == "" {
|
||||||
|
@ -93,11 +93,16 @@ EXAMPLES:
|
|||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if endpoints are either available through environment
|
func serverCmdArgs(ctx *cli.Context) []string {
|
||||||
// or command line, returns false if both fails.
|
v := env.Get(config.EnvArgs, "")
|
||||||
func endpointsPresent(ctx *cli.Context) bool {
|
if v == "" {
|
||||||
endpoints := env.Get(config.EnvEndpoints, strings.Join(ctx.Args(), config.ValueSeparator))
|
// Fall back to older ENV MINIO_ENDPOINTS
|
||||||
return len(endpoints) != 0
|
v = env.Get(config.EnvEndpoints, "")
|
||||||
|
}
|
||||||
|
if v == "" {
|
||||||
|
return ctx.Args()
|
||||||
|
}
|
||||||
|
return strings.Fields(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func serverHandleCmdArgs(ctx *cli.Context) {
|
func serverHandleCmdArgs(ctx *cli.Context) {
|
||||||
@ -106,18 +111,24 @@ func serverHandleCmdArgs(ctx *cli.Context) {
|
|||||||
|
|
||||||
logger.FatalIf(CheckLocalServerAddr(globalCLIContext.Addr), "Unable to validate passed arguments")
|
logger.FatalIf(CheckLocalServerAddr(globalCLIContext.Addr), "Unable to validate passed arguments")
|
||||||
|
|
||||||
var setupType SetupType
|
|
||||||
var err error
|
var err error
|
||||||
|
var setupType SetupType
|
||||||
|
|
||||||
|
// Check and load TLS certificates.
|
||||||
|
globalPublicCerts, globalTLSCerts, globalIsSSL, err = getTLSConfig()
|
||||||
|
logger.FatalIf(err, "Unable to load the TLS configuration")
|
||||||
|
|
||||||
|
// Check and load Root CAs.
|
||||||
|
globalRootCAs, err = config.GetRootCAs(globalCertsCADir.Get())
|
||||||
|
logger.FatalIf(err, "Failed to read root CAs (%v)", err)
|
||||||
|
|
||||||
|
// Register root CAs for remote ENVs
|
||||||
|
env.RegisterGlobalCAs(globalRootCAs)
|
||||||
|
|
||||||
globalMinioAddr = globalCLIContext.Addr
|
globalMinioAddr = globalCLIContext.Addr
|
||||||
|
|
||||||
globalMinioHost, globalMinioPort = mustSplitHostPort(globalMinioAddr)
|
globalMinioHost, globalMinioPort = mustSplitHostPort(globalMinioAddr)
|
||||||
endpoints := strings.Fields(env.Get(config.EnvEndpoints, ""))
|
globalEndpoints, setupType, err = createServerEndpoints(globalCLIContext.Addr, serverCmdArgs(ctx)...)
|
||||||
if len(endpoints) > 0 {
|
|
||||||
globalEndpoints, setupType, err = createServerEndpoints(globalCLIContext.Addr, endpoints...)
|
|
||||||
} else {
|
|
||||||
globalEndpoints, setupType, err = createServerEndpoints(globalCLIContext.Addr, ctx.Args()...)
|
|
||||||
}
|
|
||||||
logger.FatalIf(err, "Invalid command line arguments")
|
logger.FatalIf(err, "Invalid command line arguments")
|
||||||
|
|
||||||
// On macOS, if a process already listens on LOCALIPADDR:PORT, net.Listen() falls back
|
// On macOS, if a process already listens on LOCALIPADDR:PORT, net.Listen() falls back
|
||||||
@ -370,9 +381,6 @@ func startBackgroundOps(ctx context.Context, objAPI ObjectLayer) {
|
|||||||
|
|
||||||
// serverMain handler called for 'minio server' command.
|
// serverMain handler called for 'minio server' command.
|
||||||
func serverMain(ctx *cli.Context) {
|
func serverMain(ctx *cli.Context) {
|
||||||
if ctx.Args().First() == "help" || !endpointsPresent(ctx) {
|
|
||||||
cli.ShowCommandHelpAndExit(ctx, "server", 1)
|
|
||||||
}
|
|
||||||
setDefaultProfilerRates()
|
setDefaultProfilerRates()
|
||||||
|
|
||||||
// Initialize globalConsoleSys system
|
// Initialize globalConsoleSys system
|
||||||
@ -392,15 +400,7 @@ func serverMain(ctx *cli.Context) {
|
|||||||
// Initialize all help
|
// Initialize all help
|
||||||
initHelp()
|
initHelp()
|
||||||
|
|
||||||
// Check and load TLS certificates.
|
|
||||||
var err error
|
var err error
|
||||||
globalPublicCerts, globalTLSCerts, globalIsSSL, err = getTLSConfig()
|
|
||||||
logger.FatalIf(err, "Unable to load the TLS configuration")
|
|
||||||
|
|
||||||
// Check and load Root CAs.
|
|
||||||
globalRootCAs, err = config.GetRootCAs(globalCertsCADir.Get())
|
|
||||||
logger.FatalIf(err, "Failed to read root CAs (%v)", err)
|
|
||||||
|
|
||||||
globalProxyEndpoints, err = GetProxyEndpoints(globalEndpoints)
|
globalProxyEndpoints, err = GetProxyEndpoints(globalEndpoints)
|
||||||
logger.FatalIf(err, "Invalid command line arguments")
|
logger.FatalIf(err, "Invalid command line arguments")
|
||||||
|
|
||||||
|
7
pkg/env/env.go
vendored
7
pkg/env/env.go
vendored
@ -18,7 +18,6 @@
|
|||||||
package env
|
package env
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@ -46,7 +45,7 @@ func SetEnvOn() {
|
|||||||
|
|
||||||
// IsSet returns if the given env key is set.
|
// IsSet returns if the given env key is set.
|
||||||
func IsSet(key string) bool {
|
func IsSet(key string) bool {
|
||||||
_, ok := os.LookupEnv(key)
|
_, ok := LookupEnv(key)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +60,7 @@ func Get(key, defaultValue string) string {
|
|||||||
if ok {
|
if ok {
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
if v, ok := os.LookupEnv(key); ok {
|
if v, ok := LookupEnv(key); ok {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
return defaultValue
|
return defaultValue
|
||||||
@ -69,7 +68,7 @@ func Get(key, defaultValue string) string {
|
|||||||
|
|
||||||
// List all envs with a given prefix.
|
// List all envs with a given prefix.
|
||||||
func List(prefix string) (envs []string) {
|
func List(prefix string) (envs []string) {
|
||||||
for _, env := range os.Environ() {
|
for _, env := range Environ() {
|
||||||
if strings.HasPrefix(env, prefix) {
|
if strings.HasPrefix(env, prefix) {
|
||||||
values := strings.SplitN(env, "=", 2)
|
values := strings.SplitN(env, "=", 2)
|
||||||
if len(values) == 2 {
|
if len(values) == 2 {
|
||||||
|
167
pkg/env/web_env.go
vendored
Normal file
167
pkg/env/web_env.go
vendored
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* 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 (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
webEnvScheme = "env"
|
||||||
|
webEnvSchemeSecure = "env+tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
globalRootCAs *x509.CertPool
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterGlobalCAs register the global root CAs
|
||||||
|
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()
|
||||||
|
if hostKeys.MatchString(envURL) {
|
||||||
|
parts := hostKeys.FindStringSubmatch(envURL)
|
||||||
|
if len(parts) != 5 {
|
||||||
|
return "", errors.New("invalid arguments")
|
||||||
|
}
|
||||||
|
username = parts[2]
|
||||||
|
password = parts[3]
|
||||||
|
envURL = fmt.Sprintf("%s%s", parts[1], parts[4])
|
||||||
|
}
|
||||||
|
|
||||||
|
if username == "" && password == "" && u.User != nil {
|
||||||
|
username = u.User.Username()
|
||||||
|
password, _ = u.User.Password()
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, envURL+"?key="+envKey, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if username != "" && password != "" {
|
||||||
|
req.SetBasicAuth(username, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
clnt := &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
DialContext: (&net.Dialer{
|
||||||
|
Timeout: 3 * time.Second,
|
||||||
|
KeepAlive: 5 * time.Second,
|
||||||
|
}).DialContext,
|
||||||
|
ResponseHeaderTimeout: 3 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 3 * time.Second,
|
||||||
|
ExpectContinueTimeout: 3 * time.Second,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
RootCAs: globalRootCAs,
|
||||||
|
},
|
||||||
|
// Go net/http automatically unzip if content-type is
|
||||||
|
// gzip disable this feature, as we are always interested
|
||||||
|
// in raw stream.
|
||||||
|
DisableCompression: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := clnt.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
envValueBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(envValueBytes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Environ returns a copy of strings representing the
|
||||||
|
// environment, in the form "key=value".
|
||||||
|
func Environ() []string {
|
||||||
|
return os.Environ()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupEnv retrieves the value of the environment variable
|
||||||
|
// named by the key. If the variable is present in the
|
||||||
|
// environment the value (which may be empty) is returned
|
||||||
|
// and the boolean is true. Otherwise the returned value
|
||||||
|
// will be empty and the boolean will be false.
|
||||||
|
//
|
||||||
|
// Additionally if the input is env://username:password@remote:port/
|
||||||
|
// 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 "", false
|
||||||
|
}
|
||||||
|
if !isValidEnvScheme(u.Scheme) {
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
v, err = fetchEnvHTTP(key, u)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user