k8s.io/apiserver@v0.31.1/pkg/endpoints/discovery/root.go (about)

     1  /*
     2  Copyright 2017 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 discovery
    18  
    19  import (
    20  	"net/http"
    21  	"sync"
    22  
    23  	restful "github.com/emicklei/go-restful/v3"
    24  
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/apimachinery/pkg/runtime/schema"
    28  	utilnet "k8s.io/apimachinery/pkg/util/net"
    29  	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
    30  	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
    31  )
    32  
    33  // GroupManager is an interface that allows dynamic mutation of the existing webservice to handle
    34  // API groups being added or removed.
    35  type GroupManager interface {
    36  	AddGroup(apiGroup metav1.APIGroup)
    37  	RemoveGroup(groupName string)
    38  	ServeHTTP(resp http.ResponseWriter, req *http.Request)
    39  	WebService() *restful.WebService
    40  }
    41  
    42  // rootAPIsHandler creates a webservice serving api group discovery.
    43  // The list of APIGroups may change while the server is running because additional resources
    44  // are registered or removed.  It is not safe to cache the values.
    45  type rootAPIsHandler struct {
    46  	// addresses is used to build cluster IPs for discovery.
    47  	addresses Addresses
    48  
    49  	serializer runtime.NegotiatedSerializer
    50  
    51  	// Map storing information about all groups to be exposed in discovery response.
    52  	// The map is from name to the group.
    53  	lock      sync.RWMutex
    54  	apiGroups map[string]metav1.APIGroup
    55  	// apiGroupNames preserves insertion order
    56  	apiGroupNames []string
    57  }
    58  
    59  func NewRootAPIsHandler(addresses Addresses, serializer runtime.NegotiatedSerializer) *rootAPIsHandler {
    60  	// Because in release 1.1, /apis returns response with empty APIVersion, we
    61  	// use stripVersionNegotiatedSerializer to keep the response backwards
    62  	// compatible.
    63  	serializer = stripVersionNegotiatedSerializer{serializer}
    64  
    65  	return &rootAPIsHandler{
    66  		addresses:  addresses,
    67  		serializer: serializer,
    68  		apiGroups:  map[string]metav1.APIGroup{},
    69  	}
    70  }
    71  
    72  func (s *rootAPIsHandler) AddGroup(apiGroup metav1.APIGroup) {
    73  	s.lock.Lock()
    74  	defer s.lock.Unlock()
    75  
    76  	_, alreadyExists := s.apiGroups[apiGroup.Name]
    77  
    78  	s.apiGroups[apiGroup.Name] = apiGroup
    79  	if !alreadyExists {
    80  		s.apiGroupNames = append(s.apiGroupNames, apiGroup.Name)
    81  	}
    82  }
    83  
    84  func (s *rootAPIsHandler) RemoveGroup(groupName string) {
    85  	s.lock.Lock()
    86  	defer s.lock.Unlock()
    87  
    88  	delete(s.apiGroups, groupName)
    89  	for i := range s.apiGroupNames {
    90  		if s.apiGroupNames[i] == groupName {
    91  			s.apiGroupNames = append(s.apiGroupNames[:i], s.apiGroupNames[i+1:]...)
    92  			break
    93  		}
    94  	}
    95  }
    96  
    97  func (s *rootAPIsHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
    98  	s.lock.RLock()
    99  	defer s.lock.RUnlock()
   100  
   101  	orderedGroups := []metav1.APIGroup{}
   102  	for _, groupName := range s.apiGroupNames {
   103  		orderedGroups = append(orderedGroups, s.apiGroups[groupName])
   104  	}
   105  
   106  	clientIP := utilnet.GetClientIP(req)
   107  	serverCIDR := s.addresses.ServerAddressByClientCIDRs(clientIP)
   108  	groups := make([]metav1.APIGroup, len(orderedGroups))
   109  	for i := range orderedGroups {
   110  		groups[i] = orderedGroups[i]
   111  		groups[i].ServerAddressByClientCIDRs = serverCIDR
   112  	}
   113  
   114  	responsewriters.WriteObjectNegotiated(s.serializer, negotiation.DefaultEndpointRestrictions, schema.GroupVersion{}, resp, req, http.StatusOK, &metav1.APIGroupList{Groups: groups}, false)
   115  }
   116  
   117  func (s *rootAPIsHandler) restfulHandle(req *restful.Request, resp *restful.Response) {
   118  	s.ServeHTTP(resp.ResponseWriter, req.Request)
   119  }
   120  
   121  // WebService returns a webservice serving api group discovery.
   122  // Note: during the server runtime apiGroups might change.
   123  func (s *rootAPIsHandler) WebService() *restful.WebService {
   124  	mediaTypes, _ := negotiation.MediaTypesForSerializer(s.serializer)
   125  	ws := new(restful.WebService)
   126  	ws.Path(APIGroupPrefix)
   127  	ws.Doc("get available API versions")
   128  	ws.Route(ws.GET("/").To(s.restfulHandle).
   129  		Doc("get available API versions").
   130  		Operation("getAPIVersions").
   131  		Produces(mediaTypes...).
   132  		Consumes(mediaTypes...).
   133  		Writes(metav1.APIGroupList{}))
   134  	return ws
   135  }