Skip to content

Commit b9a3ffa

Browse files
Merge pull request #267 from patrickdillon/cvo-proxy
Bug 1766907: Add proxy support for CVO.
2 parents 751c6d0 + 34ab90d commit b9a3ffa

File tree

5 files changed

+92
-38
lines changed

5 files changed

+92
-38
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## Building and Publishing CVO
44

55
```sh
6-
./hack/build-image.sh && REPO=<your personal repo (quay.io/ahbinavdahiya | docker.io/abhinavdahiya)> ./hack/push-image.go
6+
./hack/build-image.sh && REPO=<your personal repo (quay.io/ahbinavdahiya | docker.io/abhinavdahiya)> ./hack/push-image.sh
77
```
88

99
1. This builds image locally and then pushes `${VERSION}` and `latest` tags to `${REPO}/origin-cluster-version-operator`.
@@ -17,7 +17,7 @@
1717
2. Run the following command to create release-image at `docker.io/abhinavdahiya/origin-release:latest`:
1818

1919
```sh
20-
oc adm release new -n openshift --server https://api.ci.openshift.org \
20+
oc adm release new -n origin \
2121
--from-image-stream=origin-v4.0 \
2222
--to-image-base=docker.io/abhinavdahiya/origin-cluster-version-operator:latest \
2323
--to-image docker.io/abhinavdahiya/origin-release:latest

pkg/cvo/availableupdates.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
// object. It will set the RetrievedUpdates condition. Updates are only checked if it has been more than
2525
// the minimumUpdateCheckInterval since the last check.
2626
func (optr *Operator) syncAvailableUpdates(config *configv1.ClusterVersion) error {
27-
var tlsConfig *tls.Config
2827
usedDefaultUpstream := false
2928
upstream := string(config.Spec.Upstream)
3029
if len(upstream) == 0 {
@@ -41,16 +40,10 @@ func (optr *Operator) syncAvailableUpdates(config *configv1.ClusterVersion) erro
4140
return nil
4241
}
4342

44-
proxyURL, cmNameRef, err := optr.getHTTPSProxyURL()
43+
proxyURL, tlsConfig, err := optr.getTransportOpts()
4544
if err != nil {
4645
return err
4746
}
48-
if cmNameRef != "" {
49-
tlsConfig, err = optr.getTLSConfig(cmNameRef)
50-
if err != nil {
51-
return err
52-
}
53-
}
5447

5548
updates, condition := calculateAvailableUpdatesStatus(string(config.Spec.ClusterID), proxyURL, tlsConfig, upstream, arch, channel, optr.releaseVersion)
5649

pkg/cvo/cvo.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package cvo
22

33
import (
44
"context"
5+
"crypto/tls"
56
"fmt"
7+
"net/http"
8+
"net/url"
69
"strconv"
710
"sync"
811
"time"
@@ -22,6 +25,7 @@ import (
2225
"k8s.io/client-go/rest"
2326
"k8s.io/client-go/tools/cache"
2427
"k8s.io/client-go/tools/record"
28+
"k8s.io/client-go/transport"
2529
"k8s.io/client-go/util/workqueue"
2630
"k8s.io/klog"
2731

@@ -212,6 +216,16 @@ func New(
212216
return optr
213217
}
214218

219+
// verifyClientBuilder is a wrapper around the operator's HTTPClient method.
220+
// It is used by the releaseVerifier to get an up-to-date http client.
221+
type verifyClientBuilder struct {
222+
builder func() (*http.Client, error)
223+
}
224+
225+
func (vcb *verifyClientBuilder) HTTPClient() (*http.Client, error) {
226+
return vcb.builder()
227+
}
228+
215229
// InitializeFromPayload retrieves the payload contents and verifies the initial state, then configures the
216230
// controller that loads and applies content to the cluster. It returns an error if the payload appears to
217231
// be in error rather than continuing.
@@ -228,8 +242,11 @@ func (optr *Operator) InitializeFromPayload(restConfig *rest.Config, burstRestCo
228242
optr.releaseCreated = update.ImageRef.CreationTimestamp.Time
229243
optr.releaseVersion = update.ImageRef.Name
230244

245+
// Wraps operator's HTTPClient method to allow releaseVerifier to create http client with up-to-date config.
246+
clientBuilder := &verifyClientBuilder{builder: optr.HTTPClient}
247+
231248
// attempt to load a verifier as defined in the payload
232-
verifier, err := verify.LoadFromPayload(update)
249+
verifier, err := verify.LoadFromPayload(update, clientBuilder)
233250
if err != nil {
234251
return err
235252
}
@@ -660,3 +677,42 @@ func (optr *Operator) defaultPreconditionChecks() precondition.List {
660677
preconditioncv.NewUpgradeable(optr.cvLister),
661678
}
662679
}
680+
681+
// HTTPClient provides a method for generating an HTTP client
682+
// with the proxy and trust settings, if set in the cluster.
683+
func (optr *Operator) HTTPClient() (*http.Client, error) {
684+
proxyURL, tlsConfig, err := optr.getTransportOpts()
685+
if err != nil {
686+
return nil, err
687+
}
688+
transportOption := &http.Transport{
689+
Proxy: http.ProxyURL(proxyURL),
690+
TLSClientConfig: tlsConfig,
691+
}
692+
transportConfig := &transport.Config{Transport: transportOption}
693+
transport, err := transport.New(transportConfig)
694+
if err != nil {
695+
return nil, err
696+
}
697+
return &http.Client{
698+
Transport: transport,
699+
}, nil
700+
}
701+
702+
// getTransportOpts retrieves the URL of the cluster proxy and the CA
703+
// trust, if they exist.
704+
func (optr *Operator) getTransportOpts() (*url.URL, *tls.Config, error) {
705+
proxyURL, cmNameRef, err := optr.getHTTPSProxyURL()
706+
if err != nil {
707+
return nil, nil, err
708+
}
709+
710+
var tlsConfig *tls.Config
711+
if cmNameRef != "" {
712+
tlsConfig, err = optr.getTLSConfig(cmNameRef)
713+
if err != nil {
714+
return nil, nil, err
715+
}
716+
}
717+
return proxyURL, tlsConfig, nil
718+
}

pkg/verify/verify.go

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ import (
1818
"strings"
1919
"time"
2020

21-
"k8s.io/klog"
2221
"github.com/pkg/errors"
2322
"golang.org/x/crypto/openpgp"
24-
2523
corev1 "k8s.io/api/core/v1"
2624
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
27-
"k8s.io/client-go/transport"
25+
"k8s.io/klog"
2826

2927
"github.com/openshift/cluster-version-operator/pkg/payload"
3028
)
@@ -74,7 +72,7 @@ var Reject Interface = rejectVerifier{}
7472
// if each provided public key has signed the release image digest. The signature may be in any
7573
// store and the lookup order is internally defined.
7674
//
77-
func LoadFromPayload(update *payload.Update) (Interface, error) {
75+
func LoadFromPayload(update *payload.Update, clientBuilder ClientBuilder) (Interface, error) {
7876
configMapGVK := corev1.SchemeGroupVersion.WithKind("ConfigMap")
7977
for _, manifest := range update.Manifests {
8078
if manifest.GVK != configMapGVK {
@@ -117,16 +115,10 @@ func LoadFromPayload(update *payload.Update) (Interface, error) {
117115
return nil, fmt.Errorf("%s did not provide any GPG public keys to verify signatures from and cannot be used", src)
118116
}
119117

120-
transport, err := transport.New(&transport.Config{})
121-
if err != nil {
122-
return nil, errors.Wrapf(err, "unable to create HTTP client for verifying signatures: %v", err)
123-
}
124118
return &releaseVerifier{
125-
verifiers: verifiers,
126-
stores: stores,
127-
client: &http.Client{
128-
Transport: transport,
129-
},
119+
verifiers: verifiers,
120+
stores: stores,
121+
clientBuilder: clientBuilder,
130122
}, nil
131123
}
132124
return nil, nil
@@ -148,9 +140,23 @@ var validReleaseDigest = regexp.MustCompile(`^[a-zA-Z0-9:]+$`)
148140
// digest - all verifiers must have at least one valid signature attesting the release
149141
// digest. If any failure occurs the caller should assume the content is unverified.
150142
type releaseVerifier struct {
151-
verifiers map[string]openpgp.EntityList
152-
stores []*url.URL
153-
client *http.Client
143+
verifiers map[string]openpgp.EntityList
144+
stores []*url.URL
145+
clientBuilder ClientBuilder
146+
}
147+
148+
// ClientBuilder provides a method for generating an HTTP Client configured
149+
// with cluster proxy settings, if they exist.
150+
type ClientBuilder interface {
151+
HTTPClient() (*http.Client, error)
152+
}
153+
154+
// simpleClientBuilder implements the ClientBuilder interface and should be used for testing.
155+
type simpleClientBuilder struct{}
156+
157+
// HTTPClient from simpleClientBuilder creates an httpClient with no configuration.
158+
func (s *simpleClientBuilder) HTTPClient() (*http.Client, error) {
159+
return &http.Client{}, nil
154160
}
155161

156162
// String summarizes the verifier for human consumption
@@ -243,6 +249,11 @@ func (v *releaseVerifier) Verify(ctx context.Context, releaseDigest string) erro
243249
return len(remaining) > 0, nil
244250
}
245251

252+
client, err := v.clientBuilder.HTTPClient()
253+
if err != nil {
254+
return err
255+
}
256+
246257
for _, store := range v.stores {
247258
if len(remaining) == 0 {
248259
break
@@ -256,7 +267,7 @@ func (v *releaseVerifier) Verify(ctx context.Context, releaseDigest string) erro
256267
case "http", "https":
257268
copied := *store
258269
copied.Path = path.Join(store.Path, name)
259-
if err := checkHTTPSignatures(ctx, v.client, copied, maxSignatureSearch, verifier); err != nil {
270+
if err := checkHTTPSignatures(ctx, client, copied, maxSignatureSearch, verifier); err != nil {
260271
return err
261272
}
262273
default:
@@ -325,7 +336,6 @@ func checkHTTPSignatures(ctx context.Context, client *http.Client, u url.URL, ma
325336
return fmt.Errorf("could not build request to check signature: %v", err)
326337
}
327338
req = req.WithContext(ctx)
328-
329339
// load the body, being careful not to allow unbounded reads
330340
resp, err := client.Do(req)
331341
if err != nil {

pkg/verify/verify_test.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import (
1010
"path/filepath"
1111
"testing"
1212

13-
"k8s.io/client-go/transport"
14-
1513
"github.com/openshift/cluster-version-operator/lib"
1614
"github.com/openshift/cluster-version-operator/pkg/payload"
1715
"golang.org/x/crypto/openpgp"
@@ -161,16 +159,13 @@ func Test_releaseVerifier_Verify(t *testing.T) {
161159
}
162160
for _, tt := range tests {
163161
t.Run(tt.name, func(t *testing.T) {
164-
transport, err := transport.New(&transport.Config{})
165162
if err != nil {
166163
t.Fatal(err)
167164
}
168165
v := &releaseVerifier{
169-
verifiers: tt.verifiers,
170-
stores: tt.stores,
171-
client: &http.Client{
172-
Transport: transport,
173-
},
166+
verifiers: tt.verifiers,
167+
stores: tt.stores,
168+
clientBuilder: &simpleClientBuilder{},
174169
}
175170
if err := v.Verify(context.Background(), tt.releaseDigest); (err != nil) != tt.wantErr {
176171
t.Errorf("releaseVerifier.Verify() error = %v, wantErr %v", err, tt.wantErr)
@@ -344,7 +339,7 @@ func Test_loadReleaseVerifierFromPayload(t *testing.T) {
344339
}
345340
for _, tt := range tests {
346341
t.Run(tt.name, func(t *testing.T) {
347-
got, err := LoadFromPayload(tt.update)
342+
got, err := LoadFromPayload(tt.update, &simpleClientBuilder{})
348343
if (err != nil) != tt.wantErr {
349344
t.Fatalf("loadReleaseVerifierFromPayload() error = %v, wantErr %v", err, tt.wantErr)
350345
return

0 commit comments

Comments
 (0)