Skip to content

Commit 471adde

Browse files
committed
Disable deprecated APIExport VirtualWorkspace url population
Signed-off-by: Mangirdas Judeikis <Mangirdas@Judeikis.LT> On-behalf-of: @SAP mangirdas.judeikis@sap.com
1 parent 37664a9 commit 471adde

File tree

16 files changed

+359
-276
lines changed

16 files changed

+359
-276
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ test-e2e-sharded-minimal: build-all
349349

350350
# This is just easy target to run 2 shard test server locally until manually killed.
351351
# You can targer test to it by running:
352-
# go test ./test/e2e/apibinding/... --kcp-kubeconfig=${WORK_DIR:-.}/.kcp/admin.kubeconfig --shard-kubeconfigs=root=${WORK_DIR:-.}/.kcp-0/admin.kubeconfig -run=^TestAPIBindingEndpointSlicesSharded$
352+
# go test ./test/e2e/apibinding/... --kcp-kubeconfig=$(pwd)/.kcp/admin.kubeconfig --shard-kubeconfigs=root=$(pwd)/.kcp-0/admin.kubeconfig -run=^TestAPIBinding$
353353
test-run-sharded-server: WORK_DIR ?= $(PWD)
354354
test-run-sharded-server: LOG_DIR ?= $(WORK_DIR)/.kcp
355355
test-run-sharded-server:

pkg/features/kcp_features.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ const (
4242
// alpha: v0.1
4343
// Enables workspace mounts via frontProxy.
4444
WorkspaceMounts featuregate.Feature = "WorkspaceMounts"
45+
46+
// owner: @mjudeikis
47+
// alpha: v0.1
48+
// Enables VirtualWorkspace urls on APIExport. This enables to use Deprecated APIExport VirtualWorkspace urls.
49+
// This is a temporary feature to ease the migration to the new VirtualWorkspace urls.
50+
EnableDeprecatedAPIExportVirtualWorkspacesUrls featuregate.Feature = "EnableDeprecatedAPIExportVirtualWorkspacesUrls"
4551
)
4652

4753
// DefaultFeatureGate exposes the upstream feature gate, but with our gate setting applied.
@@ -112,7 +118,9 @@ var defaultVersionedGenericControlPlaneFeatureGates = map[featuregate.Feature]fe
112118
WorkspaceMounts: {
113119
{Version: version.MustParse("1.28"), Default: false, PreRelease: featuregate.Alpha},
114120
},
115-
121+
EnableDeprecatedAPIExportVirtualWorkspacesUrls: {
122+
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha},
123+
},
116124
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
117125
// unintentionally on either side:
118126

