Merge pull request #35840 from caesarxuchao/hide-new-versions
Automatic merge from submit-queue Hide groups with new versions from old kubectl Fix https://github.com/kubernetes/kubernetes/issues/35791 **What caused the bug?** In 1.5, we are going to graduate Policy and Apps to beta. Old version kubectl doesn't has the new versions built-in, its TRP dynamic discover thinks Policy/v1beta1 is a TPR, and tried to register it in kubectl's scheme. The registration failed because Policy group already exist, because kubectl had registered Policy.v1alpha1. **How does this PR fix the bug?** This PR let the API server hides Policy and Apps from old version kubectl, so TPR discovery won't see them. Old version kubectl doesn't know about Policy/v1beta1 or Apps/v1beta1, and v1alpha1 will be removed, so old version kubectl won't work for Policy or Apps anyway, so this PR does not cause any function loss. @kubernetes/sig-api-machinery @liggitt @smarterclayton @deads2k @janetkuo @mwielgus
This commit is contained in:
		@@ -50,9 +50,11 @@ go_library(
 | 
				
			|||||||
        "//pkg/util/net:go_default_library",
 | 
					        "//pkg/util/net:go_default_library",
 | 
				
			||||||
        "//pkg/util/proxy:go_default_library",
 | 
					        "//pkg/util/proxy:go_default_library",
 | 
				
			||||||
        "//pkg/util/runtime:go_default_library",
 | 
					        "//pkg/util/runtime:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/util/sets:go_default_library",
 | 
				
			||||||
        "//pkg/util/strategicpatch:go_default_library",
 | 
					        "//pkg/util/strategicpatch:go_default_library",
 | 
				
			||||||
        "//pkg/util/strings:go_default_library",
 | 
					        "//pkg/util/strings:go_default_library",
 | 
				
			||||||
        "//pkg/util/wsstream:go_default_library",
 | 
					        "//pkg/util/wsstream:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/version:go_default_library",
 | 
				
			||||||
        "//pkg/watch:go_default_library",
 | 
					        "//pkg/watch:go_default_library",
 | 
				
			||||||
        "//pkg/watch/versioned:go_default_library",
 | 
					        "//pkg/watch/versioned:go_default_library",
 | 
				
			||||||
        "//vendor:bitbucket.org/ww/goautoneg",
 | 
					        "//vendor:bitbucket.org/ww/goautoneg",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ import (
 | 
				
			|||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	rt "runtime"
 | 
						rt "runtime"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/admission"
 | 
						"k8s.io/kubernetes/pkg/admission"
 | 
				
			||||||
@@ -40,7 +41,9 @@ import (
 | 
				
			|||||||
	utilerrors "k8s.io/kubernetes/pkg/util/errors"
 | 
						utilerrors "k8s.io/kubernetes/pkg/util/errors"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/flushwriter"
 | 
						"k8s.io/kubernetes/pkg/util/flushwriter"
 | 
				
			||||||
	utilruntime "k8s.io/kubernetes/pkg/util/runtime"
 | 
						utilruntime "k8s.io/kubernetes/pkg/util/runtime"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/sets"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/wsstream"
 | 
						"k8s.io/kubernetes/pkg/util/wsstream"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/version"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/emicklei/go-restful"
 | 
						"github.com/emicklei/go-restful"
 | 
				
			||||||
	"github.com/golang/glog"
 | 
						"github.com/golang/glog"
 | 
				
			||||||
@@ -343,10 +346,48 @@ func APIVersionHandler(s runtime.NegotiatedSerializer, getAPIVersionsFunc func(r
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Remove in 1.6. Returns if kubectl is older than v1.5.0
 | 
				
			||||||
 | 
					func isOldKubectl(userAgent string) bool {
 | 
				
			||||||
 | 
						// example userAgent string: kubectl-1.3/v1.3.8 (linux/amd64) kubernetes/e328d5b
 | 
				
			||||||
 | 
						if !strings.Contains(userAgent, "kubectl") {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						userAgent = strings.Split(userAgent, " ")[0]
 | 
				
			||||||
 | 
						subs := strings.Split(userAgent, "/")
 | 
				
			||||||
 | 
						if len(subs) != 2 {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						kubectlVersion, versionErr := version.Parse(subs[1])
 | 
				
			||||||
 | 
						if versionErr != nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return kubectlVersion.LT(version.MustParse("v1.5.0"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Remove in 1.6. This is for backward compatibility with 1.4 kubectl.
 | 
				
			||||||
 | 
					// See https://github.com/kubernetes/kubernetes/issues/35791
 | 
				
			||||||
 | 
					var groupsWithNewVersionsIn1_5 = sets.NewString("apps", "policy")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Remove in 1.6.
 | 
				
			||||||
 | 
					func filterAPIGroups(req *restful.Request, groups []unversioned.APIGroup) []unversioned.APIGroup {
 | 
				
			||||||
 | 
						if !isOldKubectl(req.HeaderParameter("User-Agent")) {
 | 
				
			||||||
 | 
							return groups
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// hide API group that has new versions added in 1.5.
 | 
				
			||||||
 | 
						var ret []unversioned.APIGroup
 | 
				
			||||||
 | 
						for _, group := range groups {
 | 
				
			||||||
 | 
							if groupsWithNewVersionsIn1_5.Has(group.Name) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ret = append(ret, group)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RootAPIHandler returns a handler which will list the provided groups and versions as available.
 | 
					// RootAPIHandler returns a handler which will list the provided groups and versions as available.
 | 
				
			||||||
func RootAPIHandler(s runtime.NegotiatedSerializer, f func(req *restful.Request) []unversioned.APIGroup) restful.RouteFunction {
 | 
					func RootAPIHandler(s runtime.NegotiatedSerializer, f func(req *restful.Request) []unversioned.APIGroup) restful.RouteFunction {
 | 
				
			||||||
	return func(req *restful.Request, resp *restful.Response) {
 | 
						return func(req *restful.Request, resp *restful.Response) {
 | 
				
			||||||
		writeNegotiated(s, unversioned.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, &unversioned.APIGroupList{Groups: f(req)})
 | 
							writeNegotiated(s, unversioned.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, &unversioned.APIGroupList{Groups: filterAPIGroups(req, f(req))})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user