controller_history.go 18 KB

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