Skip to content

Commit

Permalink
Add API to set timeout on requests towards the K8s API server
Browse files Browse the repository at this point in the history
The new flag "--kube-apitimeout <duration>" or env.var. HELM_KUBEAPITIMEOUT
can be used to set the timeout on requests towards the Kubernetes API server.

This enables users with problematic K8s cluster to override the default timeout
and let the sent requests finish successfully.

Examples usage:

> helm --kube-apitimeout=45s install ...
> HELM_KUBEAPITIMEOUT=45s helm env

Signed-off-by: Björn Svensson <bjorn.a.svensson@est.tech>
  • Loading branch information
bjosv committed Mar 25, 2024
1 parent 976ed8c commit 9a4d5e9
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 4 deletions.
1 change: 1 addition & 0 deletions cmd/helm/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Environment variables:
| $HELM_REPOSITORY_CONFIG | set the path to the repositories file. |
| $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") |
| $HELM_KUBEAPISERVER | set the Kubernetes API Server Endpoint for authentication |
| $HELM_KUBEAPITIMEOUT | set the timeout on requests towards the Kubernetes API Server. Requires a valid time unit (e.g. 30s, 1m). |
| $HELM_KUBECAFILE | set the Kubernetes certificate authority file. |
| $HELM_KUBEASGROUPS | set the Groups to use for impersonation using a comma-separated list. |
| $HELM_KUBEASUSER | set the Username to impersonate for the operation. |
Expand Down
1 change: 1 addition & 0 deletions cmd/helm/testdata/output/env-comp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ HELM_CONFIG_HOME
HELM_DATA_HOME
HELM_DEBUG
HELM_KUBEAPISERVER
HELM_KUBEAPITIMEOUT
HELM_KUBEASGROUPS
HELM_KUBEASUSER
HELM_KUBECAFILE
Expand Down
27 changes: 27 additions & 0 deletions pkg/cli/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"os"
"strconv"
"strings"
"time"

