Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for longer timeout for mutating operations (POST etc) #2401

Merged
merged 4 commits into from
Jun 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build/dev/config.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Database connectivity environment variables
DATABASE_PROVIDER=sqlite
HTTP_CONNECTION_TIMEOUT_IN_SECS=10
HTTP_CLIENT_TIMEOUT_IN_SECS=20
HTTP_CLIENT_TIMEOUT_IN_SECS=30
HTTP_CLIENT_TIMEOUT_MUTATING_IN_SECS=120
SKIP_SSL_VALIDATION=true
CONSOLE_PROXY_TLS_ADDRESS=:5443
CONSOLE_CLIENT=console
Expand Down
3 changes: 2 additions & 1 deletion deploy/all-in-one/config.all-in-one.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Database connectivity environment variables
DATABASE_PROVIDER=sqlite
HTTP_CONNECTION_TIMEOUT_IN_SECS=10
HTTP_CLIENT_TIMEOUT_IN_SECS=20
HTTP_CLIENT_TIMEOUT_IN_SECS=30
HTTP_CLIENT_TIMEOUT_MUTATING_IN_SECS=120
CONSOLE_PROXY_TLS_ADDRESS=:443
CF_ADMIN_ROLE=cloud_controller.admin
CF_CLIENT=cf
Expand Down
3 changes: 2 additions & 1 deletion deploy/cloud-foundry/config.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Database connectivity environment variables
DATABASE_PROVIDER=sqlite
HTTP_CONNECTION_TIMEOUT_IN_SECS=10
HTTP_CLIENT_TIMEOUT_IN_SECS=20
HTTP_CLIENT_TIMEOUT_IN_SECS=30
HTTP_CLIENT_TIMEOUT_MUTATING_IN_SECS=120
SKIP_SSL_VALIDATION=true
CONSOLE_PROXY_TLS_ADDRESS=:443
CONSOLE_CLIENT=console
Expand Down
4 changes: 3 additions & 1 deletion deploy/kubernetes/console/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ spec:
- name: HTTP_CONNECTION_TIMEOUT_IN_SECS
value: "10"
- name: HTTP_CLIENT_TIMEOUT_IN_SECS
value: "20"
value: "30"
- name: HTTP_CLIENT_TIMEOUT_MUTATING_IN_SECS
value: "120"
- name: SKIP_TLS_VERIFICATION
value: "false"
- name: CONSOLE_PROXY_TLS_ADDRESS
Expand Down
3 changes: 2 additions & 1 deletion deploy/proxy.env
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ DB_DATABASE_NAME=stratos-db
DB_HOST=mariadb
DB_PORT=3306
HTTP_CONNECTION_TIMEOUT_IN_SECS=10
HTTP_CLIENT_TIMEOUT_IN_SECS=20
HTTP_CLIENT_TIMEOUT_IN_SECS=30
HTTP_CLIENT_TIMEOUT_MUTATING_IN_SECS=120
SKIP_SSL_VALIDATION=true
CONSOLE_PROXY_TLS_ADDRESS=:3003
CONSOLE_CLIENT=console
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
HTTP_CONNECTION_TIMEOUT_IN_SECS=10
HTTP_CLIENT_TIMEOUT_IN_SECS=20
HTTP_CLIENT_TIMEOUT_IN_SECS=30
HTTP_CLIENT_TIMEOUT_MUTATING_IN_SECS=120
SKIP_SSL_VALIDATION=<%= p('stratos_ui.backend.skip_ssl_validation') %>
CONSOLE_PROXY_TLS_ADDRESS=<%= p('stratos_ui.backend.address') %>:<%= p('stratos_ui.backend.port') %>
CF_CLIENT=cf
Expand Down
8 changes: 1 addition & 7 deletions src/backend/app-core/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,13 +449,7 @@ func (p *portalProxy) getUAAToken(body url.Values, skipSSLValidation bool, clien
req.SetBasicAuth(client, clientSecret)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)

var h http.Client
if skipSSLValidation {
h = httpClientSkipSSL
} else {
h = httpClient
}

