Skip to content

Commit a6829d3

Browse files
authored
Merge pull request #3726 from olamilekan000/add-path-to-cachedresourcerefrence-api
add path to cachedresource api
2 parents e0ead60 + 340b8f4 commit a6829d3

File tree

15 files changed

+315
-16
lines changed

15 files changed

+315
-16
lines changed

config/crds/cache.kcp.io_cachedresourceendpointslices.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ spec:
5959
description: name is the name of the CachedResource the reference
6060
points to.
6161
type: string
62+
path:
63+
description: |-
64+
path is a logical cluster path where the CachedResource is defined. If empty,
65+
the CachedResource is assumed to be co-located with the referencing resource.
66+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(:[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
67+
type: string
6268
required:
6369
- name
6470
type: object

config/root-phase0/apiexport-cache.kcp.io.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ spec:
77
resources:
88
- group: cache.kcp.io
99
name: cachedresourceendpointslices
10-
schema: v251008-c4825908e.cachedresourceendpointslices.cache.kcp.io
10+
schema: v251120-d8e87a979.cachedresourceendpointslices.cache.kcp.io
1111
storage:
1212
crd: {}
1313
- group: cache.kcp.io

config/root-phase0/apiresourceschema-cachedresourceendpointslices.cache.kcp.io.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apiVersion: apis.kcp.io/v1alpha1
22
kind: APIResourceSchema
33
metadata:
44
creationTimestamp: null
5-
name: v251008-c4825908e.cachedresourceendpointslices.cache.kcp.io
5+
name: v251120-d8e87a979.cachedresourceendpointslices.cache.kcp.io
66
spec:
77
group: cache.kcp.io
88
names:
@@ -56,6 +56,12 @@ spec:
5656
description: name is the name of the CachedResource the reference
5757
points to.
5858
type: string
59+
path:
60+
description: |-
61+
path is a logical cluster path where the CachedResource is defined. If empty,
62+
the CachedResource is assumed to be co-located with the referencing resource.
63+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(:[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
64+
type: string
5965
required:
6066
- name
6167
type: object

pkg/admission/pathannotation/pathannotation_admission.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929

3030
"github.com/kcp-dev/logicalcluster/v3"
3131
apisv1alpha2 "github.com/kcp-dev/sdk/apis/apis/v1alpha2"
32+
cachev1alpha1 "github.com/kcp-dev/sdk/apis/cache/v1alpha1"
3233
"github.com/kcp-dev/sdk/apis/core"
3334
corev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1"
3435
tenancyv1alpha1 "github.com/kcp-dev/sdk/apis/tenancy/v1alpha1"
@@ -69,6 +70,7 @@ type pathAnnotationPlugin struct {
6970

7071
var pathAnnotationResources = sets.New[string](
7172
apisv1alpha2.Resource("apiexports").String(),
73+
cachev1alpha1.Resource("cachedresources").String(),
7274
tenancyv1alpha1.Resource("workspacetypes").String(),
7375
)
7476

pkg/admission/pathannotation/pathannotation_admission_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/kcp-dev/logicalcluster/v3"
3232
apisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1"
3333
apisv1alpha2 "github.com/kcp-dev/sdk/apis/apis/v1alpha2"
34+
cachev1alpha1 "github.com/kcp-dev/sdk/apis/cache/v1alpha1"
3435
"github.com/kcp-dev/sdk/apis/core"
3536
corev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1"
3637
tenancyv1alpha1 "github.com/kcp-dev/sdk/apis/tenancy/v1alpha1"
@@ -133,6 +134,15 @@ func TestPathAnnotationAdmit(t *testing.T) {
133134
getLogicalCluster: getCluster("foo"),
134135
validateAdmissionObject: objectHasPathAnnotation("root:foo"),
135136
},
137+
{
138+
name: "happy path: a CachedResource is annotated with a path",
139+
admissionVerb: admission.Create,
140+
admissionResource: cachev1alpha1.SchemeGroupVersion.WithResource("cachedresources"),
141+
admissionObject: &cachev1alpha1.CachedResource{},
142+
admissionContext: admissionContextFor("foo"),
143+
getLogicalCluster: getCluster("foo"),
144+
validateAdmissionObject: objectHasPathAnnotation("root:foo"),
145+
},
136146

137147
{
138148
name: "happy path: a WorkspaceType is annotated with a path",
@@ -239,6 +249,15 @@ func TestPathAnnotationValidate(t *testing.T) {
239249
admissionContext: admissionContextFor("foo"),
240250
getLogicalCluster: getCluster("foo"),
241251
},
252+
{
253+
name: "a CachedResource with incorrect path annotation is NOT admitted",
254+
admissionVerb: admission.Create,
255+
admissionResource: cachev1alpha1.SchemeGroupVersion.WithResource("cachedresources"),
256+
admissionObject: &cachev1alpha1.CachedResource{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{core.LogicalClusterPathAnnotationKey: "root:bar"}}},
257+
admissionContext: admissionContextFor("foo"),
258+
getLogicalCluster: getCluster("foo"),
259+
expectError: true,
260+
},
242261
}
243262

244263
for _, scenario := range scenarios {

pkg/openapi/zz_generated.openapi.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/reconciler/cache/cachedresourceendpointslice/cachedresourceendpointslice_indexers.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ func IndexCachedResourceEndpointSliceByCachedResourceFunc(obj interface{}) ([]st
6060
}
6161

6262
pathLocal := logicalcluster.From(slice).Path()
63-
// TODO(gman0): add an optional external path index key once we add "CachedResourceEndpointSlice.spec.cachedResource.path".
64-
return []string{pathLocal.Join(slice.Spec.CachedResource.Name).String()}, nil
63+
keys := []string{pathLocal.Join(slice.Spec.CachedResource.Name).String()}
64+
65+
if refPath := logicalcluster.NewPath(slice.Spec.CachedResource.Path); !refPath.Empty() {
66+
key := refPath.Join(slice.Spec.CachedResource.Name).String()
67+
if key != keys[0] {
68+
keys = append(keys, key)
69+
}
70+
}
71+
72+
return keys, nil
6573
}

pkg/reconciler/cache/cachedresourceendpointslice/cachedresourceendpointslice_reconcile.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ import (
3030
)
3131

3232
func (c *controller) reconcile(ctx context.Context, endpoints *cachev1alpha1.CachedResourceEndpointSlice) error {
33-
_, err := c.getCachedResource(logicalcluster.From(endpoints).Path(), endpoints.Spec.CachedResource.Name)
33+
cachedResourcePath := logicalcluster.NewPath(endpoints.Spec.CachedResource.Path)
34+
if cachedResourcePath.Empty() {
35+
cachedResourcePath = logicalcluster.From(endpoints).Path()
36+
}
37+
38+
_, err := c.getCachedResource(cachedResourcePath, endpoints.Spec.CachedResource.Name)
3439
if err != nil {
3540
if apierrors.IsNotFound(err) {
3641
// Don't keep the endpoints if the CachedResource has been deleted.
@@ -41,7 +46,7 @@ func (c *controller) reconcile(ctx context.Context, endpoints *cachev1alpha1.Cac
4146
cachev1alpha1.CachedResourceNotFoundReason,
4247
conditionsv1alpha1.ConditionSeverityError,
4348
"Error getting CachedResource %s|%s",
44-
logicalcluster.From(endpoints),
49+
cachedResourcePath,
4550
endpoints.Spec.CachedResource.Name,
4651
)
4752
// No need to try again.
@@ -53,7 +58,7 @@ func (c *controller) reconcile(ctx context.Context, endpoints *cachev1alpha1.Cac
5358
cachev1alpha1.InternalErrorReason,
5459
conditionsv1alpha1.ConditionSeverityError,
5560
"Error getting CachedResource %s|%s",
56-
logicalcluster.From(endpoints),
61+
cachedResourcePath,
5762
endpoints.Spec.CachedResource.Name,
5863
)
5964
return err

pkg/reconciler/cache/cachedresourceendpointslice/cachedresourceendpointslice_reconcile_test.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
)
3636

3737
func TestReconcile(t *testing.T) {
38+
const workspacePath = "root:org:ws"
3839
tests := map[string]struct {
3940
keyMissing bool
4041
cachedResourceMissing bool
@@ -49,29 +50,45 @@ func TestReconcile(t *testing.T) {
4950
wantPartitionValid bool
5051
wantCachedResourceNotValid bool
5152
wantPartitionNotValid bool
53+
54+
sliceMutator func(slice *cachev1alpha1.CachedResourceEndpointSlice)
55+
expectedCachedResourcePath string
5256
}{
5357
"CachedResourceValid set to false when CachedResource is missing": {
5458
cachedResourceMissing: true,
5559
errorReason: cachev1alpha1.CachedResourceNotFoundReason,
5660
wantCachedResourceNotValid: true,
61+
expectedCachedResourcePath: workspacePath,
5762
},
5863
"CachedResourceValid set to false if an internal error happens when fetching the CachedResource": {
5964
cachedResourceInternalErr: true,
6065
wantError: true,
6166
errorReason: cachev1alpha1.InternalErrorReason,
6267
wantCachedResourceNotValid: true,
68+
expectedCachedResourcePath: workspacePath,
6369
},
6470
"PartitionValid set to false when the Partition is missing": {
65-
partitionMissing: true,
66-
errorReason: cachev1alpha1.PartitionInvalidReferenceReason,
67-
wantPartitionNotValid: true,
71+
partitionMissing: true,
72+
errorReason: cachev1alpha1.PartitionInvalidReferenceReason,
73+
wantPartitionNotValid: true,
74+
expectedCachedResourcePath: workspacePath,
75+
},
76+
"CachedResource lookup uses referenced path when provided": {
77+
sliceMutator: func(slice *cachev1alpha1.CachedResourceEndpointSlice) {
78+
slice.Spec.CachedResource.Path = "root:org:external"
79+
},
80+
expectedCachedResourcePath: "root:org:external",
81+
wantCachedResourceValid: true,
82+
wantPartitionValid: true,
6883
},
6984
}
7085

7186
for name, tc := range tests {
7287
t.Run(name, func(t *testing.T) {
88+
var gotCachedResourcePath string
7389
c := &controller{
7490
getCachedResource: func(path logicalcluster.Path, name string) (*cachev1alpha1.CachedResource, error) {
91+
gotCachedResourcePath = path.String()
7592
if tc.cachedResourceMissing {
7693
return nil, apierrors.NewNotFound(cachev1alpha1.Resource("CachedResource"), name)
7794
} else if tc.cachedResourceInternalErr {
@@ -113,7 +130,7 @@ func TestReconcile(t *testing.T) {
113130
cachedResourceEndpointSlice := &cachev1alpha1.CachedResourceEndpointSlice{
114131
ObjectMeta: metav1.ObjectMeta{
115132
Annotations: map[string]string{
116-
logicalcluster.AnnotationKey: "root:org:ws",
133+
logicalcluster.AnnotationKey: workspacePath,
117134
},
118135
Name: "my-slice",
119136
},
@@ -124,6 +141,9 @@ func TestReconcile(t *testing.T) {
124141
Partition: "my-partition",
125142
},
126143
}
144+
if tc.sliceMutator != nil {
145+
tc.sliceMutator(cachedResourceEndpointSlice)
146+
}
127147
err := c.reconcile(context.Background(), cachedResourceEndpointSlice)
128148
if tc.wantError {
129149
require.Error(t, err, "expected an error")
@@ -164,6 +184,10 @@ func TestReconcile(t *testing.T) {
164184
conditions.TrueCondition(cachev1alpha1.PartitionValid),
165185
)
166186
}
187+
188+
if tc.expectedCachedResourcePath != "" {
189+
require.Equal(t, tc.expectedCachedResourcePath, gotCachedResourcePath, "unexpected cached resource lookup path")
190+
}
167191
})
168192
}
169193
}

pkg/reconciler/cache/cachedresourceendpointsliceurls/cachedresourceendpointsliceurls_controller.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ func NewController(
110110
getMyShard: func() (*corev1alpha1.Shard, error) {
111111
return globalShardClusterInformer.Cluster(core.RootCluster).Lister().Get(shardName)
112112
},
113-
getCachedResource: func(cluster logicalcluster.Name, name string) (*cachev1alpha1.CachedResource, error) {
114-
return indexers.ByPathAndName[*cachev1alpha1.CachedResource](cachev1alpha1.Resource("cachedresources"), globalCachedResourcelusterInformer.Informer().GetIndexer(), cluster.Path(), name)
113+
getCachedResource: func(path logicalcluster.Path, name string) (*cachev1alpha1.CachedResource, error) {
114+
return indexers.ByPathAndName[*cachev1alpha1.CachedResource](cachev1alpha1.Resource("cachedresources"), globalCachedResourcelusterInformer.Informer().GetIndexer(), path, name)
115115
},
116116
getCachedResourceEndpointSlice: func(cluster logicalcluster.Name, name string) (*cachev1alpha1.CachedResourceEndpointSlice, error) {
117117
obj, err := indexers.ByPathAndNameWithFallback[*cachev1alpha1.CachedResourceEndpointSlice](cachev1alpha1.Resource("cachedresourceendpointslices"), localCachedResourceEndpointSliceClusterInformer.Informer().GetIndexer(), globalCachedResourceEndpointSliceClusterInformer.Informer().GetIndexer(), cluster.Path(), name)
@@ -242,7 +242,7 @@ type controller struct {
242242
listAPIExportsByCachedResourceIdentityAndGR func(identityHash string, gr schema.GroupResource) ([]*apisv1alpha2.APIExport, error)
243243
listAPIBindingsByAPIExports func(exports []*apisv1alpha2.APIExport) ([]*apisv1alpha2.APIBinding, error)
244244
getMyShard func() (*corev1alpha1.Shard, error)
245-
getCachedResource func(cluster logicalcluster.Name, name string) (*cachev1alpha1.CachedResource, error)
245+
getCachedResource func(path logicalcluster.Path, name string) (*cachev1alpha1.CachedResource, error)
246246
getCachedResourceEndpointSlice func(cluster logicalcluster.Name, name string) (*cachev1alpha1.CachedResourceEndpointSlice, error)
247247
patchAPIExportEndpointSlice func(ctx context.Context, cluster logicalcluster.Path, patch *cachev1alpha1apply.CachedResourceEndpointSliceApplyConfiguration) error
248248
patchCachedResourceEndpointSlice func(ctx context.Context, cluster logicalcluster.Path, patch *cachev1alpha1apply.CachedResourceEndpointSliceApplyConfiguration) error

0 commit comments

Comments
 (0)