k8s.io/apiserver@v0.31.1/pkg/endpoints/filters/storageversion.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package filters 18 19 import ( 20 "errors" 21 "fmt" 22 "net/http" 23 24 apierrors "k8s.io/apimachinery/pkg/api/errors" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apimachinery/pkg/runtime/schema" 28 "k8s.io/apiserver/pkg/authentication/user" 29 "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" 30 "k8s.io/apiserver/pkg/endpoints/request" 31 "k8s.io/apiserver/pkg/storageversion" 32 _ "k8s.io/component-base/metrics/prometheus/workqueue" // for workqueue metric registration 33 "k8s.io/klog/v2" 34 ) 35 36 // WithStorageVersionPrecondition checks if the storage version barrier has 37 // completed, if not, it only passes the following API requests: 38 // 1. non-resource requests, 39 // 2. read requests, 40 // 3. write requests to the storageversion API, 41 // 4. create requests to the namespace API sent by apiserver itself, 42 // 5. write requests to the lease API in kube-system namespace, 43 // 6. resources whose StorageVersion is not pending update, including non-persisted resources. 44 func WithStorageVersionPrecondition(handler http.Handler, svm storageversion.Manager, s runtime.NegotiatedSerializer) http.Handler { 45 if svm == nil { 46 // TODO(roycaihw): switch to warning after the feature graduate to beta/GA 47 klog.V(2).Infof("Storage Version barrier is disabled") 48 return handler 49 } 50 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 51 if svm.Completed() { 52 handler.ServeHTTP(w, req) 53 return 54 } 55 ctx := req.Context() 56 requestInfo, found := request.RequestInfoFrom(ctx) 57 if !found { 58 responsewriters.InternalError(w, req, errors.New("no RequestInfo found in the context")) 59 return 60 } 61 // Allow non-resource requests 62 if !requestInfo.IsResourceRequest { 63 handler.ServeHTTP(w, req) 64 return 65 } 66 // Allow read requests 67 if requestInfo.Verb == "get" || requestInfo.Verb == "list" || requestInfo.Verb == "watch" { 68 handler.ServeHTTP(w, req) 69 return 70 } 71 // Allow writes to the storage version API 72 if requestInfo.APIGroup == "internal.apiserver.k8s.io" && requestInfo.Resource == "storageversions" { 73 handler.ServeHTTP(w, req) 74 return 75 } 76 // The system namespace is required for apiserver-identity lease to exist. Allow the apiserver 77 // itself to create namespaces. 78 // NOTE: with this exception, if the bootstrap client writes namespaces with a new version, 79 // and the upgraded apiserver dies before updating the StorageVersion for namespaces, the 80 // storage migrator won't be able to tell these namespaces are stored in a different version in etcd. 81 // Because the bootstrap client only creates system namespace and doesn't update them, this can 82 // only happen if the upgraded apiserver is the first apiserver that kicks off namespace creation, 83 // or if an upgraded server that joins an existing cluster has new system namespaces (other 84 // than kube-system, kube-public, kube-node-lease) that need to be created. 85 u, hasUser := request.UserFrom(ctx) 86 if requestInfo.APIGroup == "" && requestInfo.Resource == "namespaces" && 87 requestInfo.Verb == "create" && hasUser && 88 u.GetName() == user.APIServerUser && contains(u.GetGroups(), user.SystemPrivilegedGroup) { 89 handler.ServeHTTP(w, req) 90 return 91 } 92 // Allow writes to the lease API in kube-system. The storage version API depends on the 93 // apiserver-identity leases to operate. Leases in kube-system are either apiserver-identity 94 // lease (which gets garbage collected when stale) or leader-election leases (which gets 95 // periodically updated by system components). Both types of leases won't be stale in etcd. 96 if requestInfo.APIGroup == "coordination.k8s.io" && requestInfo.Resource == "leases" && 97 requestInfo.Namespace == metav1.NamespaceSystem { 98 handler.ServeHTTP(w, req) 99 return 100 } 101 // If the resource's StorageVersion is not in the to-be-updated list, let it pass. 102 // Non-persisted resources are not in the to-be-updated list, so they will pass. 103 gr := schema.GroupResource{Group: requestInfo.APIGroup, Resource: requestInfo.Resource} 104 if !svm.PendingUpdate(gr) { 105 handler.ServeHTTP(w, req) 106 return 107 } 108 109 gv := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} 110 responsewriters.ErrorNegotiated(apierrors.NewServiceUnavailable(fmt.Sprintf("wait for storage version registration to complete for resource: %v, last seen error: %v", gr, svm.LastUpdateError(gr))), s, gv, w, req) 111 }) 112 } 113 114 func contains(s []string, e string) bool { 115 for _, a := range s { 116 if a == e { 117 return true 118 } 119 } 120 return false 121 }