blob: 7ed64a9f57963ace440c91eb518e2dd5b979b831 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2017 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package discovery
18
19import (
20 "net/http"
21 "sync"
22
23 restful "github.com/emicklei/go-restful"
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.
35type GroupManager interface {
36 AddGroup(apiGroup metav1.APIGroup)
37 RemoveGroup(groupName string)
38
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.
45type 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
59func 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
72func (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
84func (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
97func (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, schema.GroupVersion{}, resp, req, http.StatusOK, &metav1.APIGroupList{Groups: groups})
115}
116
117func (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.
123func (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}