identity_mappers.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*
  2. Copyright 2016 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 petset
  14. import (
  15. "crypto/md5"
  16. "fmt"
  17. "sort"
  18. "strings"
  19. "github.com/golang/glog"
  20. "k8s.io/kubernetes/pkg/api"
  21. podapi "k8s.io/kubernetes/pkg/api/pod"
  22. "k8s.io/kubernetes/pkg/apis/apps"
  23. "k8s.io/kubernetes/pkg/util/sets"
  24. )
  25. // identityMapper is an interface for assigning identities to a pet.
  26. // All existing identity mappers just append "-(index)" to the petset name to
  27. // generate a unique identity. This is used in claims/DNS/hostname/petname
  28. // etc. There's a more elegant way to achieve this mapping, but we're
  29. // taking the simplest route till we have data on whether users will need
  30. // more customization.
  31. // Note that running a single identity mapper is not guaranteed to give
  32. // your pet a unique identity. You must run them all. Order doesn't matter.
  33. type identityMapper interface {
  34. // SetIdentity takes an id and assigns the given pet an identity based
  35. // on the pet set spec. The is must be unique amongst members of the
  36. // pet set.
  37. SetIdentity(id string, pet *api.Pod)
  38. // Identity returns the identity of the pet.
  39. Identity(pod *api.Pod) string
  40. }
  41. func newIdentityMappers(ps *apps.PetSet) []identityMapper {
  42. return []identityMapper{
  43. &NameIdentityMapper{ps},
  44. &NetworkIdentityMapper{ps},
  45. &VolumeIdentityMapper{ps},
  46. }
  47. }
  48. // NetworkIdentityMapper assigns network identity to pets.
  49. type NetworkIdentityMapper struct {
  50. ps *apps.PetSet
  51. }
  52. // SetIdentity sets network identity on the pet.
  53. func (n *NetworkIdentityMapper) SetIdentity(id string, pet *api.Pod) {
  54. pet.Annotations[podapi.PodHostnameAnnotation] = fmt.Sprintf("%v-%v", n.ps.Name, id)
  55. pet.Annotations[podapi.PodSubdomainAnnotation] = n.ps.Spec.ServiceName
  56. return
  57. }
  58. // Identity returns the network identity of the pet.
  59. func (n *NetworkIdentityMapper) Identity(pet *api.Pod) string {
  60. return n.String(pet)
  61. }
  62. // String is a string function for the network identity of the pet.
  63. func (n *NetworkIdentityMapper) String(pet *api.Pod) string {
  64. hostname := pet.Annotations[podapi.PodHostnameAnnotation]
  65. subdomain := pet.Annotations[podapi.PodSubdomainAnnotation]
  66. return strings.Join([]string{hostname, subdomain, n.ps.Namespace}, ".")
  67. }
  68. // VolumeIdentityMapper assigns storage identity to pets.
  69. type VolumeIdentityMapper struct {
  70. ps *apps.PetSet
  71. }
  72. // SetIdentity sets storge identity on the pet.
  73. func (v *VolumeIdentityMapper) SetIdentity(id string, pet *api.Pod) {
  74. petVolumes := []api.Volume{}
  75. petClaims := v.GetClaims(id)
  76. // These volumes will all go down with the pod. If a name matches one of
  77. // the claims in the pet set, it gets clobbered.
  78. podVolumes := map[string]api.Volume{}
  79. for _, podVol := range pet.Spec.Volumes {
  80. podVolumes[podVol.Name] = podVol
  81. }
  82. // Insert claims for the idempotent petSet volumes
  83. for name, claim := range petClaims {
  84. // Volumes on a pet for which there are no associated claims on the
  85. // petset are pod local, and die with the pod.
  86. podVol, ok := podVolumes[name]
  87. if ok {
  88. // TODO: Validate and reject this.
  89. glog.V(4).Infof("Overwriting existing volume source %v", podVol.Name)
  90. }
  91. newVol := api.Volume{
  92. Name: name,
  93. VolumeSource: api.VolumeSource{
  94. PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{
  95. ClaimName: claim.Name,
  96. // TODO: Use source definition to set this value when we have one.
  97. ReadOnly: false,
  98. },
  99. },
  100. }
  101. petVolumes = append(petVolumes, newVol)
  102. }
  103. // Transfer any ephemeral pod volumes
  104. for name, vol := range podVolumes {
  105. if _, ok := petClaims[name]; !ok {
  106. petVolumes = append(petVolumes, vol)
  107. }
  108. }
  109. pet.Spec.Volumes = petVolumes
  110. return
  111. }
  112. // Identity returns the storage identity of the pet.
  113. func (v *VolumeIdentityMapper) Identity(pet *api.Pod) string {
  114. // TODO: Make this a hash?
  115. return v.String(pet)
  116. }
  117. // String is a string function for the network identity of the pet.
  118. func (v *VolumeIdentityMapper) String(pet *api.Pod) string {
  119. ids := []string{}
  120. petVols := sets.NewString()
  121. for _, petVol := range v.ps.Spec.VolumeClaimTemplates {
  122. petVols.Insert(petVol.Name)
  123. }
  124. for _, podVol := range pet.Spec.Volumes {
  125. // Volumes on a pet for which there are no associated claims on the
  126. // petset are pod local, and die with the pod.
  127. if !petVols.Has(podVol.Name) {
  128. continue
  129. }
  130. if podVol.VolumeSource.PersistentVolumeClaim == nil {
  131. // TODO: Is this a part of the identity?
  132. ids = append(ids, fmt.Sprintf("%v:None", podVol.Name))
  133. continue
  134. }
  135. ids = append(ids, fmt.Sprintf("%v:%v", podVol.Name, podVol.VolumeSource.PersistentVolumeClaim.ClaimName))
  136. }
  137. sort.Strings(ids)
  138. return strings.Join(ids, "")
  139. }
  140. // GetClaims returns the volume claims associated with the given id.
  141. // The claims belong to the petset. The id should be unique within a petset.
  142. func (v *VolumeIdentityMapper) GetClaims(id string) map[string]api.PersistentVolumeClaim {
  143. petClaims := map[string]api.PersistentVolumeClaim{}
  144. for _, pvc := range v.ps.Spec.VolumeClaimTemplates {
  145. claim := pvc
  146. // TODO: Name length checking in validation.
  147. claim.Name = fmt.Sprintf("%v-%v-%v", claim.Name, v.ps.Name, id)
  148. claim.Namespace = v.ps.Namespace
  149. claim.Labels = v.ps.Spec.Selector.MatchLabels
  150. // TODO: We're assuming that the claim template has a volume QoS key, eg:
  151. // volume.alpha.kubernetes.io/storage-class: anything
  152. petClaims[pvc.Name] = claim
  153. }
  154. return petClaims
  155. }
  156. // GetClaimsForPet returns the pvcs for the given pet.
  157. func (v *VolumeIdentityMapper) GetClaimsForPet(pet *api.Pod) []api.PersistentVolumeClaim {
  158. // Strip out the "-(index)" from the pet name and use it to generate
  159. // claim names.
  160. id := strings.Split(pet.Name, "-")
  161. petID := id[len(id)-1]
  162. pvcs := []api.PersistentVolumeClaim{}
  163. for _, pvc := range v.GetClaims(petID) {
  164. pvcs = append(pvcs, pvc)
  165. }
  166. return pvcs
  167. }
  168. // NameIdentityMapper assigns names to pets.
  169. // It also puts the pet in the same namespace as the parent.
  170. type NameIdentityMapper struct {
  171. ps *apps.PetSet
  172. }
  173. // SetIdentity sets the pet namespace and name.
  174. func (n *NameIdentityMapper) SetIdentity(id string, pet *api.Pod) {
  175. pet.Name = fmt.Sprintf("%v-%v", n.ps.Name, id)
  176. pet.Namespace = n.ps.Namespace
  177. return
  178. }
  179. // Identity returns the name identity of the pet.
  180. func (n *NameIdentityMapper) Identity(pet *api.Pod) string {
  181. return n.String(pet)
  182. }
  183. // String is a string function for the name identity of the pet.
  184. func (n *NameIdentityMapper) String(pet *api.Pod) string {
  185. return fmt.Sprintf("%v/%v", pet.Namespace, pet.Name)
  186. }
  187. // identityHash computes a hash of the pet by running all the above identity
  188. // mappers.
  189. func identityHash(ps *apps.PetSet, pet *api.Pod) string {
  190. id := ""
  191. for _, idMapper := range newIdentityMappers(ps) {
  192. id += idMapper.Identity(pet)
  193. }
  194. return fmt.Sprintf("%x", md5.Sum([]byte(id)))
  195. }
  196. // copyPetID gives the realPet the same identity as the expectedPet.
  197. // Note that this is *not* a literal copy, but a copy of the fields that
  198. // contribute to the pet's identity. The returned boolean 'needsUpdate' will
  199. // be false if the realPet already has the same identity as the expectedPet.
  200. func copyPetID(realPet, expectedPet *pcb) (pod api.Pod, needsUpdate bool, err error) {
  201. if realPet.pod == nil || expectedPet.pod == nil {
  202. return pod, false, fmt.Errorf("Need a valid to and from pet for copy")
  203. }
  204. if realPet.parent.UID != expectedPet.parent.UID {
  205. return pod, false, fmt.Errorf("Cannot copy pets with different parents")
  206. }
  207. ps := realPet.parent
  208. if identityHash(ps, realPet.pod) == identityHash(ps, expectedPet.pod) {
  209. return *realPet.pod, false, nil
  210. }
  211. copyPod := *realPet.pod
  212. // This is the easiest way to give an identity to a pod. It won't work
  213. // when we stop using names for id.
  214. for _, idMapper := range newIdentityMappers(ps) {
  215. idMapper.SetIdentity(expectedPet.id, &copyPod)
  216. }
  217. return copyPod, true, nil
  218. }