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

@@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/version"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/apiserver/pkg/endpoints/metrics"
"sync/atomic"
@@ -35,6 +36,15 @@ import (
"k8s.io/klog/v2"
)
type Source uint
// The GroupVersion from the lowest Source takes precedence
const (
AggregatorSource Source = 0
BuiltinSource Source = 100
CRDSource Source = 200
)
// This handler serves the /apis endpoint for an aggregated list of
// api resources indexed by their group version.
type ResourceManager interface {
@@ -64,19 +74,67 @@ type ResourceManager interface {
// Thread-Safe
SetGroups([]apidiscoveryv2beta1.APIGroupDiscovery)
// Returns the same resource manager using a different source
// The source is used to decide how to de-duplicate groups.
// The group from the least-numbered source is used
WithSource(source Source) ResourceManager
http.Handler
}
type resourceManager struct {
source Source
*resourceDiscoveryManager
}
func (rm resourceManager) AddGroupVersion(groupName string, value apidiscoveryv2beta1.APIVersionDiscovery) {
rm.resourceDiscoveryManager.AddGroupVersion(rm.source, groupName, value)
}
func (rm resourceManager) SetGroupVersionPriority(gv metav1.GroupVersion, grouppriority, versionpriority int) {
rm.resourceDiscoveryManager.SetGroupVersionPriority(rm.source, gv, grouppriority, versionpriority)
}
func (rm resourceManager) RemoveGroup(groupName string) {
rm.resourceDiscoveryManager.RemoveGroup(rm.source, groupName)
}
func (rm resourceManager) RemoveGroupVersion(gv metav1.GroupVersion) {
rm.resourceDiscoveryManager.RemoveGroupVersion(rm.source, gv)
}
func (rm resourceManager) SetGroups(groups []apidiscoveryv2beta1.APIGroupDiscovery) {
rm.resourceDiscoveryManager.SetGroups(rm.source, groups)
}
func (rm resourceManager) WithSource(source Source) ResourceManager {
return resourceManager{
source: source,
resourceDiscoveryManager: rm.resourceDiscoveryManager,
}
}
type groupKey struct {
name string
// Source identifies where this group came from and dictates which group
// among duplicates is chosen to be used for discovery.
source Source
}
type groupVersionKey struct {
metav1.GroupVersion
source Source
}
type resourceDiscoveryManager struct {
serializer runtime.NegotiatedSerializer
// cache is an atomic pointer to avoid the use of locks
cache atomic.Pointer[cachedGroupList]
serveHTTPFunc http.HandlerFunc
// Writes protected by the lock.
// List of all apigroups & resources indexed by the resource manager
lock sync.RWMutex
apiGroups map[string]*apidiscoveryv2beta1.APIGroupDiscovery
versionPriorities map[metav1.GroupVersion]priorityInfo
apiGroups map[groupKey]*apidiscoveryv2beta1.APIGroupDiscovery
versionPriorities map[groupVersionKey]priorityInfo
}
type priorityInfo struct {
@@ -84,25 +142,46 @@ type priorityInfo struct {
VersionPriority int
}
func NewResourceManager() ResourceManager {
func NewResourceManager(path string) ResourceManager {
scheme := runtime.NewScheme()
codecs := serializer.NewCodecFactory(scheme)
utilruntime.Must(apidiscoveryv2beta1.AddToScheme(scheme))
return &resourceDiscoveryManager{serializer: codecs, versionPriorities: make(map[metav1.GroupVersion]priorityInfo)}
rdm := &resourceDiscoveryManager{
serializer: codecs,
versionPriorities: make(map[groupVersionKey]priorityInfo),
}
rdm.serveHTTPFunc = metrics.InstrumentHandlerFunc("GET",
/* group = */ "",
/* version = */ "",
/* resource = */ "",
/* subresource = */ path,
/* scope = */ "",
/* component = */ metrics.APIServerComponent,
/* deprecated */ false,
/* removedRelease */ "",
rdm.serveHTTP)
return resourceManager{
source: BuiltinSource,
resourceDiscoveryManager: rdm,
}
}
func (rdm *resourceDiscoveryManager) SetGroupVersionPriority(gv metav1.GroupVersion, groupPriorityMinimum, versionPriority int) {
func (rdm *resourceDiscoveryManager) SetGroupVersionPriority(source Source, gv metav1.GroupVersion, groupPriorityMinimum, versionPriority int) {
rdm.lock.Lock()
defer rdm.lock.Unlock()
rdm.versionPriorities[gv] = priorityInfo{
key := groupVersionKey{
GroupVersion: gv,
source: source,
}
rdm.versionPriorities[key] = priorityInfo{
GroupPriorityMinimum: groupPriorityMinimum,
VersionPriority: versionPriority,
}
rdm.cache.Store(nil)
}
func (rdm *resourceDiscoveryManager) SetGroups(groups []apidiscoveryv2beta1.APIGroupDiscovery) {
func (rdm *resourceDiscoveryManager) SetGroups(source Source, groups []apidiscoveryv2beta1.APIGroupDiscovery) {
rdm.lock.Lock()
defer rdm.lock.Unlock()
@@ -111,13 +190,17 @@ func (rdm *resourceDiscoveryManager) SetGroups(groups []apidiscoveryv2beta1.APIG
for _, group := range groups {
for _, version := range group.Versions {
rdm.addGroupVersionLocked(group.Name, version)
rdm.addGroupVersionLocked(source, group.Name, version)
}
}
// Filter unused out priority entries
for gv := range rdm.versionPriorities {
entry, exists := rdm.apiGroups[gv.Group]
key := groupKey{
source: source,
name: gv.Group,
}
entry, exists := rdm.apiGroups[key]
if !exists {
delete(rdm.versionPriorities, gv)
continue
@@ -138,21 +221,25 @@ func (rdm *resourceDiscoveryManager) SetGroups(groups []apidiscoveryv2beta1.APIG
}
}
func (rdm *resourceDiscoveryManager) AddGroupVersion(groupName string, value apidiscoveryv2beta1.APIVersionDiscovery) {
func (rdm *resourceDiscoveryManager) AddGroupVersion(source Source, groupName string, value apidiscoveryv2beta1.APIVersionDiscovery) {
rdm.lock.Lock()
defer rdm.lock.Unlock()
rdm.addGroupVersionLocked(groupName, value)
rdm.addGroupVersionLocked(source, groupName, value)
}
func (rdm *resourceDiscoveryManager) addGroupVersionLocked(groupName string, value apidiscoveryv2beta1.APIVersionDiscovery) {
klog.Infof("Adding GroupVersion %s %s to ResourceManager", groupName, value.Version)
func (rdm *resourceDiscoveryManager) addGroupVersionLocked(source Source, groupName string, value apidiscoveryv2beta1.APIVersionDiscovery) {
if rdm.apiGroups == nil {
rdm.apiGroups = make(map[string]*apidiscoveryv2beta1.APIGroupDiscovery)
rdm.apiGroups = make(map[groupKey]*apidiscoveryv2beta1.APIGroupDiscovery)
}
if existing, groupExists := rdm.apiGroups[groupName]; groupExists {
key := groupKey{
source: source,
name: groupName,
}
if existing, groupExists := rdm.apiGroups[key]; groupExists {
// If this version already exists, replace it
versionExists := false
@@ -165,6 +252,7 @@ func (rdm *resourceDiscoveryManager) addGroupVersionLocked(groupName string, val
if reflect.DeepEqual(existing.Versions[i], value) {
return
}
existing.Versions[i] = value
versionExists = true
break
@@ -182,12 +270,17 @@ func (rdm *resourceDiscoveryManager) addGroupVersionLocked(groupName string, val
},
Versions: []apidiscoveryv2beta1.APIVersionDiscovery{value},
}
rdm.apiGroups[groupName] = group
rdm.apiGroups[key] = group
}
klog.Infof("Adding GroupVersion %s %s to ResourceManager", groupName, value.Version)
gv := metav1.GroupVersion{Group: groupName, Version: value.Version}
if _, ok := rdm.versionPriorities[gv]; !ok {
rdm.versionPriorities[gv] = priorityInfo{
gvKey := groupVersionKey{
GroupVersion: gv,
source: source,
}
if _, ok := rdm.versionPriorities[gvKey]; !ok {
rdm.versionPriorities[gvKey] = priorityInfo{
GroupPriorityMinimum: 1000,
VersionPriority: 15,
}
@@ -197,10 +290,16 @@ func (rdm *resourceDiscoveryManager) addGroupVersionLocked(groupName string, val
rdm.cache.Store(nil)
}
func (rdm *resourceDiscoveryManager) RemoveGroupVersion(apiGroup metav1.GroupVersion) {
func (rdm *resourceDiscoveryManager) RemoveGroupVersion(source Source, apiGroup metav1.GroupVersion) {
rdm.lock.Lock()
defer rdm.lock.Unlock()
group, exists := rdm.apiGroups[apiGroup.Group]
key := groupKey{
source: source,
name: apiGroup.Group,
}
group, exists := rdm.apiGroups[key]
if !exists {
return
}
@@ -218,23 +317,33 @@ func (rdm *resourceDiscoveryManager) RemoveGroupVersion(apiGroup metav1.GroupVer
return
}
delete(rdm.versionPriorities, apiGroup)
gvKey := groupVersionKey{
GroupVersion: apiGroup,
source: source,
}
delete(rdm.versionPriorities, gvKey)
if len(group.Versions) == 0 {
delete(rdm.apiGroups, group.Name)
delete(rdm.apiGroups, key)
}
// Reset response document so it is recreated lazily
rdm.cache.Store(nil)
}
func (rdm *resourceDiscoveryManager) RemoveGroup(groupName string) {
func (rdm *resourceDiscoveryManager) RemoveGroup(source Source, groupName string) {
rdm.lock.Lock()
defer rdm.lock.Unlock()
delete(rdm.apiGroups, groupName)
key := groupKey{
source: source,
name: groupName,
}
delete(rdm.apiGroups, key)
for k := range rdm.versionPriorities {
if k.Group == groupName {
if k.Group == groupName && k.source == source {
delete(rdm.versionPriorities, k)
}
}
@@ -246,19 +355,66 @@ func (rdm *resourceDiscoveryManager) RemoveGroup(groupName string) {
// Prepares the api group list for serving by converting them from map into
// list and sorting them according to insertion order
func (rdm *resourceDiscoveryManager) calculateAPIGroupsLocked() []apidiscoveryv2beta1.APIGroupDiscovery {
regenerationCounter.Inc()
// Re-order the apiGroups by their priority.
groups := []apidiscoveryv2beta1.APIGroupDiscovery{}
for _, group := range rdm.apiGroups {
copied := *group.DeepCopy()
groupsToUse := map[string]apidiscoveryv2beta1.APIGroupDiscovery{}
sourcesUsed := map[metav1.GroupVersion]Source{}
for key, group := range rdm.apiGroups {
if existing, ok := groupsToUse[key.name]; ok {
for _, v := range group.Versions {
gv := metav1.GroupVersion{Group: key.name, Version: v.Version}
// Skip groupversions we've already seen before. Only DefaultSource
// takes precedence
if usedSource, seen := sourcesUsed[gv]; seen && key.source >= usedSource {
continue
} else if seen {
// Find the index of the duplicate version and replace
for i := 0; i < len(existing.Versions); i++ {
if existing.Versions[i].Version == v.Version {
existing.Versions[i] = v
break
}
}
} else {
// New group-version, just append
existing.Versions = append(existing.Versions, v)
}
sourcesUsed[gv] = key.source
groupsToUse[key.name] = existing
}
// Check to see if we have overlapping versions. If we do, take the one
// with highest source precedence
} else {
groupsToUse[key.name] = *group.DeepCopy()
for _, v := range group.Versions {
gv := metav1.GroupVersion{Group: key.name, Version: v.Version}
sourcesUsed[gv] = key.source
}
}
}
for _, group := range groupsToUse {
// Re-order versions based on their priority. Use kube-aware string
// comparison as a tie breaker
sort.SliceStable(copied.Versions, func(i, j int) bool {
iVersion := copied.Versions[i].Version
jVersion := copied.Versions[j].Version
sort.SliceStable(group.Versions, func(i, j int) bool {
iVersion := group.Versions[i].Version
jVersion := group.Versions[j].Version
iPriority := rdm.versionPriorities[metav1.GroupVersion{Group: group.Name, Version: iVersion}].VersionPriority
jPriority := rdm.versionPriorities[metav1.GroupVersion{Group: group.Name, Version: jVersion}].VersionPriority
iGV := metav1.GroupVersion{Group: group.Name, Version: iVersion}
jGV := metav1.GroupVersion{Group: group.Name, Version: jVersion}
iSource := sourcesUsed[iGV]
jSource := sourcesUsed[jGV]
iPriority := rdm.versionPriorities[groupVersionKey{iGV, iSource}].VersionPriority
jPriority := rdm.versionPriorities[groupVersionKey{jGV, jSource}].VersionPriority
// Sort by version string comparator if priority is equal
if iPriority == jPriority {
@@ -269,13 +425,16 @@ func (rdm *resourceDiscoveryManager) calculateAPIGroupsLocked() []apidiscoveryv2
return iPriority > jPriority
})
groups = append(groups, *copied.DeepCopy())
groups = append(groups, group)
}
// For each group, determine the highest minimum group priority and use that
priorities := map[string]int{}
for gv, info := range rdm.versionPriorities {
if source := sourcesUsed[gv.GroupVersion]; source != gv.source {
continue
}
if existing, exists := priorities[gv.Group]; exists {
if existing < info.GroupPriorityMinimum {
priorities[gv.Group] = info.GroupPriorityMinimum
@@ -338,6 +497,10 @@ type cachedGroupList struct {
}
func (rdm *resourceDiscoveryManager) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
rdm.serveHTTPFunc(resp, req)
}
func (rdm *resourceDiscoveryManager) serveHTTP(resp http.ResponseWriter, req *http.Request) {
cache := rdm.fetchFromCache()
response := cache.cachedResponse
etag := cache.cachedResponseETag