Skip to content
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
6 changes: 6 additions & 0 deletions pkg/controller/proxyconfig/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,9 @@ func isSpecHTTPSProxySet(proxyConfig *configv1.ProxySpec) bool {
func isSpecNoProxySet(proxyConfig *configv1.ProxySpec) bool {
return len(proxyConfig.NoProxy) > 0
}

// isSpecReadinessEndpointsSet returns true if spec.readinessEndpoints of
// proxyConfig is set.
func isSpecReadinessEndpointsSet(proxyConfig *configv1.ProxySpec) bool {
return len(proxyConfig.ReadinessEndpoints) > 0
}
86 changes: 86 additions & 0 deletions pkg/controller/proxyconfig/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package proxyconfig
import (
"fmt"
"net"
"net/http"
"net/url"
"strings"
"time"

configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/cluster-network-operator/pkg/util/validation"
Expand All @@ -12,6 +15,11 @@ import (
const (
proxyHTTPScheme = "http"
proxyHTTPSScheme = "https"
// proxyProbeMaxRetries is the number of times to attempt an http GET
// to a readinessEndpoints endpoint.
proxyProbeMaxRetries = 3
// proxyProbeWaitTime is the time to wait before retrying a failed proxy probe.
proxyProbeWaitTime = 1 * time.Second
// noProxyWildcard is the string used to as a wildcard attached to a
// domain suffix in proxy.spec.noProxy to bypass proxying.
noProxyWildcard = "*"
Expand Down Expand Up @@ -52,5 +60,83 @@ func (r *ReconcileProxyConfig) ValidateProxyConfig(proxyConfig *configv1.ProxySp
}
}

if isSpecReadinessEndpointsSet(proxyConfig) {
for _, endpoint := range proxyConfig.ReadinessEndpoints {
scheme, err := validation.URI(endpoint)
if err != nil {
return fmt.Errorf("invalid URI for endpoint '%s': %v", endpoint, err)
}
switch {
case scheme == proxyHTTPScheme:
// TODO: Add case for proxyHTTPSScheme when CA support is merged.
if err := validateHTTPReadinessEndpoint(proxyConfig.HTTPProxy, endpoint); err != nil {
return fmt.Errorf("readinessEndpoint probe failed for endpoint '%s'", endpoint)
}
default:
// TODO: Update error to include proxyHTTPSScheme when CA support is merged.
return fmt.Errorf("readiness endpoints requires a '%s' URI sheme", proxyHTTPScheme)
}
}
}

return nil
}

// validateHTTPReadinessEndpoint validates an http readinessEndpoint endpoint.
func validateHTTPReadinessEndpoint(httpProxy, endpoint string) error {
if err := validateHTTPReadinessEndpointWithRetries(httpProxy, endpoint, proxyProbeMaxRetries); err != nil {
return err
}

return nil
}

// validateHTTPReadinessEndpointWithRetries tries to validate an http
// endpoint in a finite loop and returns the last result if it never succeeds.
func validateHTTPReadinessEndpointWithRetries(httpProxy, endpoint string, retries int) error {
var err error
for i := 0; i < retries; i++ {
err = runHTTPReadinessProbe(httpProxy, endpoint)
if err == nil {
return nil
}
time.Sleep(proxyProbeWaitTime)
}

return err
}

// runHTTPReadinessProbe issues an http GET request to endpoint and returns
// an error if a 2XX or 3XX http status code is not returned. The request
// is proxied if proxy environment variables exist.
func runHTTPReadinessProbe(httpProxy, endpoint string) error {
proxyURL, err := url.Parse(httpProxy)
if err != nil {
return fmt.Errorf("failed to parse httpProxy url '%s': %v", httpProxy, err)
}

transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}

client := &http.Client{
Transport: transport,
}

request, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return fmt.Errorf("failed to create http request for '%s': %v", endpoint, err)
}

resp, err := client.Do(request)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusBadRequest {
return nil
}

return fmt.Errorf("http probe failed with statuscode: %d", resp.StatusCode)
}