feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
@@ -19,8 +19,6 @@ package request
|
||||
import (
|
||||
"context"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/apis/audit"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
@@ -30,48 +28,15 @@ import (
|
||||
type key int
|
||||
|
||||
const (
|
||||
// namespaceKey is the context key for the request namespace.
|
||||
namespaceKey key = iota
|
||||
|
||||
// userKey is the context key for the request user.
|
||||
userKey
|
||||
|
||||
// auditKey is the context key for the audit event.
|
||||
auditKey
|
||||
userKey key = iota
|
||||
)
|
||||
|
||||
// NewContext instantiates a base context object for request flows.
|
||||
func NewContext() context.Context {
|
||||
return context.TODO()
|
||||
}
|
||||
|
||||
// NewDefaultContext instantiates a base context object for request flows in the default namespace
|
||||
func NewDefaultContext() context.Context {
|
||||
return WithNamespace(NewContext(), metav1.NamespaceDefault)
|
||||
}
|
||||
|
||||
// WithValue returns a copy of parent in which the value associated with key is val.
|
||||
func WithValue(parent context.Context, key interface{}, val interface{}) context.Context {
|
||||
return context.WithValue(parent, key, val)
|
||||
}
|
||||
|
||||
// WithNamespace returns a copy of parent in which the namespace value is set
|
||||
func WithNamespace(parent context.Context, namespace string) context.Context {
|
||||
return WithValue(parent, namespaceKey, namespace)
|
||||
}
|
||||
|
||||
// NamespaceFrom returns the value of the namespace key on the ctx
|
||||
func NamespaceFrom(ctx context.Context) (string, bool) {
|
||||
namespace, ok := ctx.Value(namespaceKey).(string)
|
||||
return namespace, ok
|
||||
}
|
||||
|
||||
// NamespaceValue returns the value of the namespace key on the ctx, or the empty string if none
|
||||
func NamespaceValue(ctx context.Context) string {
|
||||
namespace, _ := NamespaceFrom(ctx)
|
||||
return namespace
|
||||
}
|
||||
|
||||
// WithUser returns a copy of parent in which the user value is set
|
||||
func WithUser(parent context.Context, user user.Info) context.Context {
|
||||
return WithValue(parent, userKey, user)
|
||||
@@ -82,14 +47,3 @@ func UserFrom(ctx context.Context) (user.Info, bool) {
|
||||
user, ok := ctx.Value(userKey).(user.Info)
|
||||
return user, ok
|
||||
}
|
||||
|
||||
// WithAuditEvent returns set audit event struct.
|
||||
func WithAuditEvent(parent context.Context, ev *audit.Event) context.Context {
|
||||
return WithValue(parent, auditKey, ev)
|
||||
}
|
||||
|
||||
// AuditEventFrom returns the audit event struct on the ctx
|
||||
func AuditEventFrom(ctx context.Context) *audit.Event {
|
||||
ev, _ := ctx.Value(auditKey).(*audit.Event)
|
||||
return ev
|
||||
}
|
||||
|
||||
@@ -17,35 +17,17 @@ limitations under the License.
|
||||
package request
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
// Following code copied from k8s.io/apiserver/pkg/endpoints/request to avoid import collision
|
||||
|
||||
// TestNamespaceContext validates that a namespace can be get/set on a context object
|
||||
func TestNamespaceContext(t *testing.T) {
|
||||
ctx := NewDefaultContext()
|
||||
result, ok := NamespaceFrom(ctx)
|
||||
if !ok {
|
||||
t.Fatalf("Error getting namespace")
|
||||
}
|
||||
if metav1.NamespaceDefault != result {
|
||||
t.Fatalf("Expected: %s, Actual: %s", metav1.NamespaceDefault, result)
|
||||
}
|
||||
|
||||
ctx = NewContext()
|
||||
_, ok = NamespaceFrom(ctx)
|
||||
if ok {
|
||||
t.Fatalf("Should not be ok because there is no namespace on the context")
|
||||
}
|
||||
}
|
||||
|
||||
// TestUserContext validates that a userinfo can be get/set on a context object
|
||||
func TestUserContext(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
ctx := context.TODO()
|
||||
_, ok := UserFrom(ctx)
|
||||
if ok {
|
||||
t.Fatalf("Should not be ok because there is no user.Info on the context")
|
||||
@@ -91,5 +73,4 @@ func TestUserContext(t *testing.T) {
|
||||
} else if actualExtra[expectedExtraKey][0] != expectedExtraValue {
|
||||
t.Fatalf("Get user extra map value error, Expected: %s, Actual: %s", expectedExtraValue, actualExtra[expectedExtraKey])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,7 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
* Please refer to the LICENSE file in the root directory of the project.
|
||||
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
// NOTE: This file is copied from k8s.io/apiserver/pkg/endpoints/request.
|
||||
// We expanded requestInfo.
|
||||
@@ -81,9 +70,6 @@ type RequestInfo struct {
|
||||
// Cluster of requested resource, this is empty in single-cluster environment
|
||||
Cluster string
|
||||
|
||||
// DevOps project of requested resource
|
||||
DevOps string
|
||||
|
||||
// Scope of requested resource.
|
||||
ResourceScope string
|
||||
|
||||
@@ -125,8 +111,8 @@ type RequestInfoFactory struct {
|
||||
// /kapis/{api-group}/{version}/namespaces/{namespace}/{resource}
|
||||
// /kapis/{api-group}/{version}/namespaces/{namespace}/{resource}/{resourceName}
|
||||
// With workspaces:
|
||||
// /kapis/clusters/{cluster}/{api-group}/{version}/namespaces/{namespace}/{resource}
|
||||
// /kapis/clusters/{cluster}/{api-group}/{version}/namespaces/{namespace}/{resource}/{resourceName}
|
||||
// /clusters/{cluster}/kapis/{api-group}/{version}/namespaces/{namespace}/{resource}
|
||||
// /clusters/{cluster}/kapis/{api-group}/{version}/namespaces/{namespace}/{resource}/{resourceName}
|
||||
func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, error) {
|
||||
requestInfo := RequestInfo{
|
||||
IsKubernetesRequest: false,
|
||||
@@ -144,7 +130,7 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
|
||||
prefix := requestInfo.APIPrefix
|
||||
if prefix == "" {
|
||||
currentParts := splitPath(requestInfo.Path)
|
||||
//Proxy discovery API
|
||||
// Proxy discovery API
|
||||
if len(currentParts) > 0 && len(currentParts) < 3 {
|
||||
prefix = currentParts[0]
|
||||
}
|
||||
@@ -159,6 +145,18 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
|
||||
return &requestInfo, nil
|
||||
}
|
||||
|
||||
// URL forms: /clusters/{cluster}/*
|
||||
if currentParts[0] == "clusters" {
|
||||
if len(currentParts) > 1 {
|
||||
requestInfo.Cluster = currentParts[1]
|
||||
// resolve the real path behind the cluster dispatcher
|
||||
requestInfo.Path = strings.TrimPrefix(requestInfo.Path, fmt.Sprintf("/clusters/%s", requestInfo.Cluster))
|
||||
}
|
||||
if len(currentParts) > 2 {
|
||||
currentParts = currentParts[2:]
|
||||
}
|
||||
}
|
||||
|
||||
if !r.APIPrefixes.Has(currentParts[0]) {
|
||||
// return a non-resource request
|
||||
return &requestInfo, nil
|
||||
@@ -166,13 +164,19 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
|
||||
requestInfo.APIPrefix = currentParts[0]
|
||||
currentParts = currentParts[1:]
|
||||
|
||||
// URL forms: /clusters/{cluster}/*
|
||||
if currentParts[0] == "clusters" {
|
||||
if len(currentParts) > 1 {
|
||||
requestInfo.Cluster = currentParts[1]
|
||||
}
|
||||
if len(currentParts) > 2 {
|
||||
currentParts = currentParts[2:]
|
||||
// fallback to legacy cluster API
|
||||
// TODO remove the following codes
|
||||
if requestInfo.Cluster == "" {
|
||||
// URL forms: /(kapis|apis|api)/clusters/{cluster}/*
|
||||
if currentParts[0] == "clusters" {
|
||||
if len(currentParts) > 1 {
|
||||
requestInfo.Cluster = currentParts[1]
|
||||
// resolve the real path behind the cluster dispatcher
|
||||
requestInfo.Path = strings.Replace(requestInfo.Path, fmt.Sprintf("/clusters/%s", requestInfo.Cluster), "", 1)
|
||||
}
|
||||
if len(currentParts) > 2 {
|
||||
currentParts = currentParts[2:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +220,7 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
|
||||
}
|
||||
|
||||
// URL forms: /workspaces/{workspace}/*
|
||||
if currentParts[0] == "workspaces" {
|
||||
if currentParts[0] == "workspaces" || currentParts[0] == "workspacetemplates" {
|
||||
if len(currentParts) > 1 {
|
||||
requestInfo.Workspace = currentParts[1]
|
||||
}
|
||||
@@ -230,25 +234,12 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
|
||||
if len(currentParts) > 1 {
|
||||
requestInfo.Namespace = currentParts[1]
|
||||
|
||||
// if there is another step after the namespace name and it is not a known namespace subresource
|
||||
// if there is another step after the namespace name, and it is not a known namespace subresource
|
||||
// move currentParts to include it as a resource in its own right
|
||||
if len(currentParts) > 2 && !namespaceSubresources.Has(currentParts[2]) {
|
||||
currentParts = currentParts[2:]
|
||||
}
|
||||
}
|
||||
} else if currentParts[0] == "devops" {
|
||||
if len(currentParts) > 1 {
|
||||
requestInfo.DevOps = currentParts[1]
|
||||
|
||||
// if there is another step after the devops name
|
||||
// move currentParts to include it as a resource in its own right
|
||||
if len(currentParts) > 2 {
|
||||
currentParts = currentParts[2:]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requestInfo.Namespace = metav1.NamespaceNone
|
||||
requestInfo.DevOps = metav1.NamespaceNone
|
||||
}
|
||||
|
||||
// parsing successful, so we now know the proper value for .Parts
|
||||
@@ -327,7 +318,7 @@ type requestInfoKeyType int
|
||||
const requestInfoKey requestInfoKeyType = iota
|
||||
|
||||
func WithRequestInfo(parent context.Context, info *RequestInfo) context.Context {
|
||||
return k8srequest.WithValue(parent, requestInfoKey, info)
|
||||
return context.WithValue(parent, requestInfoKey, info)
|
||||
}
|
||||
|
||||
func RequestInfoFrom(ctx context.Context) (*RequestInfo, bool) {
|
||||
@@ -349,12 +340,15 @@ const (
|
||||
ClusterScope = "Cluster"
|
||||
WorkspaceScope = "Workspace"
|
||||
NamespaceScope = "Namespace"
|
||||
DevOpsScope = "DevOps"
|
||||
workspaceSelectorPrefix = constants.WorkspaceLabelKey + "="
|
||||
)
|
||||
|
||||
func (r *RequestInfoFactory) resolveResourceScope(request RequestInfo) string {
|
||||
if r.isGlobalScopeResource(request.APIGroup, request.Resource) {
|
||||
// GET /apis/tenant.kubesphere.io/v1beta1/workspaces/{workspace}
|
||||
if request.Workspace != "" {
|
||||
return WorkspaceScope
|
||||
}
|
||||
return GlobalScope
|
||||
}
|
||||
|
||||
@@ -362,10 +356,6 @@ func (r *RequestInfoFactory) resolveResourceScope(request RequestInfo) string {
|
||||
return NamespaceScope
|
||||
}
|
||||
|
||||
if request.DevOps != "" {
|
||||
return DevOpsScope
|
||||
}
|
||||
|
||||
if request.Workspace != "" {
|
||||
return WorkspaceScope
|
||||
}
|
||||
|
||||
@@ -1,18 +1,7 @@
|
||||
/*
|
||||
Copyright 2020 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
* Please refer to the LICENSE file in the root directory of the project.
|
||||
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package request
|
||||
|
||||
@@ -59,7 +48,7 @@ func TestRequestInfoFactory_NewRequestInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "list clusterRoles of cluster gondor",
|
||||
url: "/apis/clusters/gondor/rbac.authorization.k8s.io/v1/clusterroles",
|
||||
url: "/clusters/gondor/apis/rbac.authorization.k8s.io/v1/clusterroles",
|
||||
method: http.MethodGet,
|
||||
expectedErr: nil,
|
||||
expectedVerb: "list",
|
||||
@@ -81,7 +70,7 @@ func TestRequestInfoFactory_NewRequestInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "list nodes of cluster gondor",
|
||||
url: "/api/clusters/gondor/v1/nodes",
|
||||
url: "/clusters/gondor/api/v1/nodes",
|
||||
method: http.MethodGet,
|
||||
expectedErr: nil,
|
||||
expectedVerb: "list",
|
||||
@@ -92,7 +81,7 @@ func TestRequestInfoFactory_NewRequestInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "list roles of cluster gondor",
|
||||
url: "/apis/clusters/gondor/rbac.authorization.k8s.io/v1/namespaces/namespace1/roles",
|
||||
url: "/clusters/gondor/apis/rbac.authorization.k8s.io/v1/namespaces/namespace1/roles",
|
||||
method: http.MethodGet,
|
||||
expectedErr: nil,
|
||||
expectedVerb: "list",
|
||||
@@ -128,7 +117,7 @@ func TestRequestInfoFactory_NewRequestInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "list namespaces of cluster gondor",
|
||||
url: "/kapis/clusters/gondor/resources.kubesphere.io/v1alpha3/workspaces/workspace1/namespaces",
|
||||
url: "/clusters/gondor/kapis/resources.kubesphere.io/v1alpha3/workspaces/workspace1/namespaces",
|
||||
method: http.MethodGet,
|
||||
expectedErr: nil,
|
||||
expectedVerb: "list",
|
||||
|
||||
Reference in New Issue
Block a user