trigger_time_tracker.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package endpoint
  14. import (
  15. "sync"
  16. "time"
  17. v1 "k8s.io/api/core/v1"
  18. podutil "k8s.io/kubernetes/pkg/api/v1/pod"
  19. )
  20. // TriggerTimeTracker is used to compute an EndpointsLastChangeTriggerTime
  21. // annotation. See the documentation for that annotation for more details.
  22. //
  23. // Please note that this util may compute a wrong EndpointsLastChangeTriggerTime
  24. // if the same object changes multiple times between two consecutive syncs.
  25. // We're aware of this limitation but we decided to accept it, as fixing it
  26. // would require a major rewrite of the endpoint(Slice) controller and
  27. // Informer framework. Such situations, i.e. frequent updates of the same object
  28. // in a single sync period, should be relatively rare and therefore this util
  29. // should provide a good approximation of the EndpointsLastChangeTriggerTime.
  30. type TriggerTimeTracker struct {
  31. // ServiceStates is a map, indexed by Service object key, storing the last
  32. // known Service object state observed during the most recent call of the
  33. // ComputeEndpointLastChangeTriggerTime function.
  34. ServiceStates map[ServiceKey]ServiceState
  35. // mutex guarding the serviceStates map.
  36. mutex sync.Mutex
  37. }
  38. // NewTriggerTimeTracker creates a new instance of the TriggerTimeTracker.
  39. func NewTriggerTimeTracker() *TriggerTimeTracker {
  40. return &TriggerTimeTracker{
  41. ServiceStates: make(map[ServiceKey]ServiceState),
  42. }
  43. }
  44. // ServiceKey is a key uniquely identifying a Service.
  45. type ServiceKey struct {
  46. // namespace, name composing a namespaced name - an unique identifier of every Service.
  47. Namespace, Name string
  48. }
  49. // ServiceState represents a state of an Service object that is known to this util.
  50. type ServiceState struct {
  51. // lastServiceTriggerTime is a service trigger time observed most recently.
  52. lastServiceTriggerTime time.Time
  53. // lastPodTriggerTimes is a map (Pod name -> time) storing the pod trigger
  54. // times that were observed during the most recent call of the
  55. // ComputeEndpointLastChangeTriggerTime function.
  56. lastPodTriggerTimes map[string]time.Time
  57. }
  58. // ComputeEndpointLastChangeTriggerTime updates the state of the Service/Endpoint
  59. // object being synced and returns the time that should be exported as the
  60. // EndpointsLastChangeTriggerTime annotation.
  61. //
  62. // If the method returns a 'zero' time the EndpointsLastChangeTriggerTime
  63. // annotation shouldn't be exported.
  64. //
  65. // Please note that this function may compute a wrong value if the same object
  66. // (pod/service) changes multiple times between two consecutive syncs.
  67. //
  68. // Important: This method is go-routing safe but only when called for different
  69. // keys. The method shouldn't be called concurrently for the same key! This
  70. // contract is fulfilled in the current implementation of the endpoint(slice)
  71. // controller.
  72. func (t *TriggerTimeTracker) ComputeEndpointLastChangeTriggerTime(
  73. namespace string, service *v1.Service, pods []*v1.Pod) time.Time {
  74. key := ServiceKey{Namespace: namespace, Name: service.Name}
  75. // As there won't be any concurrent calls for the same key, we need to guard
  76. // access only to the serviceStates map.
  77. t.mutex.Lock()
  78. state, wasKnown := t.ServiceStates[key]
  79. t.mutex.Unlock()
  80. // Update the state before returning.
  81. defer func() {
  82. t.mutex.Lock()
  83. t.ServiceStates[key] = state
  84. t.mutex.Unlock()
  85. }()
  86. // minChangedTriggerTime is the min trigger time of all trigger times that
  87. // have changed since the last sync.
  88. var minChangedTriggerTime time.Time
  89. podTriggerTimes := make(map[string]time.Time)
  90. for _, pod := range pods {
  91. if podTriggerTime := getPodTriggerTime(pod); !podTriggerTime.IsZero() {
  92. podTriggerTimes[pod.Name] = podTriggerTime
  93. if podTriggerTime.After(state.lastPodTriggerTimes[pod.Name]) {
  94. // Pod trigger time has changed since the last sync, update minChangedTriggerTime.
  95. minChangedTriggerTime = min(minChangedTriggerTime, podTriggerTime)
  96. }
  97. }
  98. }
  99. serviceTriggerTime := getServiceTriggerTime(service)
  100. if serviceTriggerTime.After(state.lastServiceTriggerTime) {
  101. // Service trigger time has changed since the last sync, update minChangedTriggerTime.
  102. minChangedTriggerTime = min(minChangedTriggerTime, serviceTriggerTime)
  103. }
  104. state.lastPodTriggerTimes = podTriggerTimes
  105. state.lastServiceTriggerTime = serviceTriggerTime
  106. if !wasKnown {
  107. // New Service, use Service creationTimestamp.
  108. return service.CreationTimestamp.Time
  109. }
  110. // Regular update of endpoint objects, return min of changed trigger times.
  111. return minChangedTriggerTime
  112. }
  113. // DeleteService deletes service state stored in this util.
  114. func (t *TriggerTimeTracker) DeleteService(namespace, name string) {
  115. key := ServiceKey{Namespace: namespace, Name: name}
  116. t.mutex.Lock()
  117. defer t.mutex.Unlock()
  118. delete(t.ServiceStates, key)
  119. }
  120. // getPodTriggerTime returns the time of the pod change (trigger) that resulted
  121. // or will result in the endpoint object change.
  122. func getPodTriggerTime(pod *v1.Pod) (triggerTime time.Time) {
  123. if readyCondition := podutil.GetPodReadyCondition(pod.Status); readyCondition != nil {
  124. triggerTime = readyCondition.LastTransitionTime.Time
  125. }
  126. return triggerTime
  127. }
  128. // getServiceTriggerTime returns the time of the service change (trigger) that
  129. // resulted or will result in the endpoint change.
  130. func getServiceTriggerTime(service *v1.Service) (triggerTime time.Time) {
  131. return service.CreationTimestamp.Time
  132. }
  133. // min returns minimum of the currentMin and newValue or newValue if the currentMin is not set.
  134. func min(currentMin, newValue time.Time) time.Time {
  135. if currentMin.IsZero() || newValue.Before(currentMin) {
  136. return newValue
  137. }
  138. return currentMin
  139. }