diff --git a/cmd/flags/target.go b/cmd/flags/target.go index e01f415..ebdc094 100644 --- a/cmd/flags/target.go +++ b/cmd/flags/target.go @@ -24,16 +24,18 @@ import ( // Target stores flags related to the target. type Target struct { - HTTPHost string - HTTPPort int - GrpcHost string - GrpcPort int - ReadinessProtocol string - ReadinessHTTPPath string - ReadinessHTTPHost string - ReadinessGrpcMethod string - ReadinessPort int - Insecure bool + HTTPHost string + HTTPPort int + HTTPTimeoutMilliseconds int + GrpcHost string + GrpcPort int + GrpcTimeoutMilliseconds int + ReadinessProtocol string + ReadinessHTTPPath string + ReadinessHTTPHost string + ReadinessGrpcMethod string + ReadinessPort int + Insecure bool } func (t *Target) String() string { @@ -43,8 +45,10 @@ func (t *Target) String() string { func (t *Target) initFlags() { flag.StringVar(&t.HTTPHost, "target-http-host", "http://localhost", "HTTP host to warm up") flag.IntVar(&t.HTTPPort, "target-http-port", 8080, "HTTP port for warm up requests") + flag.IntVar(&t.HTTPTimeoutMilliseconds, "target-http-timeout-milliseconds", 10000, "HTTP timeout for requests") flag.StringVar(&t.GrpcHost, "target-grpc-host", "localhost", "Grpc host to warm up") flag.IntVar(&t.GrpcPort, "target-grpc-port", 50051, "Grpc port for warm up requests") + flag.IntVar(&t.GrpcTimeoutMilliseconds, "target-grpc-timeout-milliseconds", 1000, "Grpc timeout for requests") flag.StringVar(&t.ReadinessProtocol, "target-readiness-protocol", "http", "Protocol to be used for readiness check. One of [http, grpc]") flag.StringVar(&t.ReadinessHTTPPath, "target-readiness-http-path", "/ready", "The path used for HTTP target readiness probe") flag.StringVar(&t.ReadinessHTTPHost, "target-readiness-http-host", toStringOrDefaultIfNull(&t.HTTPHost, "http://localhost"), "The HTTP host used for target readiness probe") @@ -78,17 +82,17 @@ func (t *Target) getWarmupTargetOptions() warmup.TargetOptions { } func (t *Target) getReadinessHTTPClient() http.Client { - return http.NewClient(fmt.Sprintf("%s:%d", t.ReadinessHTTPHost, t.ReadinessPort), t.Insecure) + return http.NewClient(fmt.Sprintf("%s:%d", t.ReadinessHTTPHost, t.ReadinessPort), t.Insecure, t.HTTPTimeoutMilliseconds) } func (t *Target) getReadinessGrpcClient() grpc.Client { - return grpc.NewClient(fmt.Sprintf("%s:%d", t.GrpcHost, t.ReadinessPort), t.Insecure) + return grpc.NewClient(fmt.Sprintf("%s:%d", t.GrpcHost, t.ReadinessPort), t.Insecure, t.GrpcTimeoutMilliseconds) } func (t *Target) getHTTPClient() http.Client { - return http.NewClient(fmt.Sprintf("%s:%d", t.HTTPHost, t.HTTPPort), t.Insecure) + return http.NewClient(fmt.Sprintf("%s:%d", t.HTTPHost, t.HTTPPort), t.Insecure, t.HTTPTimeoutMilliseconds) } func (t *Target) getGrpcClient() grpc.Client { - return grpc.NewClient(fmt.Sprintf("%s:%d", t.GrpcHost, t.GrpcPort), t.Insecure) + return grpc.NewClient(fmt.Sprintf("%s:%d", t.GrpcHost, t.GrpcPort), t.Insecure, t.GrpcTimeoutMilliseconds) } diff --git a/docs/about/getting-started.md b/docs/about/getting-started.md index 28e8dec..a80bf87 100644 --- a/docs/about/getting-started.md +++ b/docs/about/getting-started.md @@ -11,32 +11,34 @@ The application receives a number of command-line flags including the requests t ## Flags -| Flag | Type | Default value | Description | -|:---------------------------------|:--------|:----------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| -concurrency | int | 2 | Number of concurrent requests for warm up | -| -exit-after-warmup | bool | false | If mittens should exit after completion of warm up | -| -http-headers | strings | N/A | Http headers to be sent with warm up requests. To send multiple headers define this flag for each header | -| -grpc-requests | strings | N/A | gRPC requests to be sent. Request is in '\\\[:message\]' format. E.g. health/ping:{"key": "value"}. To send multiple requests, simply repeat this flag for each request. Use the notation `:file/xyz.json` if you want to use an external file for the request body. | -| -http-requests | string | N/A | Http request to be sent. Request is in `:[:body]` format. E.g. `post:/ping:{"key": "value"}`. To send multiple requests, simply repeat this flag for each request. Use the notation `:file/xyz.json` if you want to use an external file for the request body. | -| -fail-readiness | bool | false | If set to true readiness will fail if the target did not became ready in time | -| -file-probe-enabled | bool | true | If set to true writes files that can be used as readiness/liveness probes. a file with the name `alive` is created when Mittens starts and a file named `ready` is created when the warmup completes | -| -file-probe-liveness-path | string | alive | File to be used for liveness probe | -| -file-probe-readiness-path | string | ready | File to be used for readiness probe | -| -request-delay-milliseconds | int | 500 | Delay in milliseconds between requests | -| -target-grpc-host | string | localhost | gRPC host to warm up | -| -target-grpc-port | int | 50051 | gRPC port for warm up requests | -| -target-http-host | string | http://localhost | Http host to warm up | -| -target-http-port | int | 8080 | Http port for warm up requests | -| -target-insecure | bool | false | Whether to skip TLS validation | -| -target-readiness-grpc-method | string | grpc.health.v1.Health/Check | The service method used for gRPC target readiness probe | -| -target-readiness-http-path | string | /ready | The path used for target readiness probe | -| -target-readiness-http-host | string | same as -target-http-host | The host used for target readiness probe | -| -target-readiness-port | int | same as -target-http-port | The port used for target readiness probe | -| -target-readiness-protocol | string | http | Protocol to be used for readiness check. One of [`http`, `grpc`] | -| -max-duration-seconds | int | 60 | Global maximum duration. This includes both the time spent warming up the target service and also the time waiting for the target to become ready | -| -max-readiness-wait-seconds | int | 30 | Maximum time to wait for the target to become ready | -| -max-warmup-seconds | int | 30 | Maximum time spent sending warmup requests to the target service. Please note that `max-duration-seconds` may cap this duration | -| -concurrency-target-seconds | int | 0 | Time taken to reach expected concurrency. This is useful to ramp up traffic. | +| Flag | Type | Default value | Description | +|:---------------------------------------------------------------|:--------|:----------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| -concurrency | int | 2 | Number of concurrent requests for warm up | +| -exit-after-warmup | bool | false | If mittens should exit after completion of warm up | +| -http-headers | strings | N/A | Http headers to be sent with warm up requests. To send multiple headers define this flag for each header | +| -grpc-requests | strings | N/A | gRPC requests to be sent. Request is in '\\\[:message\]' format. E.g. health/ping:{"key": "value"}. To send multiple requests, simply repeat this flag for each request. Use the notation `:file/xyz.json` if you want to use an external file for the request body. | +| -http-requests | string | N/A | Http request to be sent. Request is in `:[:body]` format. E.g. `post:/ping:{"key": "value"}`. To send multiple requests, simply repeat this flag for each request. Use the notation `:file/xyz.json` if you want to use an external file for the request body. | +| -fail-readiness | bool | false | If set to true readiness will fail if the target did not became ready in time | +| -file-probe-enabled | bool | true | If set to true writes files that can be used as readiness/liveness probes. a file with the name `alive` is created when Mittens starts and a file named `ready` is created when the warmup completes | +| -file-probe-liveness-path | string | alive | File to be used for liveness probe | +| -file-probe-readiness-path | string | ready | File to be used for readiness probe | +| -request-delay-milliseconds | int | 500 | Delay in milliseconds between requests | +| -target-grpc-host | string | localhost | gRPC host to warm up | +| -target-grpc-port | int | 50051 | gRPC port for warm up requests | +| -target-grpc-timeout-milliseconds | int | 1000 | gRPC timeout | +| -target-http-host | string | http://localhost | Http host to warm up | +| -target-http-port | int | 8080 | Http port for warm up requests | +| -target-http-timeout-milliseconds | int | 10000 | Http timeout | +| -target-insecure | bool | false | Whether to skip TLS validation | +| -target-readiness-grpc-method | string | grpc.health.v1.Health/Check | The service method used for gRPC target readiness probe | +| -target-readiness-http-path | string | /ready | The path used for target readiness probe | +| -target-readiness-http-host | string | same as -target-http-host | The host used for target readiness probe | +| -target-readiness-port | int | same as -target-http-port | The port used for target readiness probe | +| -target-readiness-protocol | string | http | Protocol to be used for readiness check. One of [`http`, `grpc`] | +| -max-duration-seconds | int | 60 | Global maximum duration. This includes both the time spent warming up the target service and also the time waiting for the target to become ready | +| -max-readiness-wait-seconds | int | 30 | Maximum time to wait for the target to become ready | +| -max-warmup-seconds | int | 30 | Maximum time spent sending warmup requests to the target service. Please note that `max-duration-seconds` may cap this duration | +| -concurrency-target-seconds | int | 0 | Time taken to reach expected concurrency. This is useful to ramp up traffic. | ### Warmup request A warmup request can be an HTTP one (over REST) or a gRPC one. diff --git a/internal/pkg/grpc/client.go b/internal/pkg/grpc/client.go index ade99d8..670a63b 100644 --- a/internal/pkg/grpc/client.go +++ b/internal/pkg/grpc/client.go @@ -36,11 +36,12 @@ import ( // Client represents a gRPC client. type Client struct { - host string - insecure bool - connClose func() error - conn *grpc.ClientConn - descriptorSource grpcurl.DescriptorSource + host string + insecure bool + timeoutMilliseconds int + connClose func() error + conn *grpc.ClientConn + descriptorSource grpcurl.DescriptorSource } // eventHandler is a custom event handler with the option to enable/disable logging of responses. @@ -50,14 +51,13 @@ type eventHandler struct { } // NewClient returns a gRPC client. -func NewClient(host string, insecure bool) Client { - return Client{host: host, insecure: insecure, connClose: func() error { return nil }} +func NewClient(host string, insecure bool, timeoutMilliseconds int) Client { + return Client{host: host, insecure: insecure, connClose: func() error { return nil }, timeoutMilliseconds: timeoutMilliseconds} } // Connect attempts to establish a connection with a gRPC server. func (c *Client) Connect(headers []string) error { - timeoutSeconds := 1 // TODO: make this configurable? - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutSeconds)*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(c.timeoutMilliseconds)*time.Millisecond) headersMetadata := grpcurl.MetadataFromHeaders(headers) contextWithMetadata := metadata.NewOutgoingContext(ctx, headersMetadata) diff --git a/internal/pkg/http/client.go b/internal/pkg/http/client.go index b645046..14ec6d0 100644 --- a/internal/pkg/http/client.go +++ b/internal/pkg/http/client.go @@ -37,9 +37,9 @@ type Client struct { // NewClient creates a new HTTP client for a given host. // If insecure is true, the client will not verify the server's certificate chain and host name. -func NewClient(host string, insecure bool) Client { +func NewClient(host string, insecure bool, timeoutMilliseconds int) Client { client := &http.Client{ - Timeout: 10 * time.Second, + Timeout: time.Duration(timeoutMilliseconds) * time.Millisecond, } client.Transport = &http.Transport{ diff --git a/internal/pkg/http/client_test.go b/internal/pkg/http/client_test.go index 171b40c..5171efd 100644 --- a/internal/pkg/http/client_test.go +++ b/internal/pkg/http/client_test.go @@ -38,14 +38,14 @@ func TestMain(m *testing.M) { } func TestRequestSuccess(t *testing.T) { - c := NewClient(serverUrl, false) + c := NewClient(serverUrl, false, 10000) reqBody := "" resp := c.SendRequest("GET", WorkingPath, []string{}, &reqBody) assert.Nil(t, resp.Err) } func TestHttpError(t *testing.T) { - c := NewClient(serverUrl, false) + c := NewClient(serverUrl, false, 10000) reqBody := "" resp := c.SendRequest("GET", "/", []string{}, &reqBody) assert.Nil(t, resp.Err) @@ -53,7 +53,7 @@ func TestHttpError(t *testing.T) { } func TestConnectionError(t *testing.T) { - c := NewClient("http://localhost:9999", false) + c := NewClient("http://localhost:9999", false, 10000) reqBody := "" resp := c.SendRequest("GET", "/potato", []string{}, &reqBody) assert.NotNil(t, resp.Err)