// convertToDiscoveryAPIGroup takes apiservices in a single group and returns a discovery compatible object. // if none of the services are available, it will return nil. func convertToDiscoveryAPIGroup(apiServices []*apiregistrationapi.APIService, serviceLister v1listers.ServiceLister, endpointsLister v1listers.EndpointsLister) *metav1.APIGroup { apiServicesByGroup := apiregistrationapi.SortedByGroup(apiServices)[0] var discoveryGroup *metav1.APIGroup for _, apiService := range apiServicesByGroup { // skip any API services without actual services if _, err := serviceLister.Services(apiService.Spec.Service.Namespace).Get(apiService.Spec.Service.Name); err != nil { continue } hasActiveEndpoints := false endpoints, err := endpointsLister.Endpoints(apiService.Spec.Service.Namespace).Get(apiService.Spec.Service.Name) // skip any API services without endpoints if err != nil { continue } for _, subset := range endpoints.Subsets { if len(subset.Addresses) > 0 { hasActiveEndpoints = true break } } if !hasActiveEndpoints { continue } // the first APIService which is valid becomes the default if discoveryGroup == nil { discoveryGroup = &metav1.APIGroup{ Name: apiService.Spec.Group, PreferredVersion: metav1.GroupVersionForDiscovery{ GroupVersion: apiService.Spec.Group + "/" + apiService.Spec.Version, Version: apiService.Spec.Version, }, } } discoveryGroup.Versions = append(discoveryGroup.Versions, metav1.GroupVersionForDiscovery{ GroupVersion: apiService.Spec.Group + "/" + apiService.Spec.Version, Version: apiService.Spec.Version, }, ) } return discoveryGroup }
func (r *apisHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // if the URL is for OUR api group, serve it normally if strings.HasPrefix(req.URL.Path+"/", "/apis/"+apiregistrationapi.GroupName+"/") { r.delegate.ServeHTTP(w, req) return } // don't handle URLs that aren't /apis if req.URL.Path != "/apis" && req.URL.Path != "/apis/" { r.delegate.ServeHTTP(w, req) return } discoveryGroupList := &metav1.APIGroupList{ // always add OUR api group to the list first. Since we'll never have a registered APIService for it // and since this is the crux of the API, having this first will give our names priority. It's good to be king. Groups: []metav1.APIGroup{discoveryGroup}, } apiServices, err := r.lister.List(labels.Everything()) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } apiServicesByGroup := apiregistrationapi.SortedByGroup(apiServices) for _, apiGroupServers := range apiServicesByGroup { // skip the legacy group if len(apiGroupServers[0].Spec.Group) == 0 { continue } discoveryGroup := convertToDiscoveryAPIGroup(apiGroupServers, r.serviceLister, r.endpointsLister) if discoveryGroup != nil { discoveryGroupList.Groups = append(discoveryGroupList.Groups, *discoveryGroup) } } json, err := runtime.Encode(api.Codecs.LegacyCodec(), discoveryGroupList) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if _, err := w.Write(json); err != nil { panic(err) } }