Skip to content

Commit bb9f444

Browse files
feat(CR-31802): add configurable http client transpor (#174)
## What http client can be configured via environment variables ## Why it potentialy helps prevent "connection reset by peer" errors ## Notes <!-- Add any notes here --> ## Checklist * [x] _I have read [CONTRIBUTING.md](https://github.com/codefresh-io/terraform-provider-codefresh/blob/master/CONTRIBUTING.md)._ * [ ] _I have [allowed changes to my fork to be made](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)._ * [ ] _I have added tests, assuming new tests are warranted_. * [ ] _I understand that the `/test` comment will be ignored by the CI trigger [unless it is made by a repo admin or collaborator](https://codefresh.io/docs/docs/pipelines/triggers/git-triggers/#support-for-building-pull-requests-from-forks)._
1 parent 51e2702 commit bb9f444

File tree

6 files changed

+72
-14
lines changed

6 files changed

+72
-14
lines changed

codefresh/cfclient/client.go

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import (
77
"io"
88
"net/http"
99
"strings"
10+
"time"
11+
12+
"github.com/codefresh-io/terraform-provider-codefresh/codefresh/envutil"
1013
)
1114

1215
// Client token, host, htpp.Client
@@ -28,19 +31,41 @@ type RequestOptions struct {
2831
XAccessToken string
2932
}
3033

31-
// NewClient returns a new client configured to communicate on a server with the
32-
// given hostname and to send an Authorization Header with the value of
33-
// token
34-
func NewClient(hostname string, hostnameV2 string, token string, tokenHeader string) *Client {
34+
// HttpClient returns a client which can be configured to communicate on a server with custom timeout settings
35+
func NewHttpClient(hostname string, hostnameV2 string, token string, tokenHeader string) *Client {
3536
if tokenHeader == "" {
3637
tokenHeader = "Authorization"
3738
}
39+
40+
// Configurable HTTP transport with proper connection pooling and timeouts to prevent "connection reset by peer" errors,
41+
// default values are equivalent to default &http.Client{} settings
42+
transport := &http.Transport{
43+
// Limit maximum idle connections per host to prevent connection exhaustion
44+
MaxIdleConnsPerHost: envutil.GetEnvAsInt("CF_HTTP_MAX_IDLE_CONNECTIONS_PER_HOST", 2),
45+
// Limit total idle connections
46+
MaxIdleConns: envutil.GetEnvAsInt("CF_HTTP_MAX_IDLE_CONNECTIONS", 100),
47+
// Close idle connections after specified seconds to prevent server-side timeouts
48+
IdleConnTimeout: time.Duration(envutil.GetEnvAsInt("CF_HTTP_IDLE_CONNECTION_TIMEOUT", 90)) * time.Second,
49+
// Timeout for TLS handshake in seconds
50+
TLSHandshakeTimeout: time.Duration(envutil.GetEnvAsInt("CF_HTTP_TLS_HANDSHAKE_TIMEOUT", 10)) * time.Second,
51+
// Timeout for expecting response headers in seconds, 0 - no limits
52+
ResponseHeaderTimeout: time.Duration(envutil.GetEnvAsInt("CF_HTTP_RESPONSE_HEADER_TIMEOUT", 0)) * time.Second,
53+
// Disable connection reuse for more stable connections
54+
DisableKeepAlives: envutil.GetEnvAsBool("CF_HTTP_DISABLE_KEEPALIVES", false),
55+
}
56+
57+
httpClient := &http.Client{
58+
Transport: transport,
59+
// Overall request timeout in seconds, 0 - no limits
60+
Timeout: time.Duration(envutil.GetEnvAsInt("CF_HTTP_GLOBAL_TIMEOUT", 0)) * time.Second,
61+
}
62+
3863
return &Client{
3964
Host: hostname,
4065
HostV2: hostnameV2,
4166
Token: token,
4267
TokenHeader: tokenHeader,
43-
Client: &http.Client{},
68+
Client: httpClient,
4469
featureFlags: map[string]bool{},
4570
}
4671

codefresh/cfclient/gql_client.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ func (client *Client) SendGqlRequest(request GraphQLRequest) ([]byte, error) {
3737
req.Header.Set(tokenHeader, client.Token)
3838
req.Header.Set("Content-Type", "application/json; charset=utf-8")
3939

40-
httpClient := &http.Client{}
41-
resp, err := httpClient.Do(req)
40+
resp, err := client.Client.Do(req)
4241
if err != nil {
4342
return nil, err
4443
}

codefresh/cfclient/user.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func (client *Client) AddUserToTeamByAdmin(userID string, accountID string, team
144144
return err
145145
}
146146
// new Client for accountAdmin
147-
accountAdminClient := NewClient(client.Host, "", accountAdminToken, "x-access-token")
147+
accountAdminClient := NewHttpClient(client.Host, "", accountAdminToken, "x-access-token")
148148
usersTeam, err := accountAdminClient.GetTeamByName(team)
149149
if err != nil {
150150
return err

codefresh/envutil/env.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Package envutil provides utility functions for working with environment variables.
2+
package envutil
3+
4+
import (
5+
"os"
6+
"strconv"
7+
)
8+
9+
// GetEnvAsString retrieves an environment variable as a string, returning the default value if not set
10+
func GetEnvAsString(key, defaultValue string) string {
11+
if value := os.Getenv(key); value != "" {
12+
return value
13+
}
14+
return defaultValue
15+
}
16+
17+
// GetEnvAsInt retrieves an environment variable as an integer, returning the default value if not set or invalid
18+
func GetEnvAsInt(key string, defaultValue int) int {
19+
if value := os.Getenv(key); value != "" {
20+
if intValue, err := strconv.Atoi(value); err == nil {
21+
return intValue
22+
}
23+
}
24+
return defaultValue
25+
}
26+
27+
// GetEnvAsBool retrieves an environment variable as a boolean, returning the default value if not set or invalid
28+
func GetEnvAsBool(key string, defaultValue bool) bool {
29+
if value := os.Getenv(key); value != "" {
30+
if boolValue, err := strconv.ParseBool(value); err == nil {
31+
return boolValue
32+
}
33+
}
34+
return defaultValue
35+
}

codefresh/provider.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package codefresh
33
import (
44
"fmt"
55

6+
"os"
7+
68
"github.com/codefresh-io/terraform-provider-codefresh/codefresh/cfclient"
79
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8-
"os"
910
)
1011

1112
func Provider() *schema.Provider {
@@ -91,5 +92,5 @@ func configureProvider(d *schema.ResourceData) (interface{}, error) {
9192
token = os.Getenv(ENV_CODEFRESH_API_KEY)
9293
}
9394

94-
return cfclient.NewClient(apiURL, apiURLV2, token, ""), nil
95+
return cfclient.NewHttpClient(apiURL, apiURLV2, token, ""), nil
9596
}

main.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"os"
55

66
"github.com/codefresh-io/terraform-provider-codefresh/codefresh"
7+
"github.com/codefresh-io/terraform-provider-codefresh/codefresh/envutil"
78
"github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
89
)
910

@@ -12,10 +13,7 @@ import (
1213

1314
func main() {
1415
debugMode := (os.Getenv(codefresh.ENV_CODEFRESH_PLUGIN_DEBUG) != "")
15-
providerAddr := os.Getenv(codefresh.ENV_CODEFRESH_PLUGIN_ADDR)
16-
if providerAddr == "" {
17-
providerAddr = codefresh.DEFAULT_CODEFRESH_PLUGIN_ADDR
18-
}
16+
providerAddr := envutil.GetEnvAsString(codefresh.ENV_CODEFRESH_PLUGIN_ADDR, codefresh.DEFAULT_CODEFRESH_PLUGIN_ADDR)
1917
plugin.Serve(&plugin.ServeOpts{
2018
ProviderAddr: providerAddr,
2119
ProviderFunc: codefresh.Provider,

0 commit comments

Comments
 (0)