controller_history.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. /*
  2. Copyright 2017 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 history
  14. import (
  15. "bytes"
  16. "fmt"
  17. "hash/fnv"
  18. "sort"
  19. "strconv"
  20. apps "k8s.io/api/apps/v1"
  21. appsinformers "k8s.io/client-go/informers/apps/v1"
  22. clientset "k8s.io/client-go/kubernetes"
  23. appslisters "k8s.io/client-go/listers/apps/v1"
  24. hashutil "k8s.io/kubernetes/pkg/util/hash"
  25. apiequality "k8s.io/apimachinery/pkg/api/equality"
  26. "k8s.io/apimachinery/pkg/api/errors"
  27. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  28. "k8s.io/apimachinery/pkg/labels"
  29. "k8s.io/apimachinery/pkg/runtime"
  30. "k8s.io/apimachinery/pkg/types"
  31. "k8s.io/apimachinery/pkg/runtime/schema"
  32. "k8s.io/apimachinery/pkg/util/rand"
  33. "k8s.io/client-go/tools/cache"
  34. "k8s.io/client-go/util/retry"
  35. )
  36. // ControllerRevisionHashLabel is the label used to indicate the hash value of a ControllerRevision's Data.
  37. const ControllerRevisionHashLabel = "controller.kubernetes.io/hash"
  38. // ControllerRevisionName returns the Name for a ControllerRevision in the form prefix-hash. If the length
  39. // of prefix is greater than 223 bytes, it is truncated to allow for a name that is no larger than 253 bytes.
  40. func ControllerRevisionName(prefix string, hash string) string {
  41. if len(prefix) > 223 {
  42. prefix = prefix[:223]
  43. }
  44. return fmt.Sprintf("%s-%s", prefix, hash)
  45. }
  46. // NewControllerRevision returns a ControllerRevision with a ControllerRef pointing to parent and indicating that
  47. // parent is of parentKind. The ControllerRevision has labels matching template labels, contains Data equal to data, and
  48. // has a Revision equal to revision. The collisionCount is used when creating the name of the ControllerRevision
  49. // so the name is likely unique. If the returned error is nil, the returned ControllerRevision is valid. If the
  50. // returned error is not nil, the returned ControllerRevision is invalid for use.
  51. func NewControllerRevision(parent metav1.Object,
  52. parentKind schema.GroupVersionKind,
  53. templateLabels map[string]string,
  54. data runtime.RawExtension,
  55. revision int64,
  56. collisionCount *int32) (*apps.ControllerRevision, error) {
  57. labelMap := make(map[string]string)
  58. for k, v := range templateLabels {
  59. labelMap[k] = v
  60. }
  61. cr := &apps.ControllerRevision{
  62. ObjectMeta: metav1.ObjectMeta{
  63. Labels: labelMap,
  64. OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(parent, parentKind)},
  65. },
  66. Data: data,
  67. Revision: revision,
  68. }
  69. hash := HashControllerRevision(cr, collisionCount)
  70. cr.Name = ControllerRevisionName(parent.GetName(), hash)
  71. cr.Labels[ControllerRevisionHashLabel] = hash
  72. return cr, nil
  73. }
  74. // HashControllerRevision hashes the contents of revision's Data using FNV hashing. If probe is not nil, the byte value
  75. // of probe is added written to the hash as well. The returned hash will be a safe encoded string to avoid bad words.
  76. func HashControllerRevision(revision *apps.ControllerRevision, probe *int32) string {
  77. hf := fnv.New32()
  78. if len(revision.Data.Raw) > 0 {
  79. hf.Write(revision.Data.Raw)
  80. }
  81. if revision.Data.Object != nil {
  82. hashutil.DeepHashObject(hf, revision.Data.Object)
  83. }
  84. if probe != nil {
  85. hf.Write([]byte(strconv.FormatInt(int64(*probe), 10)))
  86. }
  87. return rand.SafeEncodeString(fmt.Sprint(hf.Sum32()))
  88. }
  89. // SortControllerRevisions sorts revisions by their Revision.
  90. func SortControllerRevisions(revisions []*apps.ControllerRevision) {
  91. sort.Stable(byRevision(revisions))
  92. }
  93. // EqualRevision returns true if lhs and rhs are either both nil, or both point to non-nil ControllerRevisions that
  94. // contain semantically equivalent data. Otherwise this method returns false.
  95. func EqualRevision(lhs *apps.ControllerRevision, rhs *apps.ControllerRevision) bool {
  96. var lhsHash, rhsHash *uint32
  97. if lhs == nil || rhs == nil {
  98. return lhs == rhs
  99. }
  100. if hs, found := lhs.Labels[ControllerRevisionHashLabel]; found {
  101. hash, err := strconv.ParseInt(hs, 10, 32)
  102. if err == nil {
  103. lhsHash = new(uint32)
  104. *lhsHash = uint32(hash)
  105. }
  106. }
  107. if hs, found := rhs.Labels[ControllerRevisionHashLabel]; found {
  108. hash, err := strconv.ParseInt(hs, 10, 32)
  109. if err == nil {
  110. rhsHash = new(uint32)
  111. *rhsHash = uint32(hash)
  112. }
  113. }
  114. if lhsHash != nil && rhsHash != nil && *lhsHash != *rhsHash {
  115. return false
  116. }
  117. return bytes.Equal(lhs.Data.Raw, rhs.Data.Raw) && apiequality.Semantic.DeepEqual(lhs.Data.Object, rhs.Data.Object)
  118. }
  119. // FindEqualRevisions returns all ControllerRevisions in revisions that are equal to needle using EqualRevision as the
  120. // equality test. The returned slice preserves the order of revisions.
  121. func FindEqualRevisions(revisions []*apps.ControllerRevision, needle *apps.ControllerRevision) []*apps.ControllerRevision {
  122. var eq []*apps.ControllerRevision
  123. for i := range revisions {
  124. if EqualRevision(revisions[i], needle) {
  125. eq = append(eq, revisions[i])
  126. }
  127. }
  128. return eq
  129. }
  130. // byRevision implements sort.Interface to allow ControllerRevisions to be sorted by Revision.
  131. type byRevision []*apps.ControllerRevision
  132. func (br byRevision) Len() int {
  133. return len(br)
  134. }
  135. // Less breaks ties first by creation timestamp, then by name
  136. func (br byRevision) Less(i, j int) bool {
  137. if br[i].Revision == br[j].Revision {
  138. if br[j].CreationTimestamp.Equal(&br[i].CreationTimestamp) {
  139. return br[i].Name < br[j].Name
  140. }
  141. return br[j].CreationTimestamp.After(br[i].CreationTimestamp.Time)
  142. }
  143. return br[i].Revision < br[j].Revision
  144. }
  145. func (br byRevision) Swap(i, j int) {
  146. br[i], br[j] = br[j], br[i]
  147. }
  148. // Interface provides an interface allowing for management of a Controller's history as realized by recorded
  149. // ControllerRevisions. An instance of Interface can be retrieved from NewHistory. Implementations must treat all
  150. // pointer parameters as "in" parameter, and they must not be mutated.
  151. type Interface interface {
  152. // ListControllerRevisions lists all ControllerRevisions matching selector and owned by parent or no other
  153. // controller. If the returned error is nil the returned slice of ControllerRevisions is valid. If the
  154. // returned error is not nil, the returned slice is not valid.
  155. ListControllerRevisions(parent metav1.Object, selector labels.Selector) ([]*apps.ControllerRevision, error)
  156. // CreateControllerRevision attempts to create the revision as owned by parent via a ControllerRef. If name
  157. // collision occurs, collisionCount (incremented each time collision occurs except for the first time) is
  158. // added to the hash of the revision and it is renamed using ControllerRevisionName. Implementations may
  159. // cease to attempt to retry creation after some number of attempts and return an error. If the returned
  160. // error is not nil, creation failed. If the returned error is nil, the returned ControllerRevision has been
  161. // created.
  162. // Callers must make sure that collisionCount is not nil. An error is returned if it is.
  163. CreateControllerRevision(parent metav1.Object, revision *apps.ControllerRevision, collisionCount *int32) (*apps.ControllerRevision, error)
  164. // DeleteControllerRevision attempts to delete revision. If the returned error is not nil, deletion has failed.
  165. DeleteControllerRevision(revision *apps.ControllerRevision) error
  166. // UpdateControllerRevision updates revision such that its Revision is equal to newRevision. Implementations
  167. // may retry on conflict. If the returned error is nil, the update was successful and returned ControllerRevision
  168. // is valid. If the returned error is not nil, the update failed and the returned ControllerRevision is invalid.
  169. UpdateControllerRevision(revision *apps.ControllerRevision, newRevision int64) (*apps.ControllerRevision, error)
  170. // AdoptControllerRevision attempts to adopt revision by adding a ControllerRef indicating that the parent
  171. // Object of parentKind is the owner of revision. If revision is already owned, an error is returned. If the
  172. // resource patch fails, an error is returned. If no error is returned, the returned ControllerRevision is
  173. // valid.
  174. AdoptControllerRevision(parent metav1.Object, parentKind schema.GroupVersionKind, revision *apps.ControllerRevision) (*apps.ControllerRevision, error)
  175. // ReleaseControllerRevision attempts to release parent's ownership of revision by removing parent from the
  176. // OwnerReferences of revision. If an error is returned, parent remains the owner of revision. If no error is
  177. // returned, the returned ControllerRevision is valid.
  178. ReleaseControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error)
  179. }
  180. // NewHistory returns an instance of Interface that uses client to communicate with the API Server and lister to list
  181. // ControllerRevisions. This method should be used to create an Interface for all scenarios other than testing.
  182. func NewHistory(client clientset.Interface, lister appslisters.ControllerRevisionLister) Interface {
  183. return &realHistory{client, lister}
  184. }
  185. // NewFakeHistory returns an instance of Interface that uses informer to create, update, list, and delete
  186. // ControllerRevisions. This method should be used to create an Interface for testing purposes.
  187. func NewFakeHistory(informer appsinformers.ControllerRevisionInformer) Interface {
  188. return &fakeHistory{informer.Informer().GetIndexer(), informer.Lister()}
  189. }
  190. type realHistory struct {
  191. client clientset.Interface
  192. lister appslisters.ControllerRevisionLister
  193. }
  194. func (rh *realHistory) ListControllerRevisions(parent metav1.Object, selector labels.Selector) ([]*apps.ControllerRevision, error) {
  195. // List all revisions in the namespace that match the selector
  196. history, err := rh.lister.ControllerRevisions(parent.GetNamespace()).List(selector)
  197. if err != nil {
  198. return nil, err
  199. }
  200. var owned []*apps.ControllerRevision
  201. for i := range history {
  202. ref := metav1.GetControllerOf(history[i])
  203. if ref == nil || ref.UID == parent.GetUID() {
  204. owned = append(owned, history[i])
  205. }
  206. }
  207. return owned, err
  208. }
  209. func (rh *realHistory) CreateControllerRevision(parent metav1.Object, revision *apps.ControllerRevision, collisionCount *int32) (*apps.ControllerRevision, error) {
  210. if collisionCount == nil {
  211. return nil, fmt.Errorf("collisionCount should not be nil")
  212. }
  213. // Clone the input
  214. clone := revision.DeepCopy()
  215. // Continue to attempt to create the revision updating the name with a new hash on each iteration
  216. for {
  217. hash := HashControllerRevision(revision, collisionCount)
  218. // Update the revisions name
  219. clone.Name = ControllerRevisionName(parent.GetName(), hash)
  220. ns := parent.GetNamespace()
  221. created, err := rh.client.AppsV1().ControllerRevisions(ns).Create(clone)
  222. if errors.IsAlreadyExists(err) {
  223. exists, err := rh.client.AppsV1().ControllerRevisions(ns).Get(clone.Name, metav1.GetOptions{})
  224. if err != nil {
  225. return nil, err
  226. }
  227. if bytes.Equal(exists.Data.Raw, clone.Data.Raw) {
  228. return exists, nil
  229. }
  230. *collisionCount++
  231. continue
  232. }
  233. return created, err
  234. }
  235. }
  236. func (rh *realHistory) UpdateControllerRevision(revision *apps.ControllerRevision, newRevision int64) (*apps.ControllerRevision, error) {
  237. clone := revision.DeepCopy()
  238. err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
  239. if clone.Revision == newRevision {
  240. return nil
  241. }
  242. clone.Revision = newRevision
  243. updated, updateErr := rh.client.AppsV1().ControllerRevisions(clone.Namespace).Update(clone)
  244. if updateErr == nil {
  245. return nil
  246. }
  247. if updated != nil {
  248. clone = updated
  249. }
  250. if updated, err := rh.lister.ControllerRevisions(clone.Namespace).Get(clone.Name); err == nil {
  251. // make a copy so we don't mutate the shared cache
  252. clone = updated.DeepCopy()
  253. }
  254. return updateErr
  255. })
  256. return clone, err
  257. }
  258. func (rh *realHistory) DeleteControllerRevision(revision *apps.ControllerRevision) error {
  259. return rh.client.AppsV1().ControllerRevisions(revision.Namespace).Delete(revision.Name, nil)
  260. }
  261. func (rh *realHistory) AdoptControllerRevision(parent metav1.Object, parentKind schema.GroupVersionKind, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) {
  262. // Return an error if the parent does not own the revision
  263. if owner := metav1.GetControllerOf(revision); owner != nil {
  264. return nil, fmt.Errorf("attempt to adopt revision owned by %v", owner)
  265. }
  266. // Use strategic merge patch to add an owner reference indicating a controller ref
  267. return rh.client.AppsV1().ControllerRevisions(parent.GetNamespace()).Patch(revision.GetName(),
  268. types.StrategicMergePatchType, []byte(fmt.Sprintf(
  269. `{"metadata":{"ownerReferences":[{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}],"uid":"%s"}}`,
  270. parentKind.GroupVersion().String(), parentKind.Kind,
  271. parent.GetName(), parent.GetUID(), revision.UID)))
  272. }
  273. func (rh *realHistory) ReleaseControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) {
  274. // Use strategic merge patch to add an owner reference indicating a controller ref
  275. released, err := rh.client.AppsV1().ControllerRevisions(revision.GetNamespace()).Patch(revision.GetName(),
  276. types.StrategicMergePatchType,
  277. []byte(fmt.Sprintf(`{"metadata":{"ownerReferences":[{"$patch":"delete","uid":"%s"}],"uid":"%s"}}`, parent.GetUID(), revision.UID)))
  278. if err != nil {
  279. if errors.IsNotFound(err) {
  280. // We ignore deleted revisions
  281. return nil, nil
  282. }
  283. if errors.IsInvalid(err) {
  284. // We ignore cases where the parent no longer owns the revision or where the revision has no
  285. // owner.
  286. return nil, nil
  287. }
  288. }
  289. return released, err
  290. }
  291. type fakeHistory struct {
  292. indexer cache.Indexer
  293. lister appslisters.ControllerRevisionLister
  294. }
  295. func (fh *fakeHistory) ListControllerRevisions(parent metav1.Object, selector labels.Selector) ([]*apps.ControllerRevision, error) {
  296. history, err := fh.lister.ControllerRevisions(parent.GetNamespace()).List(selector)
  297. if err != nil {
  298. return nil, err
  299. }
  300. var owned []*apps.ControllerRevision
  301. for i := range history {
  302. ref := metav1.GetControllerOf(history[i])
  303. if ref == nil || ref.UID == parent.GetUID() {
  304. owned = append(owned, history[i])
  305. }
  306. }
  307. return owned, err
  308. }
  309. func (fh *fakeHistory) addRevision(revision *apps.ControllerRevision) (*apps.ControllerRevision, error) {
  310. key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision)
  311. if err != nil {
  312. return nil, err
  313. }
  314. obj, found, err := fh.indexer.GetByKey(key)
  315. if err != nil {
  316. return nil, err
  317. }
  318. if found {
  319. foundRevision := obj.(*apps.ControllerRevision)
  320. return foundRevision, errors.NewAlreadyExists(apps.Resource("controllerrevision"), revision.Name)
  321. }
  322. return revision, fh.indexer.Update(revision)
  323. }
  324. func (fh *fakeHistory) CreateControllerRevision(parent metav1.Object, revision *apps.ControllerRevision, collisionCount *int32) (*apps.ControllerRevision, error) {
  325. if collisionCount == nil {
  326. return nil, fmt.Errorf("collisionCount should not be nil")
  327. }
  328. // Clone the input
  329. clone := revision.DeepCopy()
  330. clone.Namespace = parent.GetNamespace()
  331. // Continue to attempt to create the revision updating the name with a new hash on each iteration
  332. for {
  333. hash := HashControllerRevision(revision, collisionCount)
  334. // Update the revisions name and labels
  335. clone.Name = ControllerRevisionName(parent.GetName(), hash)
  336. created, err := fh.addRevision(clone)
  337. if errors.IsAlreadyExists(err) {
  338. *collisionCount++
  339. continue
  340. }
  341. return created, err
  342. }
  343. }
  344. func (fh *fakeHistory) DeleteControllerRevision(revision *apps.ControllerRevision) error {
  345. key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision)
  346. if err != nil {
  347. return err
  348. }
  349. obj, found, err := fh.indexer.GetByKey(key)
  350. if err != nil {
  351. return err
  352. }
  353. if !found {
  354. return errors.NewNotFound(apps.Resource("controllerrevisions"), revision.Name)
  355. }
  356. return fh.indexer.Delete(obj)
  357. }
  358. func (fh *fakeHistory) UpdateControllerRevision(revision *apps.ControllerRevision, newRevision int64) (*apps.ControllerRevision, error) {
  359. clone := revision.DeepCopy()
  360. clone.Revision = newRevision
  361. return clone, fh.indexer.Update(clone)
  362. }
  363. func (fh *fakeHistory) AdoptControllerRevision(parent metav1.Object, parentKind schema.GroupVersionKind, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) {
  364. if owner := metav1.GetControllerOf(revision); owner != nil {
  365. return nil, fmt.Errorf("attempt to adopt revision owned by %v", owner)
  366. }
  367. key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision)
  368. if err != nil {
  369. return nil, err
  370. }
  371. _, found, err := fh.indexer.GetByKey(key)
  372. if err != nil {
  373. return nil, err
  374. }
  375. if !found {
  376. return nil, errors.NewNotFound(apps.Resource("controllerrevisions"), revision.Name)
  377. }
  378. clone := revision.DeepCopy()
  379. clone.OwnerReferences = append(clone.OwnerReferences, *metav1.NewControllerRef(parent, parentKind))
  380. return clone, fh.indexer.Update(clone)
  381. }
  382. func (fh *fakeHistory) ReleaseControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) {
  383. key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision)
  384. if err != nil {
  385. return nil, err
  386. }
  387. _, found, err := fh.indexer.GetByKey(key)
  388. if err != nil {
  389. return nil, err
  390. }
  391. if !found {
  392. return nil, nil
  393. }
  394. clone := revision.DeepCopy()
  395. refs := clone.OwnerReferences
  396. clone.OwnerReferences = nil
  397. for i := range refs {
  398. if refs[i].UID != parent.GetUID() {
  399. clone.OwnerReferences = append(clone.OwnerReferences, refs[i])
  400. }
  401. }
  402. return clone, fh.indexer.Update(clone)
  403. }