Skip to content

Commit e034bc5

Browse files
authored
Merge pull request #270 from justinsb/smd_is_super
mockkubeapiserver: add test using SMD library
2 parents 1f1f6f7 + d33bf38 commit e034bc5

File tree

11 files changed

+12594
-14
lines changed

11 files changed

+12594
-14
lines changed

mockkubeapiserver/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.PHONY: update-schema
2+
update-schema:
3+
curl https://raw.githubusercontent.com/kubernetes/kubernetes/master/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go | \
4+
awk '/typed.YAMLObject/,/`)/' | sed 's/`)//g' | sed 's/var schemaYAML = typed.YAMLObject(`//g' > kubernetes_builtin_schema.yaml

mockkubeapiserver/apiserver.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ func NewMockKubeAPIServer(addr string) (*MockKubeAPIServer, error) {
3232

3333
s.httpServer = &http.Server{Addr: addr, Handler: s}
3434

35-
s.storage = NewMemoryStorage(NewTestClock(), NewTestUIDGenerator())
35+
var err error
36+
37+
s.storage, err = NewMemoryStorage(NewTestClock(), NewTestUIDGenerator())
38+
if err != nil {
39+
return nil, err
40+
}
3641

3742
return s, nil
3843
}

mockkubeapiserver/forked/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
This directory contains code that we copy from various places in the
2+
kubernetes source code that are generally hard to import otherwise.
3+
4+
The intent here is that we demonstrate the need & utility of exporting
5+
these functions in a stable location, and then if/when k/k exports
6+
them we can (reasonably) easily switch, because this is only
7+
test code.
8+
9+
As far as possible, we try to keep the same function names and not
10+
change the code. We want to reunify in the long-term.
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package forked
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"sort"
7+
8+
"k8s.io/apimachinery/pkg/api/meta"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/apimachinery/pkg/runtime"
11+
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
12+
)
13+
14+
// EncodeObjectManagedFields converts and stores the fieldpathManagedFields into the objects ManagedFields
15+
func EncodeObjectManagedFields(obj runtime.Object, fields fieldpath.ManagedFields, times map[string]*metav1.Time) error {
16+
accessor, err := meta.Accessor(obj)
17+
if err != nil {
18+
panic(fmt.Sprintf("couldn't get accessor: %v", err))
19+
}
20+
21+
encodedManagedFields, err := encodeManagedFields(fields, times)
22+
if err != nil {
23+
return fmt.Errorf("failed to convert back managed fields to API: %v", err)
24+
}
25+
accessor.SetManagedFields(encodedManagedFields)
26+
27+
return nil
28+
}
29+
30+
// encodeManagedFields converts ManagedFields from the format used by
31+
// sigs.k8s.io/structured-merge-diff to the wire format (api format)
32+
func encodeManagedFields(fields fieldpath.ManagedFields, times map[string]*metav1.Time) (encodedManagedFields []metav1.ManagedFieldsEntry, err error) {
33+
if len(fields) == 0 {
34+
return nil, nil
35+
}
36+
encodedManagedFields = []metav1.ManagedFieldsEntry{}
37+
for manager := range fields {
38+
versionedSet := fields[manager]
39+
v, err := encodeManagerVersionedSet(manager, versionedSet)
40+
if err != nil {
41+
return nil, fmt.Errorf("error encoding versioned set for %v: %v", manager, err)
42+
}
43+
if t, ok := times[manager]; ok {
44+
v.Time = t
45+
}
46+
encodedManagedFields = append(encodedManagedFields, *v)
47+
}
48+
return sortEncodedManagedFields(encodedManagedFields)
49+
}
50+
51+
func sortEncodedManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry) (sortedManagedFields []metav1.ManagedFieldsEntry, err error) {
52+
sort.Slice(encodedManagedFields, func(i, j int) bool {
53+
p, q := encodedManagedFields[i], encodedManagedFields[j]
54+
55+
if p.Operation != q.Operation {
56+
return p.Operation < q.Operation
57+
}
58+
59+
pSeconds, qSeconds := int64(0), int64(0)
60+
if p.Time != nil {
61+
pSeconds = p.Time.Unix()
62+
}
63+
if q.Time != nil {
64+
qSeconds = q.Time.Unix()
65+
}
66+
if pSeconds != qSeconds {
67+
return pSeconds < qSeconds
68+
}
69+
70+
if p.Manager != q.Manager {
71+
return p.Manager < q.Manager
72+
}
73+
74+
if p.APIVersion != q.APIVersion {
75+
return p.APIVersion < q.APIVersion
76+
}
77+
return p.Subresource < q.Subresource
78+
})
79+
80+
return encodedManagedFields, nil
81+
}
82+
83+
func encodeManagerVersionedSet(manager string, versionedSet fieldpath.VersionedSet) (encodedVersionedSet *metav1.ManagedFieldsEntry, err error) {
84+
encodedVersionedSet = &metav1.ManagedFieldsEntry{}
85+
86+
// Get as many fields as we can from the manager identifier
87+
err = json.Unmarshal([]byte(manager), encodedVersionedSet)
88+
if err != nil {
89+
return nil, fmt.Errorf("error unmarshalling manager identifier %v: %v", manager, err)
90+
}
91+
92+
// Get the APIVersion, Operation, and Fields from the VersionedSet
93+
encodedVersionedSet.APIVersion = string(versionedSet.APIVersion())
94+
if versionedSet.Applied() {
95+
encodedVersionedSet.Operation = metav1.ManagedFieldsOperationApply
96+
}
97+
encodedVersionedSet.FieldsType = "FieldsV1"
98+
fields, err := SetToFields(*versionedSet.Set())
99+
if err != nil {
100+
return nil, fmt.Errorf("error encoding set: %v", err)
101+
}
102+
encodedVersionedSet.FieldsV1 = &fields
103+
104+
return encodedVersionedSet, nil
105+
}
106+
107+
// SetToFields creates a trie of fields from an input set of paths
108+
func SetToFields(s fieldpath.Set) (f metav1.FieldsV1, err error) {
109+
f.Raw, err = s.ToJSON()
110+
return f, err
111+
}

