Skip to content

Commit 54228c2

Browse files
committed
(feat) incorporate proxy configuration support
Use case: The env variables defined in cluster scoped global proxy configuration object should be injected into deployment spec if not overridden in subscription pod configuration. Add a new proxy package that provides the following support: - Add support to check if Proxy api is available on cluster. - Add support to query the cluster for the 'cluster' Proxy object and return the env variable(s). - Provide logic to determine whether proxy env variable has been overridden in pod configuration.
1 parent 414ffa9 commit 54228c2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2944
-0
lines changed

pkg/lib/proxy/available.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package proxy
2+
3+
import (
4+
"errors"
5+
"strings"
6+
7+
"k8s.io/apimachinery/pkg/runtime/schema"
8+
apidiscovery "k8s.io/client-go/discovery"
9+
)
10+
11+
const (
12+
// This is the error message thrown by ServerSupportsVersion function
13+
// when an API version is not supported by the server.
14+
notSupportedErrorMessage = "server does not support API version"
15+
)
16+
17+
// IsAPIAvailable return true if OpenShift config API is present on the cluster.
18+
// Otherwise, supported is set to false.
19+
func IsAPIAvailable(discovery apidiscovery.DiscoveryInterface) (supported bool, err error) {
20+
if discovery == nil {
21+
err = errors.New("discovery interface can not be <nil>")
22+
return
23+
}
24+
25+
opStatusGV := schema.GroupVersion{
26+
Group: "config.openshift.io",
27+
Version: "v1",
28+
}
29+
if discoveryErr := apidiscovery.ServerSupportsVersion(discovery, opStatusGV); discoveryErr != nil {
30+
if strings.Contains(discoveryErr.Error(), notSupportedErrorMessage) {
31+
return
32+
}
33+
34+
err = discoveryErr
35+
return
36+
}
37+
38+
supported = true
39+
return
40+
}

pkg/lib/proxy/envvar.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package proxy
2+
3+
import (
4+
apiconfigv1 "github.com/openshift/api/config/v1"
5+
corev1 "k8s.io/api/core/v1"
6+
)
7+
8+
const (
9+
// HTTP_PROXY is the URL of the proxy for HTTP requests.
10+
// Empty means unset and will not result in an env var.
11+
envHTTPProxyName = "HTTP_PROXY"
12+
13+
// HTTPS_PROXY is the URL of the proxy for HTTPS requests.
14+
// Empty means unset and will not result in an env var.
15+
envHTTPSProxyName = "HTTPS_PROXY"
16+
17+
// NO_PROXY is the list of domains for which the proxy should not be used.
18+
// Empty means unset and will not result in an env var.
19+
envNoProxyName = "NO_PROXY"
20+
)
21+
22+
var (
23+
allProxyEnvVarNames = []string{
24+
envHTTPProxyName,
25+
envHTTPSProxyName,
26+
envNoProxyName,
27+
}
28+
)
29+
30+
// ToEnvVar accepts a config Proxy object and returns an array of all three
31+
// proxy variables with values.
32+
//
33+
// Please note that the function uses the status of the Proxy object to rea the
34+
// proxy env variables. It's because OpenShift validates the proxy variables in
35+
// spec and writes them back to status.
36+
// As a consumer we should be reading off of proxy.status.
37+
func ToEnvVar(proxy *apiconfigv1.Proxy) []corev1.EnvVar {
38+
return []corev1.EnvVar{
39+
corev1.EnvVar{
40+
Name: envHTTPProxyName,
41+
Value: proxy.Status.HTTPProxy,
42+
},
43+
corev1.EnvVar{
44+
Name: envHTTPSProxyName,
45+
Value: proxy.Status.HTTPSProxy,
46+
},
47+
corev1.EnvVar{
48+
Name: envNoProxyName,
49+
Value: proxy.Status.NoProxy,
50+
},
51+
}
52+
}

pkg/lib/proxy/envvar_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package proxy_test
2+
3+
import (
4+
"testing"
5+
6+
apiconfigv1 "github.com/openshift/api/config/v1"
7+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/proxy"
8+
"github.com/stretchr/testify/assert"
9+
corev1 "k8s.io/api/core/v1"
10+
)
11+
12+
const (
13+
envHTTPProxyName = "HTTP_PROXY"
14+
envHTTPSProxyName = "HTTPS_PROXY"
15+
envNoProxyName = "NO_PROXY"
16+
)
17+
18+
func TestToEnvVar(t *testing.T) {
19+
tests := []struct {
20+
name string
21+
proxy *apiconfigv1.Proxy
22+
envVarWant []corev1.EnvVar
23+
}{
24+
{
25+
name: "WithSet",
26+
proxy: &apiconfigv1.Proxy{
27+
Status: apiconfigv1.ProxyStatus{
28+
HTTPProxy: "http://",
29+
HTTPSProxy: "https://",
30+
NoProxy: "foo,bar",
31+
},
32+
},
33+
envVarWant: []corev1.EnvVar{
34+
corev1.EnvVar{
35+
Name: envHTTPProxyName,
36+
Value: "http://",
37+
},
38+
corev1.EnvVar{
39+
Name: envHTTPSProxyName,
40+
Value: "https://",
41+
},
42+
corev1.EnvVar{
43+
Name: envNoProxyName,
44+
Value: "foo,bar",
45+
},
46+
},
47+
},
48+
49+
{
50+
name: "WithUnset",
51+
proxy: &apiconfigv1.Proxy{
52+
Status: apiconfigv1.ProxyStatus{
53+
HTTPProxy: "http://",
54+
HTTPSProxy: "",
55+
NoProxy: "",
56+
},
57+
},
58+
envVarWant: []corev1.EnvVar{
59+
corev1.EnvVar{
60+
Name: envHTTPProxyName,
61+
Value: "http://",
62+
},
63+
corev1.EnvVar{
64+
Name: envHTTPSProxyName,
65+
Value: "",
66+
},
67+
corev1.EnvVar{
68+
Name: envNoProxyName,
69+
Value: "",
70+
},
71+
},
72+
},
73+
}
74+
75+
for _, tt := range tests {
76+
t.Run(tt.name, func(t *testing.T) {
77+
envVarGot := proxy.ToEnvVar(tt.proxy)
78+
79+
assert.NotNil(t, envVarGot)
80+
assert.Equal(t, tt.envVarWant, envVarGot)
81+
82+
})
83+
}
84+
85+
}