pkg/reconciler/apis/apiexport/apiexport_controller.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@ import (
4141
"github.com/kcp-dev/kcp/pkg/logging"
4242
"github.com/kcp-dev/kcp/pkg/reconciler/committer"
4343
"github.com/kcp-dev/kcp/pkg/reconciler/events"
44+
apisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1"
4445
apisv1alpha2 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha2"
4546
corev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1"
4647
kcpclientset "github.com/kcp-dev/kcp/sdk/client/clientset/versioned/cluster"
4748
apisv1alpha2client "github.com/kcp-dev/kcp/sdk/client/clientset/versioned/typed/apis/v1alpha2"
49+
apisv1alpha1informers "github.com/kcp-dev/kcp/sdk/client/informers/externalversions/apis/v1alpha1"
4850
apisv1alpha2informers "github.com/kcp-dev/kcp/sdk/client/informers/externalversions/apis/v1alpha2"
4951
corev1alpha1informers "github.com/kcp-dev/kcp/sdk/client/informers/externalversions/core/v1alpha1"
5052
)
@@ -59,6 +61,7 @@ const (
5961
func NewController(
6062
kcpClusterClient kcpclientset.ClusterInterface,
6163
apiExportInformer apisv1alpha2informers.APIExportClusterInformer,
64+
apiExportEndpointSliceInformer apisv1alpha1informers.APIExportEndpointSliceClusterInformer,
6265
globalShardInformer corev1alpha1informers.ShardClusterInformer,
6366
kubeClusterClient kcpkubernetesclientset.ClusterInterface,
6467
namespaceInformer kcpcorev1informers.NamespaceClusterInformer,
@@ -89,6 +92,13 @@ func NewController(
8992
getAPIExport: func(clusterName logicalcluster.Name, name string) (*apisv1alpha2.APIExport, error) {
9093
return apiExportInformer.Lister().Cluster(clusterName).Get(name)
9194
},
95+
getAPIExportEndpointSlice: func(clusterName logicalcluster.Name, name string) (*apisv1alpha1.APIExportEndpointSlice, error) {
96+
return apiExportEndpointSliceInformer.Lister().Cluster(clusterName).Get(name)
97+
},
98+
createAPIExportEndpointSlice: func(ctx context.Context, clusterName logicalcluster.Path, apiExportEndpointSlice *apisv1alpha1.APIExportEndpointSlice) error {
99+
_, err := kcpClusterClient.Cluster(clusterName).ApisV1alpha1().APIExportEndpointSlices().Create(ctx, apiExportEndpointSlice, metav1.CreateOptions{})
100+
return err
101+
},
92102

93103
getNamespace: func(clusterName logicalcluster.Name, name string) (*corev1.Namespace, error) {
94104
return namespaceInformer.Lister().Cluster(clusterName).Get(name)
@@ -183,6 +193,9 @@ type controller struct {
183193
listAPIExportsForSecret func(secret *corev1.Secret) ([]*apisv1alpha2.APIExport, error)
184194
getAPIExport func(clusterName logicalcluster.Name, name string) (*apisv1alpha2.APIExport, error)
185195

196+
getAPIExportEndpointSlice func(clusterName logicalcluster.Name, name string) (*apisv1alpha1.APIExportEndpointSlice, error)
197+
createAPIExportEndpointSlice func(ctx context.Context, clusterName logicalcluster.Path, apiExportEndpointSlice *apisv1alpha1.APIExportEndpointSlice) error
198+
186199
getNamespace func(clusterName logicalcluster.Name, name string) (*corev1.Namespace, error)
187200
createNamespace func(ctx context.Context, clusterName logicalcluster.Path, ns *corev1.Namespace) error
188201

pkg/reconciler/apis/apiexport/apiexport_controller_test.go

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,19 @@ func TestReconcile(t *testing.T) {
4949
apiExportHasSomeOtherHash bool
5050
hasPreexistingVerifyFailure bool
5151
listShardsError error
52+
apiExportEndpointSliceNotFound bool
5253

5354
apiBindings []interface{}
5455

55-
wantGenerationFailed bool
56-
wantError bool
57-
wantCreateSecretCalled bool
58-
wantUnsetIdentity bool
59-
wantDefaultSecretRef bool
60-
wantStatusHashSet bool
61-
wantVerifyFailure bool
62-
wantIdentityValid bool
63-
wantVirtualWorkspaceURLsError bool
64-
wantVirtualWorkspaceURLsReady bool
56+
wantGenerationFailed bool
57+
wantError bool
58+
wantCreateSecretCalled bool
59+
wantUnsetIdentity bool
60+
wantDefaultSecretRef bool
61+
wantStatusHashSet bool
62+
wantVerifyFailure bool
63+
wantIdentityValid bool
64+
wantCreateAPIExportEndpointSlice bool
6565
}{
6666
"create secret when ref is nil and secret doesn't exist": {
6767
secretExists: false,
@@ -122,20 +122,16 @@ func TestReconcile(t *testing.T) {
122122
apiBindings: []interface{}{
123123
"something",
124124
},
125-
listShardsError: errors.New("foo"),
126-
wantVirtualWorkspaceURLsError: true,
125+
listShardsError: errors.New("foo"),
127126
},
128-
"virtualWorkspaceURLs set when APIBindings present": {
127+
"create APIExportEndpointSlice when APIBindings present": {
129128
secretRefSet: true,
130129
secretExists: true,
131130

132-
wantStatusHashSet: true,
133-
wantIdentityValid: true,
134-
135-
apiBindings: []interface{}{
136-
"something",
137-
},
138-
wantVirtualWorkspaceURLsReady: true,
131+
wantStatusHashSet: true,
132+
apiExportEndpointSliceNotFound: true,
133+
wantCreateAPIExportEndpointSlice: true,
134+
wantIdentityValid: true,
139135
},
140136
}
141137

@@ -155,6 +151,15 @@ func TestReconcile(t *testing.T) {
155151
return nil
156152
},
157153
secretNamespace: "default-ns",
154+
getAPIExportEndpointSlice: func(clusterName logicalcluster.Name, name string) (*apisv1alpha1.APIExportEndpointSlice, error) {
155+
if tc.apiExportEndpointSliceNotFound {
156+
return nil, apierrors.NewNotFound(corev1.Resource("apiexportendpointslices"), name)
157+
}
158+
return &apisv1alpha1.APIExportEndpointSlice{}, nil
159+
},
160+
createAPIExportEndpointSlice: func(ctx context.Context, clusterName logicalcluster.Path, apiExportEndpointSlice *apisv1alpha1.APIExportEndpointSlice) error {
161+
return nil
162+
},
158163
getSecret: func(ctx context.Context, clusterName logicalcluster.Name, ns, name string) (*corev1.Secret, error) {
159164
if tc.secretExists {
160165
secret := &corev1.Secret{
@@ -287,21 +292,6 @@ func TestReconcile(t *testing.T) {
287292
if tc.wantIdentityValid {
288293
requireConditionMatches(t, apiExport, conditions.TrueCondition(apisv1alpha2.APIExportIdentityValid))
289294
}
290-
291-
if tc.wantVirtualWorkspaceURLsError {
292-
requireConditionMatches(t, apiExport,
293-
conditions.FalseCondition(
294-
apisv1alpha2.APIExportVirtualWorkspaceURLsReady,
295-
apisv1alpha2.ErrorGeneratingURLsReason,
296-
conditionsv1alpha1.ConditionSeverityError,
297-
"",
298-
),
299-
)
300-
}
301-
302-
if tc.wantVirtualWorkspaceURLsReady {
303-
requireConditionMatches(t, apiExport, conditions.TrueCondition(apisv1alpha2.APIExportVirtualWorkspaceURLsReady))
304-
}
305295
})
306296
}
307297
}

pkg/reconciler/apis/apiexport/apiexport_reconcile.go

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ import (
3131
"github.com/kcp-dev/logicalcluster/v3"
3232

3333
virtualworkspacesoptions "github.com/kcp-dev/kcp/cmd/virtual-workspaces/options"
34+
kcpfeatures "github.com/kcp-dev/kcp/pkg/features"
3435
"github.com/kcp-dev/kcp/pkg/logging"
3536
apiexportbuilder "github.com/kcp-dev/kcp/pkg/virtual/apiexport/builder"
3637
apisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1"
3738
apisv1alpha2 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha2"
39+
"github.com/kcp-dev/kcp/sdk/apis/core"
3840
conditionsv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/third_party/conditions/apis/conditions/v1alpha1"
3941
"github.com/kcp-dev/kcp/sdk/apis/third_party/conditions/util/conditions"
4042
)
@@ -46,6 +48,7 @@ func (c *controller) reconcile(ctx context.Context, apiExport *apisv1alpha2.APIE
4648
}
4749

4850
clusterName := logicalcluster.From(apiExport)
51+
clusterPath := apiExport.Annotations[core.LogicalClusterPathAnnotationKey]
4952

5053
if identity.SecretRef == nil {
5154
c.ensureSecretNamespaceExists(ctx, clusterName)
@@ -97,29 +100,43 @@ func (c *controller) reconcile(ctx context.Context, apiExport *apisv1alpha2.APIE
97100
)
98101
}
99102

100-
// TODO(sttts): reactivate this with multi-shard support eventually
101-
/*
102-
// check if any APIBindings are bound to this APIExport. If so, add a virtualworkspaceURL
103-
apiBindings, err := c.getAPIBindingsForAPIExport(clusterName, apiExport.Name)
104-
if err != nil {
105-
return fmt.Errorf("error checking for APIBindings with APIExport %s|%s: %w", clusterName, apiExport.Name, err)
103+
// Ensure the APIExportEndpointSlice exists
104+
_, err := c.getAPIExportEndpointSlice(clusterName, apiExport.Name)
105+
if err != nil {
106+
if errors.IsNotFound(err) {
107+
// Create the APIExportEndpointSlice
108+
apiExportEndpointSlice := &apisv1alpha1.APIExportEndpointSlice{
109+
ObjectMeta: metav1.ObjectMeta{
110+
Name: apiExport.Name,
111+
},
112+
Spec: apisv1alpha1.APIExportEndpointSliceSpec{
113+
APIExport: apisv1alpha1.ExportBindingReference{
114+
Name: apiExport.Name,
115+
Path: clusterPath,
116+
},
117+
},
118+
}
119+
if err := c.createAPIExportEndpointSlice(ctx, clusterName.Path(), apiExportEndpointSlice); err != nil {
120+
return fmt.Errorf("error creating APIExportEndpointSlice for APIExport %s|%s: %w", clusterName, apiExport.Name, err)
121+
}
122+
} else {
123+
return fmt.Errorf("error getting APIExportEndpointSlice for APIExport %s|%s: %w", clusterName, apiExport.Name, err)
106124
}
125+
}
107126

108-
// If there are no bindings, then we can't create a URL yet.
109-
if len(apiBindings) == 0 {
110-
return nil
127+
// Ensure the APIExportEndpointSlice has a virtual workspace URL
128+
// TODO(mjudeikis): Remove this and move to batteries.
129+
if kcpfeatures.DefaultFeatureGate.Enabled(kcpfeatures.EnableDeprecatedAPIExportVirtualWorkspacesUrls) {
130+
if err := c.updateVirtualWorkspaceURLs(ctx, apiExport); err != nil {
131+
conditions.MarkFalse(
132+
apiExport,
133+
apisv1alpha2.APIExportVirtualWorkspaceURLsReady,
134+
apisv1alpha2.ErrorGeneratingURLsReason,
135+
conditionsv1alpha1.ConditionSeverityError,
136+
"%v",
137+
err,
138+
)
111139
}
112-
*/
113-
114-
if err := c.updateVirtualWorkspaceURLs(ctx, apiExport); err != nil {
115-
conditions.MarkFalse(
116-
apiExport,
117-
apisv1alpha2.APIExportVirtualWorkspaceURLsReady,
118-
apisv1alpha2.ErrorGeneratingURLsReason,
119-
conditionsv1alpha1.ConditionSeverityError,
120-
"%v",
121-
err,
122-
)
123140
}
124141

125142
return nil
@@ -216,11 +233,11 @@ func (c *controller) updateVirtualWorkspaceURLs(ctx context.Context, apiExport *
216233
desiredURLs.Insert(u.String())
217234
}
218235

219-
//nolint:staticcheck // SA1019 VirtualWorkspaces is deprecated but not removed yet
236+
//nolint:staticcheck
220237
apiExport.Status.VirtualWorkspaces = nil
221238

222239
for _, u := range sets.List[string](desiredURLs) {
223-
//nolint:staticcheck // SA1019 VirtualWorkspaces is deprecated but not removed yet
240+
//nolint:staticcheck
224241
apiExport.Status.VirtualWorkspaces = append(apiExport.Status.VirtualWorkspaces, apisv1alpha2.VirtualWorkspace{
225242
URL: u,
226243
})

pkg/server/controllers.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,7 @@ func (s *Server) installAPIExportController(ctx context.Context, config *rest.Co
10491049
c, err := apiexport.NewController(
10501050
kcpClusterClient,
10511051
s.KcpSharedInformerFactory.Apis().V1alpha2().APIExports(),
1052+
s.KcpSharedInformerFactory.Apis().V1alpha1().APIExportEndpointSlices(),
10521053
s.CacheKcpSharedInformerFactory.Core().V1alpha1().Shards(),
10531054
kubeClusterClient,
10541055
s.KubeSharedInformerFactory.Core().V1().Namespaces(),
@@ -1067,6 +1068,7 @@ func (s *Server) installAPIExportController(ctx context.Context, config *rest.Co
10671068
return wait.PollUntilContextCancel(ctx, waitPollInterval, true, func(ctx context.Context) (bool, error) {
10681069
return s.ApiExtensionsSharedInformerFactory.Apiextensions().V1().CustomResourceDefinitions().Informer().HasSynced() &&
10691070
s.KcpSharedInformerFactory.Apis().V1alpha2().APIExports().Informer().HasSynced() &&
1071+
s.KcpSharedInformerFactory.Apis().V1alpha1().APIExportEndpointSlices().Informer().HasSynced() &&
10701072
s.KubeSharedInformerFactory.Core().V1().Namespaces().Informer().HasSynced() &&
10711073
s.KubeSharedInformerFactory.Core().V1().Secrets().Informer().HasSynced(), nil
10721074
})

pkg/server/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ func (s *Server) Run(ctx context.Context) error {
432432
logger.Info("finished bootstrapping the shard workspace")
433433

434434
go s.KcpSharedInformerFactory.Apis().V1alpha2().APIExports().Informer().Run(hookContext.Done())
435+
go s.KcpSharedInformerFactory.Apis().V1alpha1().APIExportEndpointSlices().Informer().Run(hookContext.Done())
435436
go s.CacheKcpSharedInformerFactory.Apis().V1alpha2().APIExports().Informer().Run(hookContext.Done())
436437
go s.KcpSharedInformerFactory.Core().V1alpha1().LogicalClusters().Informer().Run(hookContext.Done())
437438

test/e2e/apibinding/apibinding_logicalcluster_test.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"testing"
2323
"time"
2424

25+
"github.com/davecgh/go-spew/spew"
2526
"github.com/stretchr/testify/require"
2627

2728
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -32,6 +33,7 @@ import (
3233
kcpapiextensionsclientset "github.com/kcp-dev/client-go/apiextensions/client"
3334
kcpdynamic "github.com/kcp-dev/client-go/dynamic"
3435

36+
"github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1"
3537
apisv1alpha2 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha2"
3638
corev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1"
3739
"github.com/kcp-dev/kcp/sdk/apis/third_party/conditions/util/conditions"
@@ -132,8 +134,13 @@ func TestAPIBindingLogicalCluster(t *testing.T) {
132134
return kcpClusterClient.Cluster(consumerPath).ApisV1alpha2().APIBindings().Get(ctx, exportName, metav1.GetOptions{})
133135
}, kcptestinghelpers.Is(apisv1alpha2.PermissionClaimsValid), "unable to see valid claims")
134136

135-
export, err := kcpClusterClient.Cluster(providerPath).ApisV1alpha2().APIExports().Get(ctx, exportName, metav1.GetOptions{})
136-
require.NoError(t, err)
137+
t.Logf("Waiting for APIExportEndpointSlice to be available")
138+
var exportEndpointSlice *v1alpha1.APIExportEndpointSlice
139+
kcptestinghelpers.Eventually(t, func() (bool, string) {
140+
var err error
141+
exportEndpointSlice, err = kcpClusterClient.Cluster(providerPath).ApisV1alpha1().APIExportEndpointSlices().Get(ctx, exportName, metav1.GetOptions{})
142+
return err == nil && len(exportEndpointSlice.Status.APIExportEndpoints) > 0, fmt.Sprintf("failed to get APIExportEndpointSlice: %v, %s", err, spew.Sdump(exportEndpointSlice))
143+
}, wait.ForeverTestTimeout, time.Second*1)
137144

138145
rawConfig, err := server.RawConfig()
139146
require.NoError(t, err)
@@ -143,8 +150,7 @@ func TestAPIBindingLogicalCluster(t *testing.T) {
143150
kcptestinghelpers.Eventually(t, func() (bool, string) {
144151
items := []unstructured.Unstructured{}
145152

146-
//nolint:staticcheck // SA1019 VirtualWorkspaces is deprecated but not removed yet
147-
for _, vw := range export.Status.VirtualWorkspaces {
153+
for _, vw := range exportEndpointSlice.Status.APIExportEndpoints {
148154
vwClusterClient, err := kcpdynamic.NewForConfig(apiexportVWConfig(t, rawConfig, vw.URL))
149155
require.NoError(t, err)
150156

@@ -265,8 +271,13 @@ func TestAPIBindingCRDs(t *testing.T) {
265271
return kcpClusterClient.Cluster(consumerPath).ApisV1alpha2().APIBindings().Get(ctx, exportName, metav1.GetOptions{})
266272
}, kcptestinghelpers.Is(apisv1alpha2.PermissionClaimsValid), "unable to see valid claims")
267273

268-
export, err := kcpClusterClient.Cluster(providerPath).ApisV1alpha2().APIExports().Get(ctx, exportName, metav1.GetOptions{})
269-
require.NoError(t, err)
274+
t.Logf("Waiting for APIExportEndpointSlice to be available")
275+
var exportEndpointSlice *v1alpha1.APIExportEndpointSlice
276+
kcptestinghelpers.Eventually(t, func() (bool, string) {
277+
var err error
278+
exportEndpointSlice, err = kcpClusterClient.Cluster(providerPath).ApisV1alpha1().APIExportEndpointSlices().Get(ctx, exportName, metav1.GetOptions{})
279+
return err == nil && len(exportEndpointSlice.Status.APIExportEndpoints) > 0, fmt.Sprintf("failed to get APIExportEndpointSlice: %v. %s", err, spew.Sdump(exportEndpointSlice))
280+
}, time.Second*60, time.Second*1)
270281

271282
rawConfig, err := server.RawConfig()
272283
require.NoError(t, err)
@@ -276,8 +287,7 @@ func TestAPIBindingCRDs(t *testing.T) {
276287
kcptestinghelpers.Eventually(t, func() (bool, string) {
277288
items := []unstructured.Unstructured{}
278289

279-
//nolint:staticcheck // SA1019 VirtualWorkspaces is deprecated but not removed yet
280-
for _, vw := range export.Status.VirtualWorkspaces {
290+
for _, vw := range exportEndpointSlice.Status.APIExportEndpoints {
281291
vwClusterClient, err := kcpdynamic.NewForConfig(apiexportVWConfig(t, rawConfig, vw.URL))
282292
require.NoError(t, err)
283293

0 commit comments

Comments
 (0)