1
0
Fork 0
mirror of https://github.com/arangodb/kube-arangodb.git synced 2024-12-14 11:57:37 +00:00

[Feature] Expose HTTP Client Config (#1680)

This commit is contained in:
Adam Janikowski 2024-07-16 12:52:01 +02:00 committed by GitHub
parent a0234751d6
commit 70cdbff501
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 296 additions and 112 deletions

View file

@ -4,6 +4,7 @@
- (Maintenance) Go 1.22.4 & Kubernetes 1.29.6 libraries
- (Feature) Fix CRD Schema types
- (Bugfix) Adjust Prometheus Monitor labels
- (Feature) Expose HTTP Client Config
## [1.2.41](https://github.com/arangodb/kube-arangodb/tree/1.2.41) (2024-05-24)
- (Maintenance) Bump Prometheus API Version

View file

@ -166,6 +166,14 @@ Flags:
--deployment.feature.upgrade-version-check Enable initContainer with pre version check - Required ArangoDB 3.8.0 or higher (default true)
--deployment.feature.upgrade-version-check-v2 Enable initContainer with pre version check based by Operator - Required ArangoDB 3.8.0 or higher
--features-config-map-name string Name of the Feature Map ConfigMap (default "arangodb-operator-feature-config-map")
--http1.keep-alive If false, disables HTTP keep-alives and will only use the connection to the server for a single HTTP request (default true)
--http1.transport.dial-timeout duration Maximum amount of time a dial will wait for a connect to complete (default 30s)
--http1.transport.idle-conn-timeout duration Maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. Zero means no limit (default 1m30s)
--http1.transport.idle-conn-timeout-short duration Maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. Zero means no limit (default 100ms)
--http1.transport.keep-alive-timeout duration Interval between keep-alive probes for an active network connection (default 1m30s)
--http1.transport.keep-alive-timeout-short duration Interval between keep-alive probes for an active network connection (default 100ms)
--http1.transport.max-idle-conns int Maximum number of idle (keep-alive) connections across all hosts. Zero means no limit (default 100)
--http1.transport.tls-handshake-timeout duration Maximum amount of time to wait for a TLS handshake. Zero means no timeout (default 10s)
--image.discovery.status Discover Operator Image from Pod Status by default. When disabled Pod Spec is used. (default true)
--image.discovery.timeout duration Timeout for image discovery process (default 1m0s)
--internal.scaling-integration Enable Scaling Integration

View file

@ -262,6 +262,9 @@ func init() {
if err := reconcile.ActionsConfigGlobal.Init(&cmdMain); err != nil {
panic(err.Error())
}
if err := operatorHTTP.InitConfiguration(&cmdMain); err != nil {
panic(err.Error())
}
}
func Command() *cobra.Command {

View file

@ -22,12 +22,9 @@ package deployment
import (
"context"
"crypto/tls"
"fmt"
"net"
nhttp "net/http"
"strconv"
"time"
core "k8s.io/api/core/v1"
apiErrors "k8s.io/apimachinery/pkg/api/errors"
@ -53,9 +50,11 @@ import (
"github.com/arangodb/kube-arangodb/pkg/deployment/resources"
"github.com/arangodb/kube-arangodb/pkg/operator/scope"
"github.com/arangodb/kube-arangodb/pkg/replication"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/constants"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
"github.com/arangodb/kube-arangodb/pkg/util/globals"
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector"
persistentvolumeclaimv1 "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/persistentvolumeclaim/v1"
@ -219,24 +218,7 @@ func (d *Deployment) GetAgency(ctx context.Context, agencyIDs ...string) (agency
}
func (d *Deployment) getConnConfig() (http.ConnectionConfig, error) {
transport := &nhttp.Transport{
Proxy: nhttp.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 100 * time.Millisecond,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 100 * time.Millisecond,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
if d.GetSpec().TLS.IsSecure() {
transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
}
transport := operatorHTTP.RoundTripperWithShortTransport(operatorHTTP.WithTransportTLS(util.BoolSwitch(d.GetSpec().TLS.IsSecure(), operatorHTTP.Insecure, nil)))
connConfig := http.ConnectionConfig{
Transport: transport,

View file

@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -43,6 +43,7 @@ import (
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/constants"
"github.com/arangodb/kube-arangodb/pkg/util/crypto"
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector"
memberTls "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls"
@ -450,10 +451,7 @@ func checkServerValidCertRequest(ctx context.Context, context PlanBuilderContext
endpoint = fmt.Sprintf("https://%s:%d%s", k8sutil.CreatePodDNSNameWithDomain(apiObject, context.GetSpec().ClusterDomain, group.AsRole(), member.ID), shared.ArangoSyncMasterPort, shared.ArangoSyncStatusEndpoint)
}
tlsConfig := &tls.Config{
RootCAs: ca.AsCertPool(),
}
transport := &http.Transport{TLSClientConfig: tlsConfig}
transport := operatorHTTP.RoundTripper(operatorHTTP.WithTransportTLS(operatorHTTP.WithRootCA(ca.AsCertPool())))
client := &http.Client{Transport: transport, Timeout: time.Second}
auth, err := context.GetAuthentication()()

View file

@ -21,14 +21,15 @@
package exporter
import (
"crypto/tls"
"io"
"net/http"
"strings"
"sync"
"time"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
)
var _ http.Handler = &passthru{}
@ -44,17 +45,13 @@ type httpClientFactory func(endpoint string) (*http.Client, *http.Request, error
func newHttpClientFactory(auth Authentication, sslVerify bool, timeout time.Duration) httpClientFactory {
return func(endpoint string) (*http.Client, *http.Request, error) {
transport := &http.Transport{}
transport := operatorHTTP.Transport(operatorHTTP.WithTransportTLS(util.BoolSwitch(sslVerify, operatorHTTP.Insecure, nil)))
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return nil, nil, errors.WithStack(err)
}
if !sslVerify {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
jwt, err := auth()
if err != nil {
return nil, nil, err

View file

@ -23,16 +23,15 @@ package client
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"io"
"net"
"net/http"
"net/url"
"time"
"github.com/arangodb/kube-arangodb/pkg/storage/provisioner"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
)
// New creates a new client for the provisioner API.
@ -44,39 +43,23 @@ func New(endpoint string) (provisioner.API, error) {
u.Path = ""
return &client{
endpoint: *u,
client: &http.Client{
Transport: operatorHTTP.RoundTripper(operatorHTTP.WithTransportTLS(operatorHTTP.Insecure)),
Timeout: defaultHTTPTimeout,
},
}, nil
}
type client struct {
endpoint url.URL
client *http.Client
}
const (
defaultHTTPTimeout = time.Minute * 2
)
var (
httpClient = &http.Client{
Timeout: defaultHTTPTimeout,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 90 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
ExpectContinueTimeout: 1 * time.Second,
},
}
)
// GetNodeInfo fetches information from the current node.
func (c *client) GetNodeInfo(ctx context.Context) (provisioner.NodeInfo, error) {
req, err := c.newRequest("GET", "/nodeinfo", nil)
@ -165,7 +148,7 @@ func (c *client) newRequest(method string, localPath string, body interface{}) (
// do performs the given request and parses the result.
func (c *client) do(ctx context.Context, req *http.Request, result interface{}) error {
req = req.WithContext(ctx)
resp, err := httpClient.Do(req)
resp, err := c.client.Do(req)
if err != nil {
// Request failed
return errors.WithStack(err)

View file

@ -22,11 +22,9 @@ package arangod
import (
"context"
"crypto/tls"
"net"
nhttp "net/http"
"strconv"
"time"
typedCore "k8s.io/client-go/kubernetes/typed/core/v1"
@ -39,6 +37,7 @@ import (
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
"github.com/arangodb/kube-arangodb/pkg/util/globals"
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
)
@ -61,58 +60,21 @@ func WithRequireAuthentication(ctx context.Context) context.Context {
return context.WithValue(ctx, requireAuthenticationKey{}, true)
}
var (
sharedHTTPTransport = &nhttp.Transport{
Proxy: nhttp.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 90 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
sharedHTTPSTransport = &nhttp.Transport{
Proxy: nhttp.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 90 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
sharedHTTPTransportShortTimeout = &nhttp.Transport{
Proxy: nhttp.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 100 * time.Millisecond,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 100 * time.Millisecond,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
sharedHTTPSTransportShortTimeout = &nhttp.Transport{
Proxy: nhttp.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 100 * time.Millisecond,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 100 * time.Millisecond,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
)
func sharedHTTPTransport() nhttp.RoundTripper {
return operatorHTTP.Transport()
}
func sharedHTTPSTransport() nhttp.RoundTripper {
return operatorHTTP.Transport(operatorHTTP.WithTransportTLS(operatorHTTP.Insecure))
}
func sharedHTTPTransportShortTimeout() nhttp.RoundTripper {
return operatorHTTP.RoundTripperWithShortTransport()
}
func sharedHTTPSTransportShortTimeout() nhttp.RoundTripper {
return operatorHTTP.RoundTripperWithShortTransport(operatorHTTP.WithTransportTLS(operatorHTTP.Insecure))
}
// CreateArangodClient creates a go-driver client for a specific member in the given group.
func CreateArangodClient(ctx context.Context, cli typedCore.CoreV1Interface, apiObject *api.ArangoDeployment, group api.ServerGroup, id string, asyncSupport bool) (driver.Client, error) {
@ -192,7 +154,7 @@ func createArangodHTTPConfigForDNSNames(apiObject *api.ArangoDeployment, dnsName
}
}
connConfig := http.ConnectionConfig{
Transport: transport,
Transport: transport(),
DontFollowRedirect: true,
}
for _, dnsName := range dnsNames {

53
pkg/util/http/client.go Normal file
View file

@ -0,0 +1,53 @@
//
// DISCLAIMER
//
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package http
import (
"crypto/tls"
"crypto/x509"
"net/http"
)
func RoundTripper(mods ...TransportMod) http.RoundTripper {
df := append([]TransportMod{
configuration.DefaultTransport,
}, mods...)
return Transport(df...)
}
func RoundTripperWithShortTransport(mods ...TransportMod) http.RoundTripper {
df := append([]TransportMod{
configuration.ShortTransport,
}, mods...)
return Transport(df...)
}
func Insecure(in *tls.Config) {
in.InsecureSkipVerify = true
}
func WithRootCA(ca *x509.CertPool) TransportTLSMod {
return func(in *tls.Config) {
in.RootCAs = ca
}
}

View file

@ -0,0 +1,140 @@
//
// DISCLAIMER
//
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package http
import (
"net"
"net/http"
"time"
"github.com/spf13/cobra"
)
func InitConfiguration(cmd *cobra.Command) error {
return configuration.Init(cmd)
}
const (
defaultTransportKeepAlive bool = true
defaultTransportForceAttemptHTTP2 bool = false
defaultTransportMaxIdleConns int = 100
defaultTransportDialTimeout = 30 * time.Second
defaultTransportKeepAliveTimeout = 90 * time.Second
defaultTransportKeepAliveTimeoutShort = 100 * time.Millisecond
defaultTransportIdleConnTimeout = 90 * time.Second
defaultTransportIdleConnTimeoutShort = 100 * time.Millisecond
defaultTransportTLSHandshakeTimeout = 10 * time.Second
defaultTransportExpectContinueTimeout = 1 * time.Second
)
var configuration = configurationObject{
TransportKeepAlive: defaultTransportKeepAlive,
TransportForceAttemptHTTP2: defaultTransportForceAttemptHTTP2,
TransportMaxIdleConns: defaultTransportMaxIdleConns,
TransportDialTimeout: defaultTransportDialTimeout,
TransportKeepAliveTimeout: defaultTransportKeepAliveTimeout,
TransportKeepAliveTimeoutShort: defaultTransportKeepAliveTimeoutShort,
TransportIdleConnTimeout: defaultTransportIdleConnTimeout,
TransportIdleConnTimeoutShort: defaultTransportIdleConnTimeoutShort,
TransportTLSHandshakeTimeout: defaultTransportTLSHandshakeTimeout,
TransportExpectContinueTimeout: defaultTransportExpectContinueTimeout,
}
type configurationObject struct {
TransportKeepAlive bool
TransportForceAttemptHTTP2 bool
TransportMaxIdleConns int
TransportDialTimeout time.Duration
TransportKeepAliveTimeout time.Duration
TransportKeepAliveTimeoutShort time.Duration
TransportIdleConnTimeout time.Duration
TransportIdleConnTimeoutShort time.Duration
TransportTLSHandshakeTimeout time.Duration
TransportExpectContinueTimeout time.Duration
}
func (c *configurationObject) Init(cmd *cobra.Command) error {
if c == nil {
return nil
}
f := cmd.PersistentFlags()
f.BoolVar(&configuration.TransportKeepAlive, "http1.keep-alive", defaultTransportKeepAlive, "If false, disables HTTP keep-alives and will only use the connection to the server for a single HTTP request")
f.BoolVar(&configuration.TransportForceAttemptHTTP2, "http1.force-attempt-http2", defaultTransportForceAttemptHTTP2, "controls whether HTTP/2 is enabled")
f.IntVar(&configuration.TransportMaxIdleConns, "http1.transport.max-idle-conns", defaultTransportMaxIdleConns, "Maximum number of idle (keep-alive) connections across all hosts. Zero means no limit")
f.DurationVar(&configuration.TransportDialTimeout, "http1.transport.dial-timeout", defaultTransportDialTimeout, "Maximum amount of time a dial will wait for a connect to complete")
f.DurationVar(&configuration.TransportKeepAliveTimeout, "http1.transport.keep-alive-timeout", defaultTransportKeepAliveTimeout, "Interval between keep-alive probes for an active network connection")
f.DurationVar(&configuration.TransportKeepAliveTimeoutShort, "http1.transport.keep-alive-timeout-short", defaultTransportKeepAliveTimeoutShort, "Interval between keep-alive probes for an active network connection")
f.DurationVar(&configuration.TransportIdleConnTimeout, "http1.transport.idle-conn-timeout", defaultTransportIdleConnTimeout, "Maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. Zero means no limit")
f.DurationVar(&configuration.TransportIdleConnTimeoutShort, "http1.transport.idle-conn-timeout-short", defaultTransportIdleConnTimeoutShort, "Maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. Zero means no limit")
f.DurationVar(&configuration.TransportTLSHandshakeTimeout, "http1.transport.tls-handshake-timeout", defaultTransportTLSHandshakeTimeout, "Maximum amount of time to wait for a TLS handshake. Zero means no timeout")
f.DurationVar(&configuration.TransportExpectContinueTimeout, "http1.transport.except-continue-timeout", defaultTransportExpectContinueTimeout, "")
if err := f.MarkHidden("http1.transport.except-continue-timeout"); err != nil {
return err
}
if err := f.MarkHidden("http1.force-attempt-http2"); err != nil {
return err
}
return nil
}
func (c *configurationObject) DefaultTransport(in *http.Transport) {
if c == nil {
return
}
in.Proxy = http.ProxyFromEnvironment
in.ForceAttemptHTTP2 = c.TransportForceAttemptHTTP2
in.DialContext = (&net.Dialer{
Timeout: c.TransportDialTimeout,
KeepAlive: c.TransportKeepAliveTimeout,
DualStack: true,
}).DialContext
in.MaxIdleConns = c.TransportMaxIdleConns
in.IdleConnTimeout = c.TransportIdleConnTimeout
in.TLSHandshakeTimeout = c.TransportTLSHandshakeTimeout
in.ExpectContinueTimeout = c.TransportExpectContinueTimeout
in.DisableKeepAlives = !c.TransportKeepAlive
}
func (c *configurationObject) ShortTransport(in *http.Transport) {
if c == nil {
return
}
in.DialContext = (&net.Dialer{
Timeout: c.TransportDialTimeout,
KeepAlive: c.TransportKeepAliveTimeoutShort,
DualStack: true,
}).DialContext
in.IdleConnTimeout = c.TransportIdleConnTimeoutShort
}

View file

@ -0,0 +1,57 @@
//
// DISCLAIMER
//
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package http
import (
"crypto/tls"
"net/http"
)
type Mod[T any] func(in T)
type TransportMod Mod[*http.Transport]
type TransportTLSMod Mod[*tls.Config]
func Transport(mods ...TransportMod) http.RoundTripper {
var c http.Transport
for _, m := range mods {
if m == nil {
continue
}
m(&c)
}
return &c
}
func WithTransportTLS(mods ...TransportTLSMod) TransportMod {
return func(in *http.Transport) {
if in.TLSClientConfig == nil {
in.TLSClientConfig = &tls.Config{}
}
for _, mod := range mods {
mod(in.TLSClientConfig)
}
}
}