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:
KubeSphere CI Bot
2024-09-06 11:05:52 +08:00
committed by GitHub
parent b5015ec7b9
commit 447a51f08b
8557 changed files with 546695 additions and 1146174 deletions

View File

@@ -79,6 +79,8 @@ type Builder struct {
stdinInUse bool
dir bool
visitorConcurrency int
labelSelector *string
fieldSelector *string
selectAll bool
@@ -233,6 +235,13 @@ func (b *Builder) AddError(err error) *Builder {
return b
}
// VisitorConcurrency sets the number of concurrent visitors to use when
// visiting lists.
func (b *Builder) VisitorConcurrency(concurrency int) *Builder {
b.visitorConcurrency = concurrency
return b
}
// FilenameParam groups input in two categories: URLs and files (files, directories, STDIN)
// If enforceNamespace is false, namespaces in the specs will be allowed to
// override the default namespace. If it is true, namespaces that don't match
@@ -1124,7 +1133,10 @@ func (b *Builder) visitByPaths() *Result {
if b.continueOnError {
visitors = EagerVisitorList(b.paths)
} else {
visitors = VisitorList(b.paths)
visitors = ConcurrentVisitorList{
visitors: b.paths,
concurrency: b.visitorConcurrency,
}
}
if b.flatten {

View File

@@ -0,0 +1,59 @@
/*
Copyright 2023 The Kubernetes 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.
*/
package resource
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
)
// fallbackQueryParamVerifier encapsulates the primary Verifier that
// is invoked, and the secondary/fallback Verifier.
type fallbackQueryParamVerifier struct {
primary Verifier
secondary Verifier
}
var _ Verifier = &fallbackQueryParamVerifier{}
// NewFallbackQueryParamVerifier returns a new Verifier which will invoke the
// initial/primary Verifier. If the primary Verifier is "NotFound", then the
// secondary Verifier is invoked as a fallback.
func NewFallbackQueryParamVerifier(primary Verifier, secondary Verifier) Verifier {
return &fallbackQueryParamVerifier{
primary: primary,
secondary: secondary,
}
}
// HasSupport returns an error if the passed GVK does not support the
// query param (fieldValidation), as determined by the primary and
// secondary OpenAPI endpoints. The primary endoint is checked first,
// but if there is an error retrieving the OpenAPI V3 document, the
// secondary attempts to determine support. If the GVK supports the query param,
// nil is returned.
func (f *fallbackQueryParamVerifier) HasSupport(gvk schema.GroupVersionKind) error {
err := f.primary.HasSupport(gvk)
// If an error was returned from the primary OpenAPI endpoint,
// we fallback to check the secondary OpenAPI endpoint for
// any error *except* "paramUnsupportedError".
if err != nil && !IsParamUnsupportedError(err) {
klog.V(7).Infof("openapi v3 error...falling back to legacy: %s", err)
err = f.secondary.HasSupport(gvk)
}
return err
}

View File

@@ -38,7 +38,7 @@ type KustomizeVisitor struct {
// Visit passes the result of a kustomize build to a StreamVisitor.
func (v *KustomizeVisitor) Visit(fn VisitorFunc) error {
kOpts := krusty.MakeDefaultOptions()
kOpts.DoLegacyResourceSort = true
kOpts.Reorder = krusty.ReorderOptionLegacy
k := krusty.MakeKustomizer(kOpts)
m, err := k.Run(v.fSys, v.dirPath)
if err != nil {

View File

@@ -20,8 +20,9 @@ import (
"errors"
"fmt"
openapi_v2 "github.com/google/gnostic/openapiv2"
openapi_v2 "github.com/google/gnostic-models/openapiv2"
yaml "gopkg.in/yaml.v2"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
@@ -38,16 +39,10 @@ func NewQueryParamVerifier(dynamicClient dynamic.Interface, openAPIGetter discov
// QueryParamVerifier verifies if a given group-version-kind supports a
// given VerifiableQueryParam against the current server.
//
// Currently supported query params are:
// 1. dryRun
// 2. fieldValidation
// Currently supported query params are: fieldValidation
//
// Support for each of these query params needs to be verified because:
//
// 1. Sending dryRun requests to apiserver that
// don't support it will result in objects being unwillingly persisted.
//
// 2. We determine whether or not to perform server-side or client-side
// Support for each of these query params needs to be verified because
// we determine whether or not to perform server-side or client-side
// schema validation based on whether the fieldValidation query param is
// supported or not.
//
@@ -73,12 +68,15 @@ type Verifier interface {
type VerifiableQueryParam string
const (
QueryParamDryRun VerifiableQueryParam = "dryRun"
QueryParamFieldValidation VerifiableQueryParam = "fieldValidation"
)
// HasSupport checks if the given gvk supports the query param configured on v
func (v *QueryParamVerifier) HasSupport(gvk schema.GroupVersionKind) error {
if (gvk == schema.GroupVersionKind{Version: "v1", Kind: "List"}) {
return NewParamUnsupportedError(gvk, v.queryParam)
}
oapi, err := v.openAPIGetter.OpenAPISchema()
if err != nil {
return fmt.Errorf("failed to download openapi: %v", err)
@@ -149,6 +147,11 @@ func hasGVKExtension(extensions []*openapi_v2.NamedAny, gvk schema.GroupVersionK
// specific group-version-kind supports the specific query parameter for
// the PATCH end-point.
func supportsQueryParam(doc *openapi_v2.Document, gvk schema.GroupVersionKind, queryParam VerifiableQueryParam) (bool, error) {
globalParams := map[string]*openapi_v2.NamedParameter{}
for _, p := range doc.GetParameters().GetAdditionalProperties() {
globalParams["#/parameters/"+p.GetName()] = p
}
for _, path := range doc.GetPaths().GetPath() {
// Is this describing the gvk we're looking for?
if !hasGVKExtension(path.GetValue().GetPatch().GetVendorExtension(), gvk) {
@@ -158,6 +161,13 @@ func supportsQueryParam(doc *openapi_v2.Document, gvk schema.GroupVersionKind, q
if param.GetParameter().GetNonBodyParameter().GetQueryParameterSubSchema().GetName() == string(queryParam) {
return true, nil
}
// lookup global parameters
if ref := param.GetJsonReference().GetXRef(); ref != "" {
if globalParam, ok := globalParams[ref]; ok && globalParam != nil && globalParam.GetValue().GetNonBodyParameter().GetQueryParameterSubSchema().GetName() == string(queryParam) {
return true, nil
}
}
}
return false, nil
}

View File

@@ -0,0 +1,145 @@
/*
Copyright 2023 The Kubernetes 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.
*/
package resource
import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/openapi"
"k8s.io/client-go/openapi3"
"k8s.io/kube-openapi/pkg/spec3"
"k8s.io/kube-openapi/pkg/validation/spec"
)
var _ Verifier = &queryParamVerifierV3{}
// NewQueryParamVerifierV3 returns a pointer to the created queryParamVerifier3 struct,
// which implements the Verifier interface. The caching characteristics of the
// OpenAPI V3 specs are determined by the passed oapiClient. For memory caching, the
// client should be wrapped beforehand as: cached.NewClient(oapiClient). The disk
// caching is determined by the discovery client the oapiClient is created from.
func NewQueryParamVerifierV3(dynamicClient dynamic.Interface, oapiClient openapi.Client, queryParam VerifiableQueryParam) Verifier {
return &queryParamVerifierV3{
finder: NewCRDFinder(CRDFromDynamic(dynamicClient)),
root: openapi3.NewRoot(oapiClient),
queryParam: queryParam,
}
}
// queryParamVerifierV3 encapsulates info necessary to determine if
// the queryParam is a parameter for the Patch endpoint for a
// passed GVK.
type queryParamVerifierV3 struct {
finder CRDFinder
root openapi3.Root
queryParam VerifiableQueryParam
}
var namespaceGVK = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}
// HasSupport returns nil error if the passed GVK supports the parameter
// (stored in struct; usually "fieldValidation") for Patch endpoint.
// Returns an error if the passed GVK does not support the query param,
// or if another error occurred. If the Open API V3 spec for a CRD is not
// found, then the spec for Namespace is checked for query param support instead.
func (v *queryParamVerifierV3) HasSupport(gvk schema.GroupVersionKind) error {
if (gvk == schema.GroupVersionKind{Version: "v1", Kind: "List"}) {
return NewParamUnsupportedError(gvk, v.queryParam)
}
gvSpec, err := v.root.GVSpec(gvk.GroupVersion())
if err == nil {
return supportsQueryParamV3(gvSpec, gvk, v.queryParam)
}
if _, isErr := err.(*openapi3.GroupVersionNotFoundError); !isErr {
return err
}
// If the spec for the passed GVK is not found, then check if it is a CRD.
// For CRD's substitute Namespace OpenAPI V3 spec to check if query param is supported.
if found, _ := v.finder.HasCRD(gvk.GroupKind()); found {
namespaceSpec, err := v.root.GVSpec(namespaceGVK.GroupVersion())
if err != nil {
// If error retrieving Namespace spec, propagate error.
return err
}
return supportsQueryParamV3(namespaceSpec, namespaceGVK, v.queryParam)
}
return NewParamUnsupportedError(gvk, v.queryParam)
}
// hasGVKExtensionV3 returns true if the passed OpenAPI extensions map contains
// the passed GVK; false otherwise.
func hasGVKExtensionV3(extensions spec.Extensions, gvk schema.GroupVersionKind) bool {
var oapiGVK map[string]string
err := extensions.GetObject("x-kubernetes-group-version-kind", &oapiGVK)
if err != nil {
return false
}
if oapiGVK["group"] == gvk.Group &&
oapiGVK["version"] == gvk.Version &&
oapiGVK["kind"] == gvk.Kind {
return true
}
return false
}
// supportsQueryParam is a method that let's us look in the OpenAPI if the
// specific group-version-kind supports the specific query parameter for
// the PATCH end-point. Returns nil if the passed GVK supports the passed
// query parameter; otherwise, a "paramUnsupportedError" is returned (except
// when an invalid document error is returned when an invalid OpenAPI V3
// is passed in).
func supportsQueryParamV3(doc *spec3.OpenAPI, gvk schema.GroupVersionKind, queryParam VerifiableQueryParam) error {
if doc == nil || doc.Paths == nil {
return fmt.Errorf("Invalid OpenAPI V3 document")
}
for _, path := range doc.Paths.Paths {
// If operation is not PATCH, then continue.
if path == nil {
continue
}
op := path.PathProps.Patch
if op == nil {
continue
}
// Is this PATCH operation for the passed GVK?
if !hasGVKExtensionV3(op.VendorExtensible.Extensions, gvk) {
continue
}
// Now look for the query parameter among the parameters
// for the PATCH operation.
for _, param := range op.OperationProps.Parameters {
if param.ParameterProps.Name == string(queryParam) && param.In == "query" {
return nil
}
// lookup global parameters
if ref := param.Refable.Ref.Ref.String(); strings.HasPrefix(ref, "#/parameters/") && doc.Components != nil {
k := strings.TrimPrefix(ref, "#/parameters/")
if globalParam, ok := doc.Components.Parameters[k]; ok && globalParam != nil {
if globalParam.In == "query" && globalParam.Name == string(queryParam) {
return nil
}
}
}
}
return NewParamUnsupportedError(gvk, queryParam)
}
return fmt.Errorf("Path not found for GVK (%s) in OpenAPI V3 doc", gvk)
}

View File

@@ -28,6 +28,7 @@ import (
"strings"
"time"
"golang.org/x/sync/errgroup"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
"k8s.io/apimachinery/pkg/api/errors"
@@ -201,6 +202,33 @@ func (l VisitorList) Visit(fn VisitorFunc) error {
return nil
}
type ConcurrentVisitorList struct {
visitors []Visitor
concurrency int
}
func (l ConcurrentVisitorList) Visit(fn VisitorFunc) error {
g := errgroup.Group{}
// Concurrency 1 just runs the visitors sequentially, this is the default
// as it preserves the previous behavior, but allows components to opt into
// concurrency.
concurrency := 1
if l.concurrency > concurrency {
concurrency = l.concurrency
}
g.SetLimit(concurrency)
for i := range l.visitors {
i := i
g.Go(func() error {
return l.visitors[i].Visit(fn)
})
}
return g.Wait()
}
// EagerVisitorList implements Visit for the sub visitors it contains. All errors
// will be captured and returned at the end of iteration.
type EagerVisitorList []Visitor