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  }