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
44 changes: 43 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 49 additions & 2 deletions pkg/cvo/cvo.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import (

"github.com/blang/semver"
"github.com/google/uuid"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
informerscorev1 "k8s.io/client-go/informers/core/v1"
Expand Down Expand Up @@ -44,6 +46,7 @@ import (
"github.com/openshift/cluster-version-operator/pkg/payload/precondition"
preconditioncv "github.com/openshift/cluster-version-operator/pkg/payload/precondition/clusterversion"
"github.com/openshift/cluster-version-operator/pkg/verify"
"github.com/openshift/cluster-version-operator/pkg/verify/verifyconfigmap"
)

const (
Expand Down Expand Up @@ -134,6 +137,9 @@ type Operator struct {
// verifier, if provided, will be used to check an update before it is executed.
// Any error will prevent an update payload from being accessed.
verifier verify.Interface
// signatureStore, if set, will be used to periodically persist signatures to
// the cluster as a config map
signatureStore *verify.StorePersister

configSync ConfigSyncWorker
// statusInterval is how often the configSync worker is allowed to retrigger
Expand Down Expand Up @@ -236,17 +242,21 @@ func (optr *Operator) InitializeFromPayload(restConfig *rest.Config, burstRestCo
}
// XXX: set this to the cincinnati version in preference
if _, err := semver.Parse(update.ImageRef.Name); err != nil {
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)
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is epic. Can we pull things like this and the collector pointer bit out into separate pull requests so they can land while the API refactor and feature extension stuff gets hashed out?

}

optr.releaseCreated = update.ImageRef.CreationTimestamp.Time
optr.releaseVersion = update.ImageRef.Name

// Wraps operator's HTTPClient method to allow releaseVerifier to create http client with up-to-date config.
clientBuilder := &verifyClientBuilder{builder: optr.HTTPClient}
configClient, err := coreclientsetv1.NewForConfig(restConfig)
if err != nil {
return fmt.Errorf("unable to create a configuration client: %v", err)
}

// attempt to load a verifier as defined in the payload
verifier, err := verify.LoadFromPayload(update, clientBuilder)
verifier, signatureStore, err := loadConfigMapVerifierDataFromUpdate(update, clientBuilder, configClient)
if err != nil {
return err
}
Expand All @@ -257,6 +267,7 @@ func (optr *Operator) InitializeFromPayload(restConfig *rest.Config, burstRestCo
verifier = verify.Reject
}
optr.verifier = verifier
optr.signatureStore = signatureStore

// after the verifier has been loaded, initialize the sync worker with a payload retriever
// which will consume the verifier
Expand All @@ -276,6 +287,39 @@ func (optr *Operator) InitializeFromPayload(restConfig *rest.Config, burstRestCo
return nil
}

// loadConfigMapVerifierDataFromUpdate fetches the first config map in the payload with the correct annotation.
// It returns an error if the data is not valid, or no verifier if no config map is found. See the verify
// package for more details on the algorithm for verification. If the annotation is set, a verifier or error
// is always returned.
func loadConfigMapVerifierDataFromUpdate(update *payload.Update, clientBuilder verify.ClientBuilder, configMapClient coreclientsetv1.ConfigMapsGetter) (verify.Interface, *verify.StorePersister, error) {
configMapGVK := corev1.SchemeGroupVersion.WithKind("ConfigMap")
for _, manifest := range update.Manifests {
if manifest.GVK != configMapGVK {
continue
}
if _, ok := manifest.Obj.GetAnnotations()[verify.ReleaseAnnotationConfigMapVerifier]; !ok {
continue
}
src := fmt.Sprintf("the config map %s/%s", manifest.Obj.GetNamespace(), manifest.Obj.GetName())
data, _, err := unstructured.NestedStringMap(manifest.Obj.Object, "data")
if err != nil {
return nil, nil, errors.Wrapf(err, "%s is not valid: %v", src, err)
}
verifier, err := verify.NewFromConfigMapData(src, data, clientBuilder)
if err != nil {
return nil, nil, err
}

// allow the verifier to consult the cluster for signature data, and also configure
// a process that writes signatures back to that store
signatureStore := verifyconfigmap.NewStore(configMapClient, nil)
verifier = verifier.WithStores(signatureStore)
persister := verify.NewSignatureStorePersister(signatureStore, verifier)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having signatureStore baked into verifier and also living as a sibling here feels strange, but I'm still wrapping my head around these new functions.

return verifier, persister, nil
}
return nil, nil, nil
}

// Run runs the cluster version operator until stopCh is completed. Workers is ignored for now.
func (optr *Operator) Run(ctx context.Context, workers int) {
defer utilruntime.HandleCrash()
Expand Down Expand Up @@ -310,6 +354,9 @@ func (optr *Operator) Run(ctx context.Context, workers int) {
utilruntime.HandleError(fmt.Errorf("unable to perform final sync: %v", err))
}
}, time.Second, stopCh)
if optr.signatureStore != nil {
go optr.signatureStore.Run(ctx, optr.minimumUpdateCheckInterval*2)
}

<-stopCh

Expand Down
Loading