"github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions"
Expand Down Expand Up @@ -64,6 +65,8 @@ type EnvSettings struct {
KubeAsGroups []string
// Kubernetes API Server Endpoint for authentication
KubeAPIServer string
// The timeout on requests towards the Kubernetes API Server Endpoint.
KubeAPITimeout time.Duration
// Custom certificate authority file.
KubeCaFile string
// KubeInsecureSkipTLSVerify indicates if server's certificate will not be checked for validity.
Expand Down Expand Up @@ -99,6 +102,7 @@ func New() *EnvSettings {
KubeAsUser: os.Getenv("HELM_KUBEASUSER"),
KubeAsGroups: envCSV("HELM_KUBEASGROUPS"),
KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"),
KubeAPITimeout: envDurationOr("HELM_KUBEAPITIMEOUT", 0),
KubeCaFile: os.Getenv("HELM_KUBECAFILE"),
KubeTLSServerName: os.Getenv("HELM_KUBETLS_SERVER_NAME"),
KubeInsecureSkipTLSVerify: envBoolOr("HELM_KUBEINSECURE_SKIP_TLS_VERIFY", false),
Expand Down Expand Up @@ -126,6 +130,7 @@ func New() *EnvSettings {
WrapConfigFn: func(config *rest.Config) *rest.Config {
config.Burst = env.BurstLimit
config.QPS = env.QPS
config.Timeout = env.KubeAPITimeout
config.Wrap(func(rt http.RoundTripper) http.RoundTripper {
return &retryingRoundTripper{wrapped: rt}
})
Expand All @@ -145,6 +150,7 @@ func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.KubeAsUser, "kube-as-user", s.KubeAsUser, "username to impersonate for the operation")
fs.StringArrayVar(&s.KubeAsGroups, "kube-as-group", s.KubeAsGroups, "group to impersonate for the operation, this flag can be repeated to specify multiple groups.")
fs.StringVar(&s.KubeAPIServer, "kube-apiserver", s.KubeAPIServer, "the address and the port for the Kubernetes API server")
fs.DurationVar(&s.KubeAPITimeout, "kube-apitimeout", s.KubeAPITimeout, "the timeout on requests towards the Kubernetes API server")
fs.StringVar(&s.KubeCaFile, "kube-ca-file", s.KubeCaFile, "the certificate authority file for the Kubernetes API server connection")
fs.StringVar(&s.KubeTLSServerName, "kube-tls-server-name", s.KubeTLSServerName, "server name to use for Kubernetes API server certificate validation. If it is not provided, the hostname used to contact the server is used")
fs.BoolVar(&s.KubeInsecureSkipTLSVerify, "kube-insecure-skip-tls-verify", s.KubeInsecureSkipTLSVerify, "if true, the Kubernetes API server's certificate will not be checked for validity. This will make your HTTPS connections insecure")
Expand Down Expand Up @@ -199,6 +205,18 @@ func envFloat32Or(name string, def float32) float32 {
return float32(ret)
}

func envDurationOr(name string, def time.Duration) time.Duration {
if name == "" {
return def
}
envVal := envOr(name, def.String())
ret, err := time.ParseDuration(envVal)
if err != nil {
return def
}
return ret
}

func envCSV(name string) (ls []string) {
trimmed := strings.Trim(os.Getenv(name), ", ")
if trimmed != "" {
Expand All @@ -207,6 +225,14 @@ func envCSV(name string) (ls []string) {
return
}

// An unconfigured duration is presented as an empty string instead of '0s'.
func durationToEnvString(value time.Duration) string {
if value == 0 {
return ""
}
return value.String()
}

func (s *EnvSettings) EnvVars() map[string]string {
envvars := map[string]string{
"HELM_BIN": os.Args[0],
Expand All @@ -229,6 +255,7 @@ func (s *EnvSettings) EnvVars() map[string]string {
"HELM_KUBEASUSER": s.KubeAsUser,
"HELM_KUBEASGROUPS": strings.Join(s.KubeAsGroups, ","),
"HELM_KUBEAPISERVER": s.KubeAPIServer,
"HELM_KUBEAPITIMEOUT": durationToEnvString(s.KubeAPITimeout),
"HELM_KUBECAFILE": s.KubeCaFile,
"HELM_KUBEINSECURE_SKIP_TLS_VERIFY": strconv.FormatBool(s.KubeInsecureSkipTLSVerify),
"HELM_KUBETLS_SERVER_NAME": s.KubeTLSServerName,
Expand Down
16 changes: 12 additions & 4 deletions pkg/cli/environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"reflect"
"strings"
"testing"
"time"

"github.com/spf13/pflag"

Expand Down Expand Up @@ -55,6 +56,7 @@ func TestEnvSettings(t *testing.T) {
maxhistory int
kubeAsUser string
kubeAsGroups []string
kubeTimeout time.Duration
kubeCaFile string
kubeInsecure bool
kubeTLSServer string
Expand All @@ -70,43 +72,46 @@ func TestEnvSettings(t *testing.T) {
},
{
name: "with flags set",
args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-ca-file=/tmp/ca.crt --burst-limit 100 --qps 50.12 --kube-insecure-skip-tls-verify=true --kube-tls-server-name=example.org",
args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-apitimeout=10s --kube-ca-file=/tmp/ca.crt --burst-limit 100 --qps 50.12 --kube-insecure-skip-tls-verify=true --kube-tls-server-name=example.org",
ns: "myns",
debug: true,
maxhistory: defaultMaxHistory,
burstLimit: 100,
qps: 50.12,
kubeAsUser: "poro",
kubeAsGroups: []string{"admins", "teatime", "snackeaters"},
kubeTimeout: 10 * time.Second,
kubeCaFile: "/tmp/ca.crt",
kubeTLSServer: "example.org",
kubeInsecure: true,
},
{
name: "with envvars set",
envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt", "HELM_BURST_LIMIT": "150", "HELM_KUBEINSECURE_SKIP_TLS_VERIFY": "true", "HELM_KUBETLS_SERVER_NAME": "example.org", "HELM_QPS": "60.34"},
envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_KUBEAPITIMEOUT": "40s", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt", "HELM_BURST_LIMIT": "150", "HELM_KUBEINSECURE_SKIP_TLS_VERIFY": "true", "HELM_KUBETLS_SERVER_NAME": "example.org", "HELM_QPS": "60.34"},
ns: "yourns",
maxhistory: 5,
burstLimit: 150,
qps: 60.34,
debug: true,
kubeAsUser: "pikachu",
kubeAsGroups: []string{"operators", "snackeaters", "partyanimals"},
kubeTimeout: 40 * time.Second,
kubeCaFile: "/tmp/ca.crt",
kubeTLSServer: "example.org",
kubeInsecure: true,
},
{
name: "with flags and envvars set",
args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-ca-file=/my/ca.crt --burst-limit 175 --qps 70 --kube-insecure-skip-tls-verify=true --kube-tls-server-name=example.org",
envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt", "HELM_BURST_LIMIT": "200", "HELM_KUBEINSECURE_SKIP_TLS_VERIFY": "true", "HELM_KUBETLS_SERVER_NAME": "example.org", "HELM_QPS": "40"},
args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-apitimeout=45s --kube-ca-file=/my/ca.crt --burst-limit 175 --qps 70 --kube-insecure-skip-tls-verify=true --kube-tls-server-name=example.org",
envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_KUBEAPITIMEOUT": "1m30s", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt", "HELM_BURST_LIMIT": "200", "HELM_KUBEINSECURE_SKIP_TLS_VERIFY": "true", "HELM_KUBETLS_SERVER_NAME": "example.org", "HELM_QPS": "40"},
ns: "myns",
debug: true,
maxhistory: 5,
burstLimit: 175,
qps: 70,
kubeAsUser: "poro",
kubeAsGroups: []string{"admins", "teatime", "snackeaters"},
kubeTimeout: 45 * time.Second,
kubeCaFile: "/my/ca.crt",
kubeTLSServer: "example.org",
kubeInsecure: true,
Expand Down Expand Up @@ -145,6 +150,9 @@ func TestEnvSettings(t *testing.T) {
if !reflect.DeepEqual(tt.kubeAsGroups, settings.KubeAsGroups) {
t.Errorf("expected kAsGroups %+v, got %+v", len(tt.kubeAsGroups), len(settings.KubeAsGroups))
}
if tt.kubeTimeout != settings.KubeAPITimeout {
t.Errorf("expected KubeAPITimeout %q, got %q", tt.kubeTimeout, settings.KubeAPITimeout)
}
if tt.kubeCaFile != settings.KubeCaFile {
t.Errorf("expected kCaFile %q, got %q", tt.kubeCaFile, settings.KubeCaFile)
}
Expand Down

0 comments on commit 9a4d5e9

Please sign in to comment.