pkg/lib/proxy/overridden.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package proxy
2+
3+
import (
4+
corev1 "k8s.io/api/core/v1"
5+
)
6+
7+
// IsOverridden returns true if the given container overrides proxy env variable(s).
8+
// We apply the following rule:
9+
// If a container already defines any of the proxy env variable then it
10+
// overrides all of these.
11+
func IsOverridden(envVar []corev1.EnvVar) (overrides bool) {
12+
for _, envVarName := range allProxyEnvVarNames {
13+
_, found := find(envVar, envVarName)
14+
if found {
15+
overrides = true
16+
return
17+
}
18+
}
19+
20+
return
21+
}
22+
23+
func find(proxyEnvVar []corev1.EnvVar, name string) (envVar *corev1.EnvVar, found bool) {
24+
for i := range proxyEnvVar {
25+
if name == proxyEnvVar[i].Name {
26+
// Environment variable names are case sensitive.
27+
found = true
28+
envVar = &proxyEnvVar[i]
29+
30+
break
31+
}
32+
}
33+
34+
return
35+
}

pkg/lib/proxy/overridden_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package proxy_test
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/proxy"
8+
9+
"github.com/stretchr/testify/assert"
10+
corev1 "k8s.io/api/core/v1"
11+
)
12+
13+
var (
14+
globalProxyConfig = []corev1.EnvVar{
15+
corev1.EnvVar{
16+
Name: "HTTP_PROXY",
17+
Value: "http://foo.com:8080",
18+
},
19+
corev1.EnvVar{
20+
Name: "HTTPS_PROXY",
21+
Value: "https://foo.com:443",
22+
},
23+
corev1.EnvVar{
24+
Name: "NO_PROXY",
25+
Value: "a.com,b.com",
26+
},
27+
}
28+
)
29+
30+
func TestIsOverridden(t *testing.T) {
31+
tests := []struct {
32+
name string
33+
envVar []corev1.EnvVar
34+
expected bool
35+
}{
36+
{
37+
name: "WithEmptyEnvVar",
38+
envVar: []corev1.EnvVar{},
39+
expected: false,
40+
},
41+
{
42+
name: "WithNilEnvVar",
43+
envVar: nil,
44+
expected: false,
45+
},
46+
{
47+
name: "WithUnrelatedEnvVar",
48+
envVar: []corev1.EnvVar{
49+
corev1.EnvVar{
50+
Name: "foo",
51+
Value: "foo_value",
52+
},
53+
},
54+
expected: false,
55+
},
56+
{
57+
name: "WithHTTP_PROXY",
58+
envVar: []corev1.EnvVar{
59+
corev1.EnvVar{
60+
Name: envHTTPProxyName,
61+
Value: "http://",
62+
},
63+
},
64+
expected: true,
65+
},
66+
{
67+
name: "WithHTTPS_PROXY",
68+
envVar: []corev1.EnvVar{
69+
corev1.EnvVar{
70+
Name: envHTTPSProxyName,
71+
Value: "https://",
72+
},
73+
},
74+
expected: true,
75+
},
76+
{
77+
name: "WithNO_PROXY",
78+
envVar: []corev1.EnvVar{
79+
corev1.EnvVar{
80+
Name: envNoProxyName,
81+
Value: "https://",
82+
},
83+
},
84+
expected: true,
85+
},
86+
{
87+
name: "WithCaseSensitive",
88+
envVar: []corev1.EnvVar{
89+
corev1.EnvVar{
90+
Name: strings.ToLower(envHTTPSProxyName),
91+
Value: "http://",
92+
},
93+
},
94+
expected: false,
95+
},
96+
}
97+
98+
for _, tt := range tests {
99+
t.Run(tt.name, func(t *testing.T) {
100+
actual := proxy.IsOverridden(tt.envVar)
101+
102+
assert.Equal(t, tt.expected, actual)
103+
})
104+
}
105+
}

pkg/lib/proxy/querier.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package proxy
2+
3+
import (
4+
corev1 "k8s.io/api/core/v1"
5+
)
6+
7+
// DefaultQuerier does...
8+
func DefaultQuerier() Querier {
9+
return &defaultQuerier{}
10+
}
11+
12+
// Querier is an interface that wraps the QueryProxyConfig method.
13+
//
14+
// QueryProxyConfig returns the global cluster level proxy env variable(s).
15+
type Querier interface {
16+
QueryProxyConfig() (proxy []corev1.EnvVar, err error)
17+
}
18+
19+
type defaultQuerier struct {
20+
}
21+
22+
// QueryProxyConfig returns no env variable(s), err is set to nil to indicate
23+
// that the cluster has no global proxy configuration.
24+
func (*defaultQuerier) QueryProxyConfig() (proxy []corev1.EnvVar, err error) {
25+
return
26+
}

0 commit comments

Comments
 (0)