Skip to content

Commit d2ffee0

Browse files
authored
Merge pull request #3152 from RedbackThomson/inactive_workspaces
✨ Add inactive annotation to logicalcluster
2 parents 9d45e6d + 4df1bd8 commit d2ffee0

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

pkg/server/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ func NewConfig(opts kcpserveroptions.CompletedOptions) (*Config, error) {
445445
apiHandler = filters.WithWarningRecorder(apiHandler)
446446

447447
apiHandler = kcpfilters.WithAuditEventClusterAnnotation(apiHandler)
448+
apiHandler = kcpfilters.WithBlockInactiveLogicalClusters(apiHandler, c.KcpSharedInformerFactory.Core().V1alpha1().LogicalClusters())
448449

449450
// Add a mux before the chain, for other handlers with their own handler chain to hook in. For example, when
450451
// the virtual workspace server is running as part of kcp, it registers /services with the mux so it can handle

pkg/server/filters/filters.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package filters
1818

1919
import (
2020
"context"
21+
"errors"
2122
"fmt"
2223
"net/http"
2324
"net/url"
@@ -35,6 +36,9 @@ import (
3536
kaudit "k8s.io/apiserver/pkg/audit"
3637
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
3738
"k8s.io/apiserver/pkg/endpoints/request"
39+
40+
corev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1"
41+
informersv1alpha1 "github.com/kcp-dev/kcp/sdk/client/informers/externalversions/core/v1alpha1"
3842
)
3943

4044
type (
@@ -44,6 +48,10 @@ type (
4448
const (
4549
workspaceAnnotation = "tenancy.kcp.io/workspace"
4650

51+
// inactiveAnnotation is the annotation denoting a logical cluster should be
52+
// deemed unreachable.
53+
inactiveAnnotation = "internal.kcp.io/inactive"
54+
4755
// clusterKey is the context key for the request namespace.
4856
acceptHeaderContextKey acceptHeaderContextKeyType = iota
4957
)
@@ -78,6 +86,49 @@ func WithAuditEventClusterAnnotation(handler http.Handler) http.HandlerFunc {
7886
})
7987
}
8088

89+
// WithBlockInactiveLogicalClusters ensures that any requests to logical
90+
// clusters marked inactive are rejected.
91+
func WithBlockInactiveLogicalClusters(handler http.Handler, kcpClusterClient informersv1alpha1.LogicalClusterClusterInformer) http.HandlerFunc {
92+
allowedPathPrefixes := []string{
93+
"/openapi",
94+
"/apis/core.kcp.io/v1alpha1/logicalclusters",
95+
}
96+
97+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
98+
_, newURL, _, err := ClusterPathFromAndStrip(req)
99+
if err != nil {
100+
responsewriters.InternalError(w, req, err)
101+
return
102+
}
103+
104+
isException := false
105+
for _, prefix := range allowedPathPrefixes {
106+
if strings.HasPrefix(newURL.String(), prefix) {
107+
isException = true
108+
}
109+
}
110+
111+
cluster := request.ClusterFrom(req.Context())
112+
if cluster != nil && !cluster.Name.Empty() && !isException {
113+
logicalCluster, err := kcpClusterClient.Cluster(cluster.Name).Lister().Get(corev1alpha1.LogicalClusterName)
114+
if err == nil {
115+
if ann, ok := logicalCluster.ObjectMeta.Annotations[inactiveAnnotation]; ok && ann == "true" {
116+
responsewriters.ErrorNegotiated(
117+
apierrors.NewForbidden(corev1alpha1.Resource("logicalclusters"), cluster.Name.String(), errors.New("logical cluster is marked inactive")),
118+
errorCodecs, schema.GroupVersion{}, w, req,
119+
)
120+
return
121+
}
122+
} else if !apierrors.IsNotFound(err) {
123+
responsewriters.InternalError(w, req, err)
124+
return
125+
}
126+
}
127+
128+
handler.ServeHTTP(w, req)
129+
})
130+
}
131+
81132
// WithClusterScope reads a cluster name from the URL path and puts it into the context.
82133
// It also trims "/clusters/" prefix from the URL.
83134
func WithClusterScope(apiHandler http.Handler) http.HandlerFunc {

0 commit comments

Comments
 (0)