mockkubeapiserver/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ module sigs.k8s.io/kubebuilder-declarative-pattern/mockkubeapiserver
33
go 1.19
44

55
require (
6+
github.com/google/go-cmp v0.5.8
67
k8s.io/apimachinery v0.23.0
78
k8s.io/klog/v2 v2.70.1
9+
sigs.k8s.io/structured-merge-diff/v4 v4.2.3
810
sigs.k8s.io/yaml v1.3.0
911
)
1012

1113
require (
1214
github.com/go-logr/logr v1.2.3 // indirect
1315
github.com/gogo/protobuf v1.3.2 // indirect
14-
github.com/google/go-cmp v0.5.8 // indirect
1516
github.com/google/gofuzz v1.1.0 // indirect
1617
github.com/google/uuid v1.1.2 // indirect
1718
github.com/json-iterator/go v1.1.12 // indirect
@@ -23,5 +24,4 @@ require (
2324
gopkg.in/yaml.v2 v2.4.0 // indirect
2425
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
2526
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
26-
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
2727
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package mockkubeapiserver
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
7+
"sigs.k8s.io/structured-merge-diff/v4/typed"
8+
9+
_ "embed"
10+
)
11+
12+
//go:embed kubernetes_builtin_schema.yaml
13+
var kubernetesBuiltinSchemaYAML string
14+
15+
type Schema struct {
16+
Parser *typed.Parser
17+
}
18+
19+
var cacheKubernetesBuiltinSchema *Schema
20+
21+
var mutexKubernetesBuiltinSchema sync.Mutex
22+
23+
func KubernetesBuiltInSchema() (*Schema, error) {
24+
mutexKubernetesBuiltinSchema.Lock()
25+
defer mutexKubernetesBuiltinSchema.Unlock()
26+
27+
if cacheKubernetesBuiltinSchema != nil {
28+
return cacheKubernetesBuiltinSchema, nil
29+
}
30+
31+
schemaYAML := kubernetesBuiltinSchemaYAML
32+
33+
parser, err := typed.NewParser(typed.YAMLObject(schemaYAML))
34+
if err != nil {
35+
return nil, fmt.Errorf("error parsing schema: %w", err)
36+
}
37+
cacheKubernetesBuiltinSchema = &Schema{Parser: parser}
38+
return cacheKubernetesBuiltinSchema, nil
39+
}

0 commit comments

Comments
 (0)