Skip to content

Commit

Permalink
Merge pull request #359 from justinsb/crd_ready
Browse files Browse the repository at this point in the history
mockkubeapiserver: ensure CRDs become ready
  • Loading branch information
k8s-ci-robot committed Oct 30, 2023
2 parents bdee2d1 + b027d66 commit f6a5c89
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 6 deletions.
2 changes: 1 addition & 1 deletion mockkubeapiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func NewMockKubeAPIServer(addr string, options ...Option) (*MockKubeAPIServer, e
}

// These hooks mock behaviour that would otherwise require full controllers
s.storage.AddStorageHook(&hooks.CRDHook{})
s.storage.AddStorageHook(&hooks.CRDHook{Storage: s.storage})
s.storage.AddStorageHook(&hooks.NamespaceHook{})
s.storage.AddStorageHook(&hooks.DeploymentHook{})

Expand Down
74 changes: 72 additions & 2 deletions mockkubeapiserver/hooks/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,92 @@ limitations under the License.
package hooks

import (
"fmt"

"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
"sigs.k8s.io/kubebuilder-declarative-pattern/mockkubeapiserver/storage"
)

// CRDHook implements functionality for CRD objects (the definitions themselves, not instances of CRDs)
type CRDHook struct {
storage storage.Storage
Storage storage.Storage
}

func (s *CRDHook) OnWatchEvent(ev *storage.WatchEvent) {
switch ev.GroupKind() {
case schema.GroupKind{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"}:
// When a CRD is created, we notify the storage layer so it can store instances of the CRD
if err := s.storage.UpdateCRD(ev); err != nil {
if err := s.Storage.UpdateCRD(ev); err != nil {
klog.Warningf("crd change was invalid: %v", err)
}

if err := s.updateCRDConditions(ev); err != nil {
klog.Fatalf("could not update crd status: %v", err)
}
}
}
func (s *CRDHook) updateCRDConditions(ev *storage.WatchEvent) error {
u := ev.Unstructured()

// So that CRDs become ready, we immediately update the status.
// We could do something better here, like e.g. a 1 second pause before changing the status
statusObj := u.Object["status"]
if statusObj == nil {
statusObj = make(map[string]interface{})
u.Object["status"] = statusObj
}
status, ok := statusObj.(map[string]interface{})
if !ok {
return fmt.Errorf("status was of unexpected type %T", statusObj)
}

generation := u.GetGeneration()
if generation == 0 {
generation = 1
u.SetGeneration(generation)
}

var conditions []interface{}
conditions = append(conditions, map[string]interface{}{
"type": "NamesAccepted",
"status": "True",
"reason": "NoConflicts",
"message": "no conflicts found",
})
conditions = append(conditions, map[string]interface{}{
"type": "Established",
"status": "True",
"reason": "InitialNamesAccepted",
"message": "the initial names have been accepted",
})
status["conditions"] = conditions

// TODO: More of status? Here is an example of the full status
// status:
// acceptedNames:
// kind: VolumeSnapshot
// listKind: VolumeSnapshotList
// plural: volumesnapshots
// singular: volumesnapshot
// conditions:
// - lastTransitionTime: "2023-09-21T01:04:36Z"
// message: no conflicts found
// reason: NoConflicts
// status: "True"
// type: NamesAccepted
// - lastTransitionTime: "2023-09-21T01:04:36Z"
// message: the initial names have been accepted
// reason: InitialNamesAccepted
// status: "True"
// type: Established
// - lastTransitionTime: "2023-09-21T01:04:36Z"
// message: approved in https://github.com/kubernetes-csi/external-snapshotter/pull/419
// reason: ApprovedAnnotation
// status: "True"
// type: KubernetesAPIApprovalPolicyConformant
// storedVersions:
// - v1

return nil
}
7 changes: 4 additions & 3 deletions mockkubeapiserver/storage/memorystorage/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ func (s *MemoryStorage) UpdateCRD(ev *storage.WatchEvent) error {
gr := gvr.GroupResource()

storage := &resourceStorage{
GroupResource: gr,
objects: make(map[types.NamespacedName]*unstructured.Unstructured),
parent: s,
GroupResource: gr,
objects: make(map[types.NamespacedName]*unstructured.Unstructured),
parent: s,
resourceVersionClock: &s.resourceVersionClock,
}

// TODO: share storage across different versions
Expand Down

0 comments on commit f6a5c89

Please sign in to comment.