var h = p.GetHttpClientForRequest(req, skipSSLValidation)
res, err := h.Do(req)
if err != nil || res.StatusCode != http.StatusOK {
log.Errorf("Error performing http request - response: %v, error: %v", res, err)
Expand Down
3 changes: 2 additions & 1 deletion src/backend/app-core/development.rc.sample
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export DB_PORT=5432
export DB_CONNECT_TIMEOUT_IN_SECS=100
export DB_SSL_MODE='disable'

export HTTP_CLIENT_TIMEOUT_IN_SECS=100
export HTTP_CLIENT_TIMEOUT_IN_SECS=30
export HTTP_CLIENT_TIMEOUT_MUTATING_IN_SECS=120
export SKIP_SSL_VALIDATION='true'
export TLS_ADDRESS=':8080'
export CONSOLE_CLIENT='portal-proxy'
Expand Down
7 changes: 1 addition & 6 deletions src/backend/app-core/http_basic_requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ func (p *portalProxy) doHttpBasicFlowRequest(cnsiRequest *interfaces.CNSIRequest

// Http Basic has no token refresh or expiry - so much simpler than the OAuth flow
req.Header.Set("Authorization", "basic "+tokenRec.AuthToken)
var client http.Client
if cnsi.SkipSSLValidation {
client = httpClientSkipSSL
} else {
client = httpClient
}
client := p.GetHttpClientForRequest(req, cnsi.SkipSSLValidation)
return client.Do(req)
}
41 changes: 36 additions & 5 deletions src/backend/app-core/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,12 @@ const (
var appVersion string

var (
// Standard clients
httpClient = http.Client{}
httpClientSkipSSL = http.Client{}
// Clients to use typically for mutating operations - typically allow a longer request timeout
httpClientMutating = http.Client{}
httpClientMutatingSkipSSL = http.Client{}
)

func cleanup(dbc *sql.DB, ss HttpSessionStore) {
Expand Down Expand Up @@ -102,7 +106,7 @@ func main() {
log.Infof("Stratos Version: %s", portalConfig.ConsoleVersion)

// Initialize the HTTP client
initializeHTTPClients(portalConfig.HTTPClientTimeoutInSecs, portalConfig.HTTPConnectionTimeoutInSecs)
initializeHTTPClients(portalConfig.HTTPClientTimeoutInSecs, portalConfig.HTTPClientTimeoutMutatingInSecs, portalConfig.HTTPConnectionTimeoutInSecs)
log.Info("HTTP client initialized.")

// Get the encryption key we need for tokens in the database
Expand Down Expand Up @@ -366,6 +370,11 @@ func loadPortalConfig(pc interfaces.PortalConfig) (interfaces.PortalConfig, erro
pc.HTTPS = true
pc.PluginConfig = make(map[string]string)

// Default to standard timeout if the mutating one is not configured
if pc.HTTPClientTimeoutMutatingInSecs == 0 {
pc.HTTPClientTimeoutMutatingInSecs = pc.HTTPClientTimeoutInSecs
}

return pc, nil
}

Expand Down Expand Up @@ -441,7 +450,7 @@ func newPortalProxy(pc interfaces.PortalConfig, dcp *sql.DB, ss HttpSessionStore
return pp
}

func initializeHTTPClients(timeout int64, connectionTimeout int64) {
func initializeHTTPClients(timeout int64, timeoutMutating int64, connectionTimeout int64) {
log.Debug("initializeHTTPClients")

// Common KeepAlive dialer shared by both transports
Expand Down Expand Up @@ -471,6 +480,11 @@ func initializeHTTPClients(timeout int64, connectionTimeout int64) {
httpClientSkipSSL.Transport = trSkipSSL
httpClientSkipSSL.Timeout = time.Duration(timeout) * time.Second

// Clients with longer timeouts (use for mutating operations)
httpClientMutating.Transport = tr
httpClientMutating.Timeout = time.Duration(timeoutMutating) * time.Second
httpClientMutatingSkipSSL.Transport = trSkipSSL
httpClientMutatingSkipSSL.Timeout = time.Duration(timeoutMutating) * time.Second
}

func start(config interfaces.PortalConfig, p *portalProxy, addSetupMiddleware *setupMiddleware, isUpgrade bool) error {
Expand Down Expand Up @@ -552,11 +566,28 @@ func (p *portalProxy) GetEndpointTypeSpec(typeName string) (interfaces.EndpointP
}

func (p *portalProxy) GetHttpClient(skipSSLValidation bool) http.Client {
return p.getHttpClient(skipSSLValidation, false)
}

func (p *portalProxy) GetHttpClientForRequest(req *http.Request, skipSSLValidation bool) http.Client {
isMutating := req.Method != "GET" && req.Method != "HEAD"
return p.getHttpClient(skipSSLValidation, isMutating)
}

func (p *portalProxy) getHttpClient(skipSSLValidation bool, mutating bool) http.Client {
var client http.Client
if skipSSLValidation {
client = httpClientSkipSSL
if !mutating {
if skipSSLValidation {
client = httpClientSkipSSL
} else {
client = httpClient
}
} else {
client = httpClient
if skipSSLValidation {
client = httpClientMutatingSkipSSL
} else {
client = httpClientMutating
}
}
return client
}
Expand Down
7 changes: 6 additions & 1 deletion src/backend/app-core/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

// DO NOT DELETE - this is necessary for thr HTTP Client used during unit tests
func init() {
initializeHTTPClients(10, 10)
initializeHTTPClients(10, 10, 10)
}

/* type echoContextMock struct{}
Expand Down Expand Up @@ -62,6 +62,7 @@ func TestLoadPortalConfig(t *testing.T) {

os.Unsetenv("DATABASE_PROVIDER")
os.Setenv("HTTP_CLIENT_TIMEOUT_IN_SECS", "10")
os.Setenv("HTTP_CLIENT_TIMEOUT_MUTATING_IN_SECS", "35")
os.Setenv("SKIP_SSL_VALIDATION", "true")
os.Setenv("CONSOLE_PROXY_TLS_ADDRESS", ":8080")
os.Setenv("CONSOLE_CLIENT", "portal-proxy")
Expand All @@ -84,6 +85,10 @@ func TestLoadPortalConfig(t *testing.T) {
t.Error("Unable to get HTTPClientTimeoutInSecs from config")
}

if result.HTTPClientTimeoutMutatingInSecs != 35 {
t.Error("Unable to get HTTPClientTimeoutMutatingInSecs from config")
}

if result.TLSAddress != ":8080" {
t.Error("Unable to get TLSAddress from config")
}
Expand Down
6 changes: 1 addition & 5 deletions src/backend/app-core/oauth_requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@ func (p *portalProxy) doOauthFlowRequest(cnsiRequest *interfaces.CNSIRequest, re
req.Header.Set("Authorization", "bearer "+tokenRec.AuthToken)

var client http.Client
if cnsi.SkipSSLValidation {
client = httpClientSkipSSL
} else {
client = httpClient
}
client = p.GetHttpClientForRequest(req, cnsi.SkipSSLValidation)
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("Request failed: %v", err)
Expand Down
6 changes: 1 addition & 5 deletions src/backend/app-core/oidc_requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@ func (p *portalProxy) doOidcFlowRequest(cnsiRequest *interfaces.CNSIRequest, req
req.Header.Set("Authorization", "bearer "+tokenRec.AuthToken)

var client http.Client
if cnsi.SkipSSLValidation {
client = httpClientSkipSSL
} else {
client = httpClient
}
client = p.GetHttpClientForRequest(req, cnsi.SkipSSLValidation)
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("Request failed: %v", err)
Expand Down
1 change: 1 addition & 0 deletions src/backend/app-core/repository/interfaces/portal_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

type PortalProxy interface {
GetHttpClient(skipSSLValidation bool) http.Client
GetHttpClientForRequest(req *http.Request, skipSSLValidation bool) http.Client
RegisterEndpoint(c echo.Context, fetchInfo InfoFunc) error

DoRegisterEndpoint(cnsiName string, apiEndpoint string, skipSSLValidation bool, fetchInfo InfoFunc) (CNSIRecord, error)
Expand Down
53 changes: 27 additions & 26 deletions src/backend/app-core/repository/interfaces/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,30 +183,31 @@ type CNSIRequest struct {
}

type PortalConfig struct {
HTTPClientTimeoutInSecs int64 `configName:"HTTP_CLIENT_TIMEOUT_IN_SECS"`
HTTPConnectionTimeoutInSecs int64 `configName:"HTTP_CONNECTION_TIMEOUT_IN_SECS"`
TLSAddress string `configName:"CONSOLE_PROXY_TLS_ADDRESS"`
TLSCert string `configName:"CONSOLE_PROXY_CERT"`
TLSCertKey string `configName:"CONSOLE_PROXY_CERT_KEY"`
TLSCertPath string `configName:"CONSOLE_PROXY_CERT_PATH"`
TLSCertKeyPath string `configName:"CONSOLE_PROXY_CERT_KEY_PATH"`
CFClient string `configName:"CF_CLIENT"`
CFClientSecret string `configName:"CF_CLIENT_SECRET"`
AllowedOrigins []string `configName:"ALLOWED_ORIGINS"`
SessionStoreSecret string `configName:"SESSION_STORE_SECRET"`
EncryptionKeyVolume string `configName:"ENCRYPTION_KEY_VOLUME"`
EncryptionKeyFilename string `configName:"ENCRYPTION_KEY_FILENAME"`
EncryptionKey string `configName:"ENCRYPTION_KEY"`
AutoRegisterCFUrl string `configName:"AUTO_REG_CF_URL"`
CookieDomain string `configName:"COOKIE_DOMAIN"`
CFAdminIdentifier string
CloudFoundryInfo *CFInfo
HTTPS bool
EncryptionKeyInBytes []byte
ConsoleVersion string
IsCloudFoundry bool
LoginHook LoginHookFunc
SessionStore SessionStorer
ConsoleConfig *ConsoleConfig
PluginConfig map[string]string
HTTPClientTimeoutInSecs int64 `configName:"HTTP_CLIENT_TIMEOUT_IN_SECS"`
HTTPClientTimeoutMutatingInSecs int64 `configName:"HTTP_CLIENT_TIMEOUT_MUTATING_IN_SECS"`
HTTPConnectionTimeoutInSecs int64 `configName:"HTTP_CONNECTION_TIMEOUT_IN_SECS"`
TLSAddress string `configName:"CONSOLE_PROXY_TLS_ADDRESS"`
TLSCert string `configName:"CONSOLE_PROXY_CERT"`
TLSCertKey string `configName:"CONSOLE_PROXY_CERT_KEY"`
TLSCertPath string `configName:"CONSOLE_PROXY_CERT_PATH"`
TLSCertKeyPath string `configName:"CONSOLE_PROXY_CERT_KEY_PATH"`
CFClient string `configName:"CF_CLIENT"`
CFClientSecret string `configName:"CF_CLIENT_SECRET"`
AllowedOrigins []string `configName:"ALLOWED_ORIGINS"`
SessionStoreSecret string `configName:"SESSION_STORE_SECRET"`
EncryptionKeyVolume string `configName:"ENCRYPTION_KEY_VOLUME"`
EncryptionKeyFilename string `configName:"ENCRYPTION_KEY_FILENAME"`
EncryptionKey string `configName:"ENCRYPTION_KEY"`
AutoRegisterCFUrl string `configName:"AUTO_REG_CF_URL"`
CookieDomain string `configName:"COOKIE_DOMAIN"`
CFAdminIdentifier string
CloudFoundryInfo *CFInfo
HTTPS bool
EncryptionKeyInBytes []byte
ConsoleVersion string
IsCloudFoundry bool
LoginHook LoginHookFunc
SessionStore SessionStorer
ConsoleConfig *ConsoleConfig
PluginConfig map[string]string
}