treecache.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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 dns
  14. import (
  15. "encoding/json"
  16. "strings"
  17. skymsg "github.com/skynetservices/skydns/msg"
  18. )
  19. type TreeCache struct {
  20. ChildNodes map[string]*TreeCache
  21. Entries map[string]interface{}
  22. }
  23. func NewTreeCache() *TreeCache {
  24. return &TreeCache{
  25. ChildNodes: make(map[string]*TreeCache),
  26. Entries: make(map[string]interface{}),
  27. }
  28. }
  29. func (cache *TreeCache) Serialize() (string, error) {
  30. prettyJSON, err := json.MarshalIndent(cache, "", "\t")
  31. if err != nil {
  32. return "", err
  33. }
  34. return string(prettyJSON), nil
  35. }
  36. // setEntry creates the entire path if it doesn't already exist in the cache,
  37. // then sets the given service record under the given key. The path this entry
  38. // would have occupied in an etcd datastore is computed from the given fqdn and
  39. // stored as the "Key" of the skydns service; this is only required because
  40. // skydns expects the service record to contain a key in a specific format
  41. // (presumably for legacy compatibility). Note that the fqnd string typically
  42. // contains both the key and all elements in the path.
  43. func (cache *TreeCache) setEntry(key string, val *skymsg.Service, fqdn string, path ...string) {
  44. // TODO: Consolidate setEntry and setSubCache into a single method with a
  45. // type switch.
  46. // TODO: Insted of passing the fqdn as an argument, we can reconstruct
  47. // it from the path, provided callers always pass the full path to the
  48. // object. This is currently *not* the case, since callers first create
  49. // a new, empty node, populate it, then parent it under the right path.
  50. // So we don't know the full key till the final parenting operation.
  51. node := cache.ensureChildNode(path...)
  52. // This key is used to construct the "target" for SRV record lookups.
  53. // For normal service/endpoint lookups, this will result in a key like:
  54. // /skydns/local/cluster/svc/svcNS/svcName/record-hash
  55. // but for headless services that govern pods requesting a specific
  56. // hostname (as used by petset), this will end up being:
  57. // /skydns/local/cluster/svc/svcNS/svcName/pod-hostname
  58. val.Key = skymsg.Path(fqdn)
  59. node.Entries[key] = val
  60. }
  61. func (cache *TreeCache) getSubCache(path ...string) *TreeCache {
  62. childCache := cache
  63. for _, subpath := range path {
  64. childCache = childCache.ChildNodes[subpath]
  65. if childCache == nil {
  66. return nil
  67. }
  68. }
  69. return childCache
  70. }
  71. // setSubCache inserts the given subtree under the given path:key. Usually the
  72. // key is the name of a Kubernetes Service, and the path maps to the cluster
  73. // subdomains matching the Service.
  74. func (cache *TreeCache) setSubCache(key string, subCache *TreeCache, path ...string) {
  75. node := cache.ensureChildNode(path...)
  76. node.ChildNodes[key] = subCache
  77. }
  78. func (cache *TreeCache) getEntry(key string, path ...string) (interface{}, bool) {
  79. childNode := cache.getSubCache(path...)
  80. if childNode == nil {
  81. return nil, false
  82. }
  83. val, ok := childNode.Entries[key]
  84. return val, ok
  85. }
  86. func (cache *TreeCache) getValuesForPathWithWildcards(path ...string) []*skymsg.Service {
  87. retval := []*skymsg.Service{}
  88. nodesToExplore := []*TreeCache{cache}
  89. for idx, subpath := range path {
  90. nextNodesToExplore := []*TreeCache{}
  91. if idx == len(path)-1 {
  92. // if path ends on an entry, instead of a child node, add the entry
  93. for _, node := range nodesToExplore {
  94. if subpath == "*" {
  95. nextNodesToExplore = append(nextNodesToExplore, node)
  96. } else {
  97. if val, ok := node.Entries[subpath]; ok {
  98. retval = append(retval, val.(*skymsg.Service))
  99. } else {
  100. childNode := node.ChildNodes[subpath]
  101. if childNode != nil {
  102. nextNodesToExplore = append(nextNodesToExplore, childNode)
  103. }
  104. }
  105. }
  106. }
  107. nodesToExplore = nextNodesToExplore
  108. break
  109. }
  110. if subpath == "*" {
  111. for _, node := range nodesToExplore {
  112. for subkey, subnode := range node.ChildNodes {
  113. if !strings.HasPrefix(subkey, "_") {
  114. nextNodesToExplore = append(nextNodesToExplore, subnode)
  115. }
  116. }
  117. }
  118. } else {
  119. for _, node := range nodesToExplore {
  120. childNode := node.ChildNodes[subpath]
  121. if childNode != nil {
  122. nextNodesToExplore = append(nextNodesToExplore, childNode)
  123. }
  124. }
  125. }
  126. nodesToExplore = nextNodesToExplore
  127. }
  128. for _, node := range nodesToExplore {
  129. for _, val := range node.Entries {
  130. retval = append(retval, val.(*skymsg.Service))
  131. }
  132. }
  133. return retval
  134. }
  135. func (cache *TreeCache) deletePath(path ...string) bool {
  136. if len(path) == 0 {
  137. return false
  138. }
  139. if parentNode := cache.getSubCache(path[:len(path)-1]...); parentNode != nil {
  140. name := path[len(path)-1]
  141. if _, ok := parentNode.ChildNodes[name]; ok {
  142. delete(parentNode.ChildNodes, name)
  143. return true
  144. }
  145. // ExternalName services are stored with their name as the leaf key
  146. if _, ok := parentNode.Entries[name]; ok {
  147. delete(parentNode.Entries, name)
  148. return true
  149. }
  150. }
  151. return false
  152. }
  153. func (cache *TreeCache) deleteEntry(key string, path ...string) bool {
  154. childNode := cache.getSubCache(path...)
  155. if childNode == nil {
  156. return false
  157. }
  158. if _, ok := childNode.Entries[key]; ok {
  159. delete(childNode.Entries, key)
  160. return true
  161. }
  162. return false
  163. }
  164. func (cache *TreeCache) appendValues(recursive bool, ref [][]interface{}) {
  165. for _, value := range cache.Entries {
  166. ref[0] = append(ref[0], value)
  167. }
  168. if recursive {
  169. for _, node := range cache.ChildNodes {
  170. node.appendValues(recursive, ref)
  171. }
  172. }
  173. }
  174. func (cache *TreeCache) ensureChildNode(path ...string) *TreeCache {
  175. childNode := cache
  176. for _, subpath := range path {
  177. newNode, ok := childNode.ChildNodes[subpath]
  178. if !ok {
  179. newNode = NewTreeCache()
  180. childNode.ChildNodes[subpath] = newNode
  181. }
  182. childNode = newNode
  183. }
  184. return childNode
  185. }
  186. // unused function. keeping it around in commented-fashion
  187. // in the future, we might need some form of this function so that
  188. // we can serialize to a file in a mounted empty dir..
  189. //const (
  190. // dataFile = "data.dat"
  191. // crcFile = "data.crc"
  192. //)
  193. //func (cache *TreeCache) Serialize(dir string) (string, error) {
  194. // cache.m.RLock()
  195. // defer cache.m.RUnlock()
  196. // b, err := json.Marshal(cache)
  197. // if err != nil {
  198. // return "", err
  199. // }
  200. //
  201. // if err := ensureDir(dir, os.FileMode(0755)); err != nil {
  202. // return "", err
  203. // }
  204. // if err := ioutil.WriteFile(path.Join(dir, dataFile), b, 0644); err != nil {
  205. // return "", err
  206. // }
  207. // if err := ioutil.WriteFile(path.Join(dir, crcFile), getMD5(b), 0644); err != nil {
  208. // return "", err
  209. // }
  210. // return string(b), nil
  211. //}
  212. //func ensureDir(path string, perm os.FileMode) error {
  213. // s, err := os.Stat(path)
  214. // if err != nil || !s.IsDir() {
  215. // return os.Mkdir(path, perm)
  216. // }
  217. // return nil
  218. //}
  219. //func getMD5(b []byte) []byte {
  220. // h := md5.New()
  221. // h.Write(b)
  222. // return []byte(fmt.Sprintf("%x", h.Sum(nil)))
  223. //}
  224. // unused function. keeping it around in commented-fashion
  225. // in the future, we might need some form of this function so that
  226. // we can restart kube-dns, deserialize the tree and have a cache
  227. // without having to wait for kube-dns to reach out to API server.
  228. //func Deserialize(dir string) (*TreeCache, error) {
  229. // b, err := ioutil.ReadFile(path.Join(dir, dataFile))
  230. // if err != nil {
  231. // return nil, err
  232. // }
  233. //
  234. // hash, err := ioutil.ReadFile(path.Join(dir, crcFile))
  235. // if err != nil {
  236. // return nil, err
  237. // }
  238. // if !reflect.DeepEqual(hash, getMD5(b)) {
  239. // return nil, fmt.Errorf("Checksum failed")
  240. // }
  241. //
  242. // var cache TreeCache
  243. // err = json.Unmarshal(b, &cache)
  244. // if err != nil {
  245. // return nil, err
  246. // }
  247. // cache.m = &sync.RWMutex{}
  248. // return &cache, nil
  249. //}