1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
package gitlab
import (
"context"
"crypto/tls"
"net"
"net/http"
"net/url"
"time"
"github.com/hashicorp/go-retryablehttp"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/httpz"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/tlstool"
"go.opentelemetry.io/otel"
otelmetric "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
)
const (
// Default retry configuration
defaultRetryWaitMin = 100 * time.Millisecond
defaultRetryWaitMax = 30 * time.Second
defaultRetryMax = 4
)
type RetryConfig struct {
// Logger instance. Can be either retryablehttp.Logger or retryablehttp.LeveledLogger
Logger interface{}
RetryWaitMin time.Duration // Minimum time to wait
RetryWaitMax time.Duration // Maximum time to wait
RetryMax int // Maximum number of retries
// RequestLogHook allows a user-supplied function to be called
// before each retry.
RequestLogHook retryablehttp.RequestLogHook
// ResponseLogHook allows a user-supplied function to be called
// with the response from each HTTP request executed.
ResponseLogHook retryablehttp.ResponseLogHook
// CheckRetry specifies the policy for handling retries, and is called
// after each request. The default policy is retryablehttp.DefaultRetryPolicy.
CheckRetry retryablehttp.CheckRetry
// Backoff specifies the policy for how long to wait between retries.
// retryablehttp.DefaultBackoff is used by default.
Backoff retryablehttp.Backoff
}
type transportConfig struct {
Proxy func(*http.Request) (*url.URL, error)
DialContext func(ctx context.Context, network, address string) (net.Conn, error)
TLSClientConfig *tls.Config
TLSHandshakeTimeout time.Duration
MaxIdleConns int
MaxIdleConnsPerHost int
MaxConnsPerHost int
IdleConnTimeout time.Duration
ResponseHeaderTimeout time.Duration
ForceAttemptHTTP2 bool
}
// clientConfig holds configuration for the client.
type clientConfig struct {
retryConfig RetryConfig
transportConfig transportConfig
tracePropagator propagation.TextMapPropagator
traceProvider trace.TracerProvider
meterProvider otelmetric.MeterProvider
limiter httpz.Limiter
userAgent string
}
// ClientOption to configure the client.
type ClientOption func(*clientConfig)
func applyClientOptions(opts []ClientOption) clientConfig {
dialer := &net.Dialer{
Timeout: 30 * time.Second,
}
config := clientConfig{
retryConfig: RetryConfig{
RetryWaitMin: defaultRetryWaitMin,
RetryWaitMax: defaultRetryWaitMax,
RetryMax: defaultRetryMax,
CheckRetry: retryablehttp.DefaultRetryPolicy,
Backoff: retryablehttp.DefaultBackoff,
},
transportConfig: transportConfig{
Proxy: http.ProxyFromEnvironment,
DialContext: dialer.DialContext,
TLSClientConfig: tlstool.DefaultClientTLSConfig(),
TLSHandshakeTimeout: 10 * time.Second,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 50,
MaxConnsPerHost: 50,
IdleConnTimeout: 90 * time.Second,
ResponseHeaderTimeout: 20 * time.Second,
ForceAttemptHTTP2: true,
},
tracePropagator: otel.GetTextMapPropagator(),
traceProvider: otel.GetTracerProvider(),
meterProvider: otel.GetMeterProvider(),
userAgent: "",
}
for _, v := range opts {
v(&config)
}
return config
}
// WithRetryConfig configures retry behavior.
func WithRetryConfig(retryConfig RetryConfig) ClientOption {
return func(config *clientConfig) {
config.retryConfig = retryConfig
}
}
// WithTextMapPropagator sets a custom trace propagator to be used, otherwise the OTEL's global TextMapPropagator is used.
func WithTextMapPropagator(p propagation.TextMapPropagator) ClientOption {
return func(config *clientConfig) {
config.tracePropagator = p
}
}
// WithTracerProvider sets a custom trace provider to be used, otherwise the OTEL's global TracerProvider is used.
func WithTracerProvider(traceProvider trace.TracerProvider) ClientOption {
return func(config *clientConfig) {
config.traceProvider = traceProvider
}
}
// WithMeterProvider sets a custom meter provider to be used, otherwise the OTEL's global MeterProvider is used.
func WithMeterProvider(meterProvider otelmetric.MeterProvider) ClientOption {
return func(config *clientConfig) {
config.meterProvider = meterProvider
}
}
// WithUserAgent configures the User-Agent header on the http client.
func WithUserAgent(userAgent string) ClientOption {
return func(config *clientConfig) {
config.userAgent = userAgent
}
}
// WithTLSConfig sets the TLS config to use.
func WithTLSConfig(tlsConfig *tls.Config) ClientOption {
return func(config *clientConfig) {
config.transportConfig.TLSClientConfig = tlsConfig
}
}
// WithRateLimiter sets the rate limiter to use.
func WithRateLimiter(limiter httpz.Limiter) ClientOption {
return func(config *clientConfig) {
config.limiter = limiter
}
}
|