1/* 2Copyright 2016 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 helper 18 19import ( 20 "sort" 21 "strings" 22 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/runtime/schema" 25 "k8s.io/apimachinery/pkg/version" 26 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" 27) 28 29// SortedByGroupAndVersion sorts APIServices into their different groups, and then sorts them based on their versions. 30// For example, the first element of the first array contains the APIService with the highest version number, in the 31// group with the highest priority; while the last element of the last array contains the APIService with the lowest 32// version number, in the group with the lowest priority. 33func SortedByGroupAndVersion(servers []*v1.APIService) [][]*v1.APIService { 34 serversByGroupPriorityMinimum := ByGroupPriorityMinimum(servers) 35 sort.Sort(serversByGroupPriorityMinimum) 36 37 ret := [][]*v1.APIService{} 38 for _, curr := range serversByGroupPriorityMinimum { 39 // check to see if we already have an entry for this group 40 existingIndex := -1 41 for j, groupInReturn := range ret { 42 if groupInReturn[0].Spec.Group == curr.Spec.Group { 43 existingIndex = j 44 break 45 } 46 } 47 48 if existingIndex >= 0 { 49 ret[existingIndex] = append(ret[existingIndex], curr) 50 sort.Sort(ByVersionPriority(ret[existingIndex])) 51 continue 52 } 53 54 ret = append(ret, []*v1.APIService{curr}) 55 } 56 57 return ret 58} 59 60// ByGroupPriorityMinimum sorts with the highest group number first, then by name. 61// This is not a simple reverse, because we want the name sorting to be alpha, not 62// reverse alpha. 63type ByGroupPriorityMinimum []*v1.APIService 64 65func (s ByGroupPriorityMinimum) Len() int { return len(s) } 66func (s ByGroupPriorityMinimum) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 67func (s ByGroupPriorityMinimum) Less(i, j int) bool { 68 if s[i].Spec.GroupPriorityMinimum != s[j].Spec.GroupPriorityMinimum { 69 return s[i].Spec.GroupPriorityMinimum > s[j].Spec.GroupPriorityMinimum 70 } 71 return s[i].Name < s[j].Name 72} 73 74// ByVersionPriority sorts with the highest version number first, then by name. 75// This is not a simple reverse, because we want the name sorting to be alpha, not 76// reverse alpha. 77type ByVersionPriority []*v1.APIService 78 79func (s ByVersionPriority) Len() int { return len(s) } 80func (s ByVersionPriority) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 81func (s ByVersionPriority) Less(i, j int) bool { 82 if s[i].Spec.VersionPriority != s[j].Spec.VersionPriority { 83 return s[i].Spec.VersionPriority > s[j].Spec.VersionPriority 84 } 85 return version.CompareKubeAwareVersionStrings(s[i].Spec.Version, s[j].Spec.Version) > 0 86} 87 88// APIServiceNameToGroupVersion returns the GroupVersion for a given apiServiceName. The name 89// must be valid, but any object you get back from an informer will be valid. 90func APIServiceNameToGroupVersion(apiServiceName string) schema.GroupVersion { 91 tokens := strings.SplitN(apiServiceName, ".", 2) 92 return schema.GroupVersion{Group: tokens[1], Version: tokens[0]} 93} 94 95// NewLocalAvailableAPIServiceCondition returns a condition for an available local APIService. 96func NewLocalAvailableAPIServiceCondition() v1.APIServiceCondition { 97 return v1.APIServiceCondition{ 98 Type: v1.Available, 99 Status: v1.ConditionTrue, 100 LastTransitionTime: metav1.Now(), 101 Reason: "Local", 102 Message: "Local APIServices are always available", 103 } 104} 105 106// SetAPIServiceCondition sets the status condition. It either overwrites the existing one or 107// creates a new one 108func SetAPIServiceCondition(apiService *v1.APIService, newCondition v1.APIServiceCondition) { 109 existingCondition := GetAPIServiceConditionByType(apiService, newCondition.Type) 110 if existingCondition == nil { 111 apiService.Status.Conditions = append(apiService.Status.Conditions, newCondition) 112 return 113 } 114 115 if existingCondition.Status != newCondition.Status { 116 existingCondition.Status = newCondition.Status 117 existingCondition.LastTransitionTime = newCondition.LastTransitionTime 118 } 119 120 existingCondition.Reason = newCondition.Reason 121 existingCondition.Message = newCondition.Message 122} 123 124// IsAPIServiceConditionTrue indicates if the condition is present and strictly true 125func IsAPIServiceConditionTrue(apiService *v1.APIService, conditionType v1.APIServiceConditionType) bool { 126 condition := GetAPIServiceConditionByType(apiService, conditionType) 127 return condition != nil && condition.Status == v1.ConditionTrue 128} 129 130// GetAPIServiceConditionByType gets an *APIServiceCondition by APIServiceConditionType if present 131func GetAPIServiceConditionByType(apiService *v1.APIService, conditionType v1.APIServiceConditionType) *v1.APIServiceCondition { 132 for i := range apiService.Status.Conditions { 133 if apiService.Status.Conditions[i].Type == conditionType { 134 return &apiService.Status.Conditions[i] 135 } 136 } 137 return nil 138} 139