mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00

The `--listen-metrics-urls` also responds to the `/health` endpoint, but that was not made clear in the documentation. These updates should help explain how else that flag can be used.
403 lines
17 KiB
Go
403 lines
17 KiB
Go
// Copyright 2015 The etcd Authors
|
|
//
|
|
// 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.
|
|
|
|
// Every change should be reflected on help.go as well.
|
|
|
|
package etcdmain
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/url"
|
|
"os"
|
|
"runtime"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/coreos/etcd/embed"
|
|
"github.com/coreos/etcd/pkg/flags"
|
|
"github.com/coreos/etcd/pkg/types"
|
|
"github.com/coreos/etcd/version"
|
|
|
|
"github.com/ghodss/yaml"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
var (
|
|
proxyFlagOff = "off"
|
|
proxyFlagReadonly = "readonly"
|
|
proxyFlagOn = "on"
|
|
|
|
fallbackFlagExit = "exit"
|
|
fallbackFlagProxy = "proxy"
|
|
|
|
ignored = []string{
|
|
"cluster-active-size",
|
|
"cluster-remove-delay",
|
|
"cluster-sync-interval",
|
|
"config",
|
|
"force",
|
|
"max-result-buffer",
|
|
"max-retry-attempts",
|
|
"peer-heartbeat-interval",
|
|
"peer-election-timeout",
|
|
"retry-interval",
|
|
"snapshot",
|
|
"v",
|
|
"vv",
|
|
// for coverage testing
|
|
"test.coverprofile",
|
|
"test.outputdir",
|
|
}
|
|
)
|
|
|
|
type configProxy struct {
|
|
ProxyFailureWaitMs uint `json:"proxy-failure-wait"`
|
|
ProxyRefreshIntervalMs uint `json:"proxy-refresh-interval"`
|
|
ProxyDialTimeoutMs uint `json:"proxy-dial-timeout"`
|
|
ProxyWriteTimeoutMs uint `json:"proxy-write-timeout"`
|
|
ProxyReadTimeoutMs uint `json:"proxy-read-timeout"`
|
|
Fallback string
|
|
Proxy string
|
|
ProxyJSON string `json:"proxy"`
|
|
FallbackJSON string `json:"discovery-fallback"`
|
|
}
|
|
|
|
// config holds the config for a command line invocation of etcd
|
|
type config struct {
|
|
ec embed.Config
|
|
cp configProxy
|
|
cf configFlags
|
|
configFile string
|
|
printVersion bool
|
|
ignored []string
|
|
}
|
|
|
|
// configFlags has the set of flags used for command line parsing a Config
|
|
type configFlags struct {
|
|
flagSet *flag.FlagSet
|
|
clusterState *flags.SelectiveStringValue
|
|
fallback *flags.SelectiveStringValue
|
|
proxy *flags.SelectiveStringValue
|
|
}
|
|
|
|
func newConfig() *config {
|
|
cfg := &config{
|
|
ec: *embed.NewConfig(),
|
|
cp: configProxy{
|
|
Proxy: proxyFlagOff,
|
|
ProxyFailureWaitMs: 5000,
|
|
ProxyRefreshIntervalMs: 30000,
|
|
ProxyDialTimeoutMs: 1000,
|
|
ProxyWriteTimeoutMs: 5000,
|
|
},
|
|
ignored: ignored,
|
|
}
|
|
cfg.cf = configFlags{
|
|
flagSet: flag.NewFlagSet("etcd", flag.ContinueOnError),
|
|
clusterState: flags.NewSelectiveStringValue(
|
|
embed.ClusterStateFlagNew,
|
|
embed.ClusterStateFlagExisting,
|
|
),
|
|
fallback: flags.NewSelectiveStringValue(
|
|
fallbackFlagProxy,
|
|
fallbackFlagExit,
|
|
),
|
|
proxy: flags.NewSelectiveStringValue(
|
|
proxyFlagOff,
|
|
proxyFlagReadonly,
|
|
proxyFlagOn,
|
|
),
|
|
}
|
|
|
|
fs := cfg.cf.flagSet
|
|
fs.Usage = func() {
|
|
fmt.Fprintln(os.Stderr, usageline)
|
|
}
|
|
|
|
fs.StringVar(&cfg.configFile, "config-file", "", "Path to the server configuration file")
|
|
|
|
// member
|
|
fs.StringVar(&cfg.ec.Dir, "data-dir", cfg.ec.Dir, "Path to the data directory.")
|
|
fs.StringVar(&cfg.ec.WalDir, "wal-dir", cfg.ec.WalDir, "Path to the dedicated wal directory.")
|
|
fs.Var(
|
|
flags.NewUniqueURLsWithExceptions(embed.DefaultListenPeerURLs, ""),
|
|
"listen-peer-urls",
|
|
"List of URLs to listen on for peer traffic.",
|
|
)
|
|
fs.Var(
|
|
flags.NewUniqueURLsWithExceptions(embed.DefaultListenClientURLs, ""), "listen-client-urls",
|
|
"List of URLs to listen on for client traffic.",
|
|
)
|
|
fs.Var(
|
|
flags.NewUniqueURLsWithExceptions("", ""),
|
|
"listen-metrics-urls",
|
|
"List of URLs to listen on for the metrics and health endpoints.",
|
|
)
|
|
fs.UintVar(&cfg.ec.MaxSnapFiles, "max-snapshots", cfg.ec.MaxSnapFiles, "Maximum number of snapshot files to retain (0 is unlimited).")
|
|
fs.UintVar(&cfg.ec.MaxWalFiles, "max-wals", cfg.ec.MaxWalFiles, "Maximum number of wal files to retain (0 is unlimited).")
|
|
fs.StringVar(&cfg.ec.Name, "name", cfg.ec.Name, "Human-readable name for this member.")
|
|
fs.Uint64Var(&cfg.ec.SnapCount, "snapshot-count", cfg.ec.SnapCount, "Number of committed transactions to trigger a snapshot to disk.")
|
|
fs.UintVar(&cfg.ec.TickMs, "heartbeat-interval", cfg.ec.TickMs, "Time (in milliseconds) of a heartbeat interval.")
|
|
fs.UintVar(&cfg.ec.ElectionMs, "election-timeout", cfg.ec.ElectionMs, "Time (in milliseconds) for an election to timeout.")
|
|
fs.BoolVar(&cfg.ec.InitialElectionTickAdvance, "initial-election-tick-advance", cfg.ec.InitialElectionTickAdvance, "Whether to fast-forward initial election ticks on boot for faster election.")
|
|
fs.Int64Var(&cfg.ec.QuotaBackendBytes, "quota-backend-bytes", cfg.ec.QuotaBackendBytes, "Raise alarms when backend size exceeds the given quota. 0 means use the default quota.")
|
|
fs.UintVar(&cfg.ec.MaxTxnOps, "max-txn-ops", cfg.ec.MaxTxnOps, "Maximum number of operations permitted in a transaction.")
|
|
fs.UintVar(&cfg.ec.MaxRequestBytes, "max-request-bytes", cfg.ec.MaxRequestBytes, "Maximum client request size in bytes the server will accept.")
|
|
fs.DurationVar(&cfg.ec.GRPCKeepAliveMinTime, "grpc-keepalive-min-time", cfg.ec.GRPCKeepAliveMinTime, "Minimum interval duration that a client should wait before pinging server.")
|
|
fs.DurationVar(&cfg.ec.GRPCKeepAliveInterval, "grpc-keepalive-interval", cfg.ec.GRPCKeepAliveInterval, "Frequency duration of server-to-client ping to check if a connection is alive (0 to disable).")
|
|
fs.DurationVar(&cfg.ec.GRPCKeepAliveTimeout, "grpc-keepalive-timeout", cfg.ec.GRPCKeepAliveTimeout, "Additional duration of wait before closing a non-responsive connection (0 to disable).")
|
|
|
|
// clustering
|
|
fs.Var(
|
|
flags.NewUniqueURLsWithExceptions(embed.DefaultInitialAdvertisePeerURLs, ""),
|
|
"initial-advertise-peer-urls",
|
|
"List of this member's peer URLs to advertise to the rest of the cluster.",
|
|
)
|
|
fs.Var(
|
|
flags.NewUniqueURLsWithExceptions(embed.DefaultAdvertiseClientURLs, ""),
|
|
"advertise-client-urls",
|
|
"List of this member's client URLs to advertise to the public.",
|
|
)
|
|
fs.StringVar(&cfg.ec.Durl, "discovery", cfg.ec.Durl, "Discovery URL used to bootstrap the cluster.")
|
|
fs.Var(cfg.cf.fallback, "discovery-fallback", fmt.Sprintf("Valid values include %q", cfg.cf.fallback.Valids()))
|
|
|
|
fs.StringVar(&cfg.ec.Dproxy, "discovery-proxy", cfg.ec.Dproxy, "HTTP proxy to use for traffic to discovery service.")
|
|
fs.StringVar(&cfg.ec.DNSCluster, "discovery-srv", cfg.ec.DNSCluster, "DNS domain used to bootstrap initial cluster.")
|
|
fs.StringVar(&cfg.ec.DNSClusterServiceName, "discovery-srv-name", cfg.ec.DNSClusterServiceName, "Service name to query when using DNS discovery.")
|
|
fs.StringVar(&cfg.ec.InitialCluster, "initial-cluster", cfg.ec.InitialCluster, "Initial cluster configuration for bootstrapping.")
|
|
fs.StringVar(&cfg.ec.InitialClusterToken, "initial-cluster-token", cfg.ec.InitialClusterToken, "Initial cluster token for the etcd cluster during bootstrap.")
|
|
fs.Var(cfg.cf.clusterState, "initial-cluster-state", "Initial cluster state ('new' or 'existing').")
|
|
|
|
fs.BoolVar(&cfg.ec.StrictReconfigCheck, "strict-reconfig-check", cfg.ec.StrictReconfigCheck, "Reject reconfiguration requests that would cause quorum loss.")
|
|
fs.BoolVar(&cfg.ec.EnableV2, "enable-v2", cfg.ec.EnableV2, "Accept etcd V2 client requests.")
|
|
fs.BoolVar(&cfg.ec.PreVote, "pre-vote", cfg.ec.PreVote, "Enable to run an additional Raft election phase.")
|
|
|
|
// proxy
|
|
fs.Var(cfg.cf.proxy, "proxy", fmt.Sprintf("Valid values include %q", cfg.cf.proxy.Valids()))
|
|
fs.UintVar(&cfg.cp.ProxyFailureWaitMs, "proxy-failure-wait", cfg.cp.ProxyFailureWaitMs, "Time (in milliseconds) an endpoint will be held in a failed state.")
|
|
fs.UintVar(&cfg.cp.ProxyRefreshIntervalMs, "proxy-refresh-interval", cfg.cp.ProxyRefreshIntervalMs, "Time (in milliseconds) of the endpoints refresh interval.")
|
|
fs.UintVar(&cfg.cp.ProxyDialTimeoutMs, "proxy-dial-timeout", cfg.cp.ProxyDialTimeoutMs, "Time (in milliseconds) for a dial to timeout.")
|
|
fs.UintVar(&cfg.cp.ProxyWriteTimeoutMs, "proxy-write-timeout", cfg.cp.ProxyWriteTimeoutMs, "Time (in milliseconds) for a write to timeout.")
|
|
fs.UintVar(&cfg.cp.ProxyReadTimeoutMs, "proxy-read-timeout", cfg.cp.ProxyReadTimeoutMs, "Time (in milliseconds) for a read to timeout.")
|
|
|
|
// security
|
|
fs.StringVar(&cfg.ec.ClientTLSInfo.CertFile, "cert-file", "", "Path to the client server TLS cert file.")
|
|
fs.StringVar(&cfg.ec.ClientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.")
|
|
fs.BoolVar(&cfg.ec.ClientTLSInfo.ClientCertAuth, "client-cert-auth", false, "Enable client cert authentication.")
|
|
fs.StringVar(&cfg.ec.ClientTLSInfo.CRLFile, "client-crl-file", "", "Path to the client certificate revocation list file.")
|
|
fs.StringVar(&cfg.ec.ClientTLSInfo.TrustedCAFile, "trusted-ca-file", "", "Path to the client server TLS trusted CA cert file.")
|
|
fs.BoolVar(&cfg.ec.ClientAutoTLS, "auto-tls", false, "Client TLS using generated certificates")
|
|
fs.StringVar(&cfg.ec.PeerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.")
|
|
fs.StringVar(&cfg.ec.PeerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.")
|
|
fs.BoolVar(&cfg.ec.PeerTLSInfo.ClientCertAuth, "peer-client-cert-auth", false, "Enable peer client cert authentication.")
|
|
fs.StringVar(&cfg.ec.PeerTLSInfo.TrustedCAFile, "peer-trusted-ca-file", "", "Path to the peer server TLS trusted CA file.")
|
|
fs.BoolVar(&cfg.ec.PeerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates")
|
|
fs.StringVar(&cfg.ec.PeerTLSInfo.CRLFile, "peer-crl-file", "", "Path to the peer certificate revocation list file.")
|
|
fs.StringVar(&cfg.ec.PeerTLSInfo.AllowedCN, "peer-cert-allowed-cn", "", "Allowed CN for inter peer authentication.")
|
|
|
|
fs.Var(
|
|
flags.NewUniqueURLsWithExceptions("*", "*"),
|
|
"cors",
|
|
"Comma-separated white list of origins for CORS, or cross-origin resource sharing, (empty or * means allow all)",
|
|
)
|
|
fs.Var(flags.NewUniqueStringsValue("*"), "host-whitelist", "Comma-separated acceptable hostnames from HTTP client requests, if server is not secure (empty means allow all).")
|
|
|
|
// logging
|
|
fs.StringVar(&cfg.ec.Logger, "logger", "capnslog", "Specify 'zap' for structured logging or 'capnslog'.")
|
|
fs.Var(flags.NewUniqueStringsValue(embed.DefaultLogOutput), "log-output", "DEPRECATED: use '--log-outputs'.")
|
|
fs.Var(flags.NewUniqueStringsValue(embed.DefaultLogOutput), "log-outputs", "Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd, or list of comma separated output targets.")
|
|
fs.BoolVar(&cfg.ec.Debug, "debug", false, "Enable debug-level logging for etcd.")
|
|
fs.StringVar(&cfg.ec.LogPkgLevels, "log-package-levels", "", "(To be deprecated) Specify a particular log level for each etcd package (eg: 'etcdmain=CRITICAL,etcdserver=DEBUG').")
|
|
|
|
// version
|
|
fs.BoolVar(&cfg.printVersion, "version", false, "Print the version and exit.")
|
|
|
|
fs.StringVar(&cfg.ec.AutoCompactionRetention, "auto-compaction-retention", "0", "Auto compaction retention for mvcc key value store. 0 means disable auto compaction.")
|
|
fs.StringVar(&cfg.ec.AutoCompactionMode, "auto-compaction-mode", "periodic", "interpret 'auto-compaction-retention' one of: periodic|revision. 'periodic' for duration based retention, defaulting to hours if no time unit is provided (e.g. '5m'). 'revision' for revision number based retention.")
|
|
|
|
// pprof profiler via HTTP
|
|
fs.BoolVar(&cfg.ec.EnablePprof, "enable-pprof", false, "Enable runtime profiling data via HTTP server. Address is at client URL + \"/debug/pprof/\"")
|
|
|
|
// additional metrics
|
|
fs.StringVar(&cfg.ec.Metrics, "metrics", cfg.ec.Metrics, "Set level of detail for exported metrics, specify 'extensive' to include histogram metrics")
|
|
|
|
// auth
|
|
fs.StringVar(&cfg.ec.AuthToken, "auth-token", cfg.ec.AuthToken, "Specify auth token specific options.")
|
|
|
|
// experimental
|
|
fs.BoolVar(&cfg.ec.ExperimentalInitialCorruptCheck, "experimental-initial-corrupt-check", cfg.ec.ExperimentalInitialCorruptCheck, "Enable to check data corruption before serving any client/peer traffic.")
|
|
fs.DurationVar(&cfg.ec.ExperimentalCorruptCheckTime, "experimental-corrupt-check-time", cfg.ec.ExperimentalCorruptCheckTime, "Duration of time between cluster corruption check passes.")
|
|
fs.StringVar(&cfg.ec.ExperimentalEnableV2V3, "experimental-enable-v2v3", cfg.ec.ExperimentalEnableV2V3, "v3 prefix for serving emulated v2 state.")
|
|
|
|
// unsafe
|
|
fs.BoolVar(&cfg.ec.ForceNewCluster, "force-new-cluster", false, "Force to create a new one member cluster.")
|
|
|
|
// ignored
|
|
for _, f := range cfg.ignored {
|
|
fs.Var(&flags.IgnoredFlag{Name: f}, f, "")
|
|
}
|
|
return cfg
|
|
}
|
|
|
|
func (cfg *config) parse(arguments []string) error {
|
|
perr := cfg.cf.flagSet.Parse(arguments)
|
|
switch perr {
|
|
case nil:
|
|
case flag.ErrHelp:
|
|
fmt.Println(flagsline)
|
|
os.Exit(0)
|
|
default:
|
|
os.Exit(2)
|
|
}
|
|
if len(cfg.cf.flagSet.Args()) != 0 {
|
|
return fmt.Errorf("'%s' is not a valid flag", cfg.cf.flagSet.Arg(0))
|
|
}
|
|
|
|
if cfg.printVersion {
|
|
fmt.Printf("etcd Version: %s\n", version.Version)
|
|
fmt.Printf("Git SHA: %s\n", version.GitSHA)
|
|
fmt.Printf("Go Version: %s\n", runtime.Version())
|
|
fmt.Printf("Go OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
|
os.Exit(0)
|
|
}
|
|
|
|
var err error
|
|
if cfg.configFile != "" {
|
|
err = cfg.configFromFile(cfg.configFile)
|
|
if lg := cfg.ec.GetLogger(); lg != nil {
|
|
lg.Info(
|
|
"loaded server configuraionl, other configuration command line flags and environment variables will be ignored if provided",
|
|
zap.String("path", cfg.configFile),
|
|
)
|
|
} else {
|
|
plog.Infof("Loading server configuration from %q. Other configuration command line flags and environment variables will be ignored if provided.", cfg.configFile)
|
|
}
|
|
} else {
|
|
err = cfg.configFromCmdLine()
|
|
}
|
|
// now logger is set up
|
|
return err
|
|
}
|
|
|
|
func (cfg *config) configFromCmdLine() error {
|
|
err := flags.SetFlagsFromEnv("ETCD", cfg.cf.flagSet)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cfg.ec.LPUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-peer-urls")
|
|
cfg.ec.APUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "initial-advertise-peer-urls")
|
|
cfg.ec.LCUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-client-urls")
|
|
cfg.ec.ACUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "advertise-client-urls")
|
|
cfg.ec.ListenMetricsUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-metrics-urls")
|
|
|
|
cfg.ec.CORS = flags.UniqueURLsMapFromFlag(cfg.cf.flagSet, "cors")
|
|
cfg.ec.HostWhitelist = flags.UniqueStringsMapFromFlag(cfg.cf.flagSet, "host-whitelist")
|
|
|
|
// TODO: remove this in v3.5
|
|
output := flags.UniqueStringsMapFromFlag(cfg.cf.flagSet, "log-output")
|
|
oss1 := make([]string, 0, len(output))
|
|
for v := range output {
|
|
oss1 = append(oss1, v)
|
|
}
|
|
sort.Strings(oss1)
|
|
cfg.ec.DeprecatedLogOutput = oss1
|
|
|
|
outputs := flags.UniqueStringsMapFromFlag(cfg.cf.flagSet, "log-outputs")
|
|
oss2 := make([]string, 0, len(outputs))
|
|
for v := range outputs {
|
|
oss2 = append(oss2, v)
|
|
}
|
|
sort.Strings(oss2)
|
|
cfg.ec.LogOutputs = oss2
|
|
|
|
cfg.ec.ClusterState = cfg.cf.clusterState.String()
|
|
cfg.cp.Fallback = cfg.cf.fallback.String()
|
|
cfg.cp.Proxy = cfg.cf.proxy.String()
|
|
|
|
// disable default advertise-client-urls if lcurls is set
|
|
missingAC := flags.IsSet(cfg.cf.flagSet, "listen-client-urls") && !flags.IsSet(cfg.cf.flagSet, "advertise-client-urls")
|
|
if !cfg.mayBeProxy() && missingAC {
|
|
cfg.ec.ACUrls = nil
|
|
}
|
|
|
|
// disable default initial-cluster if discovery is set
|
|
if (cfg.ec.Durl != "" || cfg.ec.DNSCluster != "" || cfg.ec.DNSClusterServiceName != "") && !flags.IsSet(cfg.cf.flagSet, "initial-cluster") {
|
|
cfg.ec.InitialCluster = ""
|
|
}
|
|
|
|
return cfg.validate()
|
|
}
|
|
|
|
func (cfg *config) configFromFile(path string) error {
|
|
eCfg, err := embed.ConfigFromFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cfg.ec = *eCfg
|
|
|
|
// load extra config information
|
|
b, rerr := ioutil.ReadFile(path)
|
|
if rerr != nil {
|
|
return rerr
|
|
}
|
|
if yerr := yaml.Unmarshal(b, &cfg.cp); yerr != nil {
|
|
return yerr
|
|
}
|
|
|
|
if cfg.ec.ListenMetricsUrlsJSON != "" {
|
|
us, err := types.NewURLs(strings.Split(cfg.ec.ListenMetricsUrlsJSON, ","))
|
|
if err != nil {
|
|
log.Fatalf("unexpected error setting up listen-metrics-urls: %v", err)
|
|
}
|
|
cfg.ec.ListenMetricsUrls = []url.URL(us)
|
|
}
|
|
|
|
if cfg.cp.FallbackJSON != "" {
|
|
if err := cfg.cf.fallback.Set(cfg.cp.FallbackJSON); err != nil {
|
|
log.Fatalf("unexpected error setting up discovery-fallback flag: %v", err)
|
|
}
|
|
cfg.cp.Fallback = cfg.cf.fallback.String()
|
|
}
|
|
|
|
if cfg.cp.ProxyJSON != "" {
|
|
if err := cfg.cf.proxy.Set(cfg.cp.ProxyJSON); err != nil {
|
|
log.Fatalf("unexpected error setting up proxyFlag: %v", err)
|
|
}
|
|
cfg.cp.Proxy = cfg.cf.proxy.String()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (cfg *config) mayBeProxy() bool {
|
|
mayFallbackToProxy := cfg.ec.Durl != "" && cfg.cp.Fallback == fallbackFlagProxy
|
|
return cfg.cp.Proxy != proxyFlagOff || mayFallbackToProxy
|
|
}
|
|
|
|
func (cfg *config) validate() error {
|
|
err := cfg.ec.Validate()
|
|
// TODO(yichengq): check this for joining through discovery service case
|
|
if err == embed.ErrUnsetAdvertiseClientURLsFlag && cfg.mayBeProxy() {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (cfg config) isProxy() bool { return cfg.cf.proxy.String() != proxyFlagOff }
|
|
func (cfg config) isReadonlyProxy() bool { return cfg.cf.proxy.String() == proxyFlagReadonly }
|
|
func (cfg config) shouldFallbackToProxy() bool { return cfg.cf.fallback.String() == fallbackFlagProxy }
|