Skip to content

Commit 973ea74

Browse files
authored
Merge pull request #3430 from xmudrii/vw-bindings
Serve both v1alpha1 and v1alpha2 APIBindings in APIExport VW, fix virtual workspace OpenAPI v3 panics
2 parents 017171a + 0269efc commit 973ea74

File tree

8 files changed

+116
-23
lines changed

8 files changed

+116
-23
lines changed

pkg/virtual/apiexport/builder/build.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func BuildVirtualWorkspace(
179179
cancelFn: cancelFn,
180180
}, nil
181181
},
182-
func(ctx context.Context, clusterName logicalcluster.Name, apiExportName string) (apidefinition.APIDefinition, error) {
182+
func(ctx context.Context, apibindingVersion string, clusterName logicalcluster.Name, apiExportName string) (apidefinition.APIDefinition, error) {
183183
restProvider, err := provideAPIExportFilteredRestStorage(ctx, impersonatedDynamicClientGetter, clusterName, apiExportName)
184184
if err != nil {
185185
return nil, err
@@ -188,7 +188,7 @@ func BuildVirtualWorkspace(
188188
return apiserver.CreateServingInfoFor(
189189
mainConfig,
190190
schemas.ApisKcpDevSchemas["apibindings"],
191-
apisv1alpha1.SchemeGroupVersion.Version,
191+
apibindingVersion,
192192
restProvider,
193193
)
194194
},

pkg/virtual/apiexport/controllers/apireconciler/apiexport_apireconciler_controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func NewAPIReconciler(
6262
apiResourceSchemaInformer apisv1alpha1informers.APIResourceSchemaClusterInformer,
6363
apiExportInformer apisv1alpha2informers.APIExportClusterInformer,
6464
createAPIDefinition CreateAPIDefinitionFunc,
65-
createAPIBindingAPIDefinition func(ctx context.Context, clusterName logicalcluster.Name, apiExportName string) (apidefinition.APIDefinition, error),
65+
createAPIBindingAPIDefinition func(ctx context.Context, apibindingVersion string, clusterName logicalcluster.Name, apiExportName string) (apidefinition.APIDefinition, error),
6666
) (*APIReconciler, error) {
6767
c := &APIReconciler{
6868
kcpClusterClient: kcpClusterClient,
@@ -138,7 +138,7 @@ type APIReconciler struct {
138138
queue workqueue.TypedRateLimitingInterface[string]
139139

140140
createAPIDefinition CreateAPIDefinitionFunc
141-
createAPIBindingAPIDefinition func(ctx context.Context, clusterName logicalcluster.Name, apiExportName string) (apidefinition.APIDefinition, error)
141+
createAPIBindingAPIDefinition func(ctx context.Context, apibindingVersion string, clusterName logicalcluster.Name, apiExportName string) (apidefinition.APIDefinition, error)
142142

143143
mutex sync.RWMutex // protects the map, not the values!
144144
apiSets map[dynamiccontext.APIDomainKey]apidefinition.APIDefinitionSet

pkg/virtual/apiexport/controllers/apireconciler/apiexport_apireconciler_reconcile.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -244,19 +244,20 @@ func (c *APIReconciler) reconcile(ctx context.Context, apiExport *apisv1alpha2.A
244244

245245
// always serve apibindings, either through a claim, or with this fallback
246246
if !claimsAPIBindings {
247-
d, err := c.createAPIBindingAPIDefinition(ctx, clusterName, apiExport.Name)
248-
if err != nil {
249-
// TODO(ncdc): would be nice to expose some sort of user-visible error
250-
logger.Error(err, "error creating api definition for apibindings")
251-
}
247+
for _, gvr := range []schema.GroupVersionResource{apisv1alpha1.SchemeGroupVersion.WithResource("apibindings"), apisv1alpha2.SchemeGroupVersion.WithResource("apibindings")} {
248+
d, err := c.createAPIBindingAPIDefinition(ctx, gvr.Version, clusterName, apiExport.Name)
249+
if err != nil {
250+
// TODO(ncdc): would be nice to expose some sort of user-visible error
251+
logger.Error(err, "error creating api definition for apibindings")
252+
}
252253

253-
gvr := apisv1alpha2.SchemeGroupVersion.WithResource("apibindings")
254-
newSet[gvr] = apiResourceSchemaApiDefinition{
255-
APIDefinition: d,
256-
}
257-
newGVRs = append(newGVRs, gvrString(gvr))
258-
if _, ok := oldSet[gvr]; ok {
259-
preservedGVR = append(preservedGVR, gvrString(gvr))
254+
newSet[gvr] = apiResourceSchemaApiDefinition{
255+
APIDefinition: d,
256+
}
257+
newGVRs = append(newGVRs, gvrString(gvr))
258+
if _, ok := oldSet[gvr]; ok {
259+
preservedGVR = append(preservedGVR, gvrString(gvr))
260+
}
260261
}
261262
}
262263

pkg/virtual/framework/dynamic/apiserver/openapi.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,9 @@ func (o *openAPIHandler) handleOpenAPIRequest(apiSet apidefinition.APIDefinition
228228
}
229229

230230
groupPath := "apis/" + gv.Group
231-
webservices[groupPath] = append(webservices[groupPath], discovery.NewAPIGroupHandler(codecs, apiGroup).WebService())
231+
if _, ok := webservices[groupPath]; !ok {
232+
webservices[groupPath] = append(webservices[groupPath], discovery.NewAPIGroupHandler(codecs, apiGroup).WebService())
233+
}
232234
}
233235

234236
// Build OpenAPI v3 spec for /apis/<group> webservices
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
apiVersion: apis.kcp.io/v1alpha1
2+
kind: APIResourceSchema
3+
metadata:
4+
name: today.cowboys.wildwest.dev
5+
spec:
6+
group: wildwest.dev
7+
names:
8+
kind: Cowboy
9+
listKind: CowboyList
10+
plural: cowboys
11+
singular: cowboy
12+
scope: Namespaced
13+
conversion:
14+
strategy: None
15+
versions:
16+
- name: v1alpha1
17+
schema:
18+
description: Cowboy is part of the wild west
19+
properties:
20+
apiVersion:
21+
description: 'APIVersion defines the versioned schema of this representation
22+
of an object. Servers should convert recognized schemas to the latest
23+
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
24+
type: string
25+
kind:
26+
description: 'Kind is a string value representing the REST resource this
27+
object represents. Servers may infer this from the endpoint the client
28+
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
29+
type: string
30+
metadata:
31+
type: object
32+
spec:
33+
description: CowboySpec holds the desired state of the Cowboy.
34+
properties:
35+
intent:
36+
type: string
37+
type: object
38+
status:
39+
description: CowboyStatus communicates the observed state of the Cowboy.
40+
properties:
41+
result:
42+
type: string
43+
type: object
44+
type: object
45+
served: true
46+
storage: true
47+
subresources:
48+
status: {}
49+
- name: v1alpha2
50+
schema:
51+
description: Cowboy is part of the wild west
52+
properties:
53+
apiVersion:
54+
description: 'APIVersion defines the versioned schema of this representation
55+
of an object. Servers should convert recognized schemas to the latest
56+
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
57+
type: string
58+
kind:
59+
description: 'Kind is a string value representing the REST resource this
60+
object represents. Servers may infer this from the endpoint the client
61+
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
62+
type: string
63+
metadata:
64+
type: object
65+
spec:
66+
description: CowboySpec holds the desired state of the Cowboy.
67+
properties:
68+
intent:
69+
type: string
70+
type: object
71+
status:
72+
description: CowboyStatus communicates the observed state of the Cowboy.
73+
properties:
74+
result:
75+
type: string
76+
type: object
77+
type: object
78+
served: true
79+
storage: false
80+
subresources:
81+
status: {}

test/e2e/virtual/apiexport/binding_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ func TestAPIBindingPermissionClaimsVerbs(t *testing.T) {
262262
permissionClaims := defaultPermissionsClaims(identityHash, pcModifiers...)
263263

264264
t.Logf("set up service provider with permission claims")
265-
setUpServiceProvider(ctx, t, dynamicClusterClient, kcpClusterClient, providerPath, cfg, permissionClaims)
265+
setUpServiceProvider(ctx, t, dynamicClusterClient, kcpClusterClient, false, providerPath, cfg, permissionClaims)
266266

267267
t.Logf("set up binding")
268268
bindConsumerToProvider(ctx, t, consumerPath, providerPath, kcpClusterClient, cfg, permissionClaimsToAcceptable(permissionClaims)...)

test/e2e/virtual/apiexport/openapi_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func TestAPIExportOpenAPI(t *testing.T) {
6767

6868
framework.AdmitWorkspaceAccess(ctx, t, kubeClusterClient, serviceProviderPath, []string{"user-1"}, nil, false)
6969

70-
setUpServiceProvider(ctx, t, dynamicClusterClient, kcpClients, serviceProviderPath, cfg, nil)
70+
setUpServiceProvider(ctx, t, dynamicClusterClient, kcpClients, true, serviceProviderPath, cfg, nil)
7171

7272
t.Logf("Waiting for APIExport to have a virtual workspace URL for the bound workspace %q", consumerWorkspace.Name)
7373
apiExportVWCfg := rest.CopyConfig(cfg)

test/e2e/virtual/apiexport/virtualworkspace_test.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func TestAPIExportVirtualWorkspace(t *testing.T) {
103103

104104
framework.AdmitWorkspaceAccess(ctx, t, kubeClusterClient, serviceProviderPath, []string{"user-1"}, nil, false)
105105

106-
setUpServiceProvider(ctx, t, dynamicClusterClient, kcpClients, serviceProviderPath, cfg, nil)
106+
setUpServiceProvider(ctx, t, dynamicClusterClient, kcpClients, false, serviceProviderPath, cfg, nil)
107107
bindConsumerToProvider(ctx, t, consumerPath, serviceProviderPath, kcpClients, cfg)
108108
createCowboyInConsumer(ctx, t, consumerPath, wildwestClusterClient)
109109

@@ -133,6 +133,10 @@ func TestAPIExportVirtualWorkspace(t *testing.T) {
133133
require.NoError(t, err, "error retrieving APIExport discovery")
134134
require.True(t, resourceExists(resources, "apibindings"), "missing apibindings")
135135

136+
resources, err = discoveryVCClusterClient.ServerResourcesForGroupVersion(apisv1alpha1.SchemeGroupVersion.String())
137+
require.NoError(t, err, "error retrieving APIExport discovery")
138+
require.True(t, resourceExists(resources, "apibindings"), "missing apibindings")
139+
136140
user1VWCfg := framework.StaticTokenUserConfig("user-1", apiExportVWCfg)
137141
wwUser1VC, err := wildwestclientset.NewForConfig(user1VWCfg)
138142
require.NoError(t, err)
@@ -1003,18 +1007,23 @@ func defaultPermissionsClaims(identityHash string, modifiers ...func([]apisv1alp
10031007
func setUpServiceProviderWithPermissionClaims(ctx context.Context, t *testing.T, dynamicClusterClient kcpdynamic.ClusterInterface, kcpClients kcpclientset.ClusterInterface, serviceProviderWorkspace logicalcluster.Path, cfg *rest.Config, identityHash string) {
10041008
t.Helper()
10051009

1006-
setUpServiceProvider(ctx, t, dynamicClusterClient, kcpClients, serviceProviderWorkspace, cfg, defaultPermissionsClaims(identityHash))
1010+
setUpServiceProvider(ctx, t, dynamicClusterClient, kcpClients, false, serviceProviderWorkspace, cfg, defaultPermissionsClaims(identityHash))
10071011
}
10081012

1009-
func setUpServiceProvider(ctx context.Context, t *testing.T, dynamicClusterClient kcpdynamic.ClusterInterface, kcpClients kcpclientset.ClusterInterface, serviceProviderWorkspace logicalcluster.Path, cfg *rest.Config, claims []apisv1alpha2.PermissionClaim) {
1013+
func setUpServiceProvider(ctx context.Context, t *testing.T, dynamicClusterClient kcpdynamic.ClusterInterface, kcpClients kcpclientset.ClusterInterface, multipleVersions bool, serviceProviderWorkspace logicalcluster.Path, cfg *rest.Config, claims []apisv1alpha2.PermissionClaim) {
10101014
t.Helper()
10111015
t.Logf("Install today cowboys APIResourceSchema into service provider workspace %q", serviceProviderWorkspace)
10121016

10131017
serviceProviderClient, err := kcpclientset.NewForConfig(cfg)
10141018
require.NoError(t, err)
10151019

1020+
apiResPath := "apiresourceschema_cowboys.yaml"
1021+
if multipleVersions {
1022+
apiResPath = "apiresourceschema_cowboys_versions.yaml"
1023+
}
1024+
10161025
mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(serviceProviderClient.Cluster(serviceProviderWorkspace).Discovery()))
1017-
err = helpers.CreateResourceFromFS(ctx, dynamicClusterClient.Cluster(serviceProviderWorkspace), mapper, nil, "apiresourceschema_cowboys.yaml", testFiles)
1026+
err = helpers.CreateResourceFromFS(ctx, dynamicClusterClient.Cluster(serviceProviderWorkspace), mapper, nil, apiResPath, testFiles)
10181027
require.NoError(t, err)
10191028

10201029
t.Logf("Create an APIExport for it")

0 commit comments

Comments
 (0)