@@ -12,9 +12,11 @@ import (
12
12
13
13
"github.com/blang/semver"
14
14
"github.com/google/uuid"
15
+ "github.com/pkg/errors"
15
16
corev1 "k8s.io/api/core/v1"
16
17
apierrors "k8s.io/apimachinery/pkg/api/errors"
17
18
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
18
20
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
19
21
"k8s.io/apimachinery/pkg/util/wait"
20
22
informerscorev1 "k8s.io/client-go/informers/core/v1"
@@ -44,6 +46,7 @@ import (
44
46
"github.com/openshift/cluster-version-operator/pkg/payload/precondition"
45
47
preconditioncv "github.com/openshift/cluster-version-operator/pkg/payload/precondition/clusterversion"
46
48
"github.com/openshift/cluster-version-operator/pkg/verify"
49
+ "github.com/openshift/cluster-version-operator/pkg/verify/verifyconfigmap"
47
50
)
48
51
49
52
const (
@@ -134,6 +137,9 @@ type Operator struct {
134
137
// verifier, if provided, will be used to check an update before it is executed.
135
138
// Any error will prevent an update payload from being accessed.
136
139
verifier verify.Interface
140
+ // signatureStore, if set, will be used to periodically persist signatures to
141
+ // the cluster as a config map
142
+ signatureStore * verify.StorePersister
137
143
138
144
configSync ConfigSyncWorker
139
145
// statusInterval is how often the configSync worker is allowed to retrigger
@@ -236,17 +242,21 @@ func (optr *Operator) InitializeFromPayload(restConfig *rest.Config, burstRestCo
236
242
}
237
243
// XXX: set this to the cincinnati version in preference
238
244
if _ , err := semver .Parse (update .ImageRef .Name ); err != nil {
239
- return fmt .Errorf ("The local release contents name %q is not a valid semantic version - no current version will be reported: %v" , update .ImageRef .Name , err )
245
+ return fmt .Errorf ("the local release contents name %q is not a valid semantic version - no current version will be reported: %v" , update .ImageRef .Name , err )
240
246
}
241
247
242
248
optr .releaseCreated = update .ImageRef .CreationTimestamp .Time
243
249
optr .releaseVersion = update .ImageRef .Name
244
250
245
251
// Wraps operator's HTTPClient method to allow releaseVerifier to create http client with up-to-date config.
246
252
clientBuilder := & verifyClientBuilder {builder : optr .HTTPClient }
253
+ configClient , err := coreclientsetv1 .NewForConfig (restConfig )
254
+ if err != nil {
255
+ return fmt .Errorf ("unable to create a configuration client: %v" , err )
256
+ }
247
257
248
258
// attempt to load a verifier as defined in the payload
249
- verifier , err := verify . LoadFromPayload (update , clientBuilder )
259
+ verifier , signatureStore , err := loadConfigMapVerifierDataFromUpdate (update , clientBuilder , configClient )
250
260
if err != nil {
251
261
return err
252
262
}
@@ -257,6 +267,7 @@ func (optr *Operator) InitializeFromPayload(restConfig *rest.Config, burstRestCo
257
267
verifier = verify .Reject
258
268
}
259
269
optr .verifier = verifier
270
+ optr .signatureStore = signatureStore
260
271
261
272
// after the verifier has been loaded, initialize the sync worker with a payload retriever
262
273
// which will consume the verifier
@@ -276,6 +287,39 @@ func (optr *Operator) InitializeFromPayload(restConfig *rest.Config, burstRestCo
276
287
return nil
277
288
}
278
289
290
+ // loadConfigMapVerifierDataFromUpdate fetches the first config map in the payload with the correct annotation.
291
+ // It returns an error if the data is not valid, or no verifier if no config map is found. See the verify
292
+ // package for more details on the algorithm for verification. If the annotation is set, a verifier or error
293
+ // is always returned.
294
+ func loadConfigMapVerifierDataFromUpdate (update * payload.Update , clientBuilder verify.ClientBuilder , configMapClient coreclientsetv1.ConfigMapsGetter ) (verify.Interface , * verify.StorePersister , error ) {
295
+ configMapGVK := corev1 .SchemeGroupVersion .WithKind ("ConfigMap" )
296
+ for _ , manifest := range update .Manifests {
297
+ if manifest .GVK != configMapGVK {
298
+ continue
299
+ }
300
+ if _ , ok := manifest .Obj .GetAnnotations ()[verify .ReleaseAnnotationConfigMapVerifier ]; ! ok {
301
+ continue
302
+ }
303
+ src := fmt .Sprintf ("the config map %s/%s" , manifest .Obj .GetNamespace (), manifest .Obj .GetName ())
304
+ data , _ , err := unstructured .NestedStringMap (manifest .Obj .Object , "data" )
305
+ if err != nil {
306
+ return nil , nil , errors .Wrapf (err , "%s is not valid: %v" , src , err )
307
+ }
308
+ verifier , err := verify .NewFromConfigMapData (src , data , clientBuilder )
309
+ if err != nil {
310
+ return nil , nil , err
311
+ }
312
+
313
+ // allow the verifier to consult the cluster for signature data, and also configure
314
+ // a process that writes signatures back to that store
315
+ signatureStore := verifyconfigmap .NewStore (configMapClient , nil )
316
+ verifier = verifier .WithStores (signatureStore )
317
+ persister := verify .NewSignatureStorePersister (signatureStore , verifier )
318
+ return verifier , persister , nil
319
+ }
320
+ return nil , nil , nil
321
+ }
322
+
279
323
// Run runs the cluster version operator until stopCh is completed. Workers is ignored for now.
280
324
func (optr * Operator ) Run (ctx context.Context , workers int ) {
281
325
defer utilruntime .HandleCrash ()
@@ -310,6 +354,9 @@ func (optr *Operator) Run(ctx context.Context, workers int) {
310
354
utilruntime .HandleError (fmt .Errorf ("unable to perform final sync: %v" , err ))
311
355
}
312
356
}, time .Second , stopCh )
357
+ if optr .signatureStore != nil {
358
+ go optr .signatureStore .Run (ctx , optr .minimumUpdateCheckInterval * 2 )
359
+ }
313
360
314
361
<- stopCh
315
362
0 commit comments