github.com/Cloud-Foundations/Dominator@v0.3.4/dom/images/impl.go (about) 1 package images 2 3 import ( 4 "time" 5 6 "github.com/Cloud-Foundations/Dominator/imageserver/client" 7 "github.com/Cloud-Foundations/Dominator/lib/image" 8 "github.com/Cloud-Foundations/Dominator/lib/log" 9 "github.com/Cloud-Foundations/Dominator/lib/srpc" 10 "github.com/Cloud-Foundations/Dominator/lib/stringutil" 11 ) 12 13 func newManager(imageServerAddress string, logger log.Logger) *Manager { 14 imageInterestChannel := make(chan map[string]struct{}) 15 imageRequestChannel := make(chan string) 16 imageExpireChannel := make(chan string, 16) 17 m := &Manager{ 18 imageServerAddress: imageServerAddress, 19 logger: logger, 20 deduper: stringutil.NewStringDeduplicator(false), 21 imageInterestChannel: imageInterestChannel, 22 imageRequestChannel: imageRequestChannel, 23 imageExpireChannel: imageExpireChannel, 24 imagesByName: make(map[string]*image.Image), 25 missingImages: make(map[string]error), 26 } 27 go m.manager(imageInterestChannel, imageRequestChannel, imageExpireChannel) 28 return m 29 } 30 31 func (m *Manager) getNoWait(name string) (*image.Image, error) { 32 m.RLock() 33 defer m.RUnlock() 34 if image := m.imagesByName[name]; image != nil { 35 return image, nil 36 } 37 if err, ok := m.missingImages[name]; ok { 38 return nil, err 39 } 40 return nil, nil 41 } 42 43 func (m *Manager) getWait(name string) (*image.Image, error) { 44 if image, err := m.getNoWait(name); err != nil { 45 return nil, err 46 } else if image != nil { 47 return image, nil 48 } 49 m.imageRequestChannel <- name 50 m.imageRequestChannel <- "" 51 return m.getNoWait(name) 52 } 53 54 func (m *Manager) setImageInterestList(images map[string]struct{}, wait bool) { 55 delete(images, "") 56 m.imageInterestChannel <- images 57 if wait { 58 m.imageRequestChannel <- "" 59 } 60 } 61 62 func (m *Manager) manager(imageInterestChannel <-chan map[string]struct{}, 63 imageRequestChannel <-chan string, 64 imageExpireChannel <-chan string) { 65 var imageClient *srpc.Client 66 timer := time.NewTimer(time.Second) 67 for { 68 select { 69 case imageList := <-imageInterestChannel: 70 imageClient = m.setInterest(imageClient, imageList) 71 case name := <-imageRequestChannel: 72 if name == "" { 73 continue 74 } 75 imageClient = m.requestImage(imageClient, name) 76 case name := <-imageExpireChannel: 77 m.Lock() 78 delete(m.imagesByName, name) 79 m.missingImages[name] = nil // Try to get it again (expire extended) 80 m.Unlock() 81 m.rebuildDeDuper() 82 case <-timer.C: 83 // Loop over missing (pending) images. First obtain a copy. 84 missingImages := make(map[string]struct{}) 85 m.RLock() 86 for name := range m.missingImages { 87 missingImages[name] = struct{}{} 88 } 89 m.RUnlock() 90 for name := range missingImages { 91 imageClient = m.requestImage(imageClient, name) 92 } 93 } 94 m.RLock() 95 if len(m.missingImages) > 0 { 96 timer.Reset(time.Second) 97 } 98 m.RUnlock() 99 } 100 } 101 102 func (m *Manager) setInterest(imageClient *srpc.Client, 103 imageList map[string]struct{}) *srpc.Client { 104 for name := range imageList { 105 imageClient = m.requestImage(imageClient, name) 106 } 107 deletedSome := false 108 // Clean up unreferenced images. 109 for name := range m.imagesByName { 110 if _, ok := imageList[name]; !ok { 111 m.Lock() 112 delete(m.imagesByName, name) 113 m.Unlock() 114 deletedSome = true 115 } 116 } 117 for name := range m.missingImages { 118 if _, ok := imageList[name]; !ok { 119 m.Lock() 120 delete(m.missingImages, name) 121 m.Unlock() 122 } 123 } 124 if deletedSome { 125 m.rebuildDeDuper() 126 } 127 return imageClient 128 } 129 130 func (m *Manager) requestImage(imageClient *srpc.Client, 131 name string) *srpc.Client { 132 if _, ok := m.imagesByName[name]; ok { 133 return imageClient 134 } 135 var img *image.Image 136 var err error 137 imageClient, img, err = m.loadImage(imageClient, name) 138 m.Lock() 139 defer m.Unlock() 140 if img != nil && err == nil { 141 delete(m.missingImages, name) 142 m.imagesByName[name] = img 143 return imageClient 144 } 145 delete(m.imagesByName, name) 146 m.missingImages[name] = err 147 return imageClient 148 } 149 150 func (m *Manager) loadImage(imageClient *srpc.Client, name string) ( 151 *srpc.Client, *image.Image, error) { 152 if imageClient == nil { 153 var err error 154 imageClient, err = srpc.DialHTTP("tcp", m.imageServerAddress, 0) 155 if err != nil { 156 if !m.loggedDialFailure { 157 m.logger.Printf("Error dialing: %s: %s\n", 158 m.imageServerAddress, err) 159 m.loggedDialFailure = true 160 } 161 return nil, nil, err 162 } 163 } 164 img, err := client.GetImage(imageClient, name) 165 if err != nil { 166 m.logger.Printf("Error calling: %s\n", err) 167 imageClient.Close() 168 return nil, nil, err 169 } 170 if img == nil || m.scheduleExpiration(img, name) { 171 return imageClient, nil, nil 172 } 173 if err := img.FileSystem.RebuildInodePointers(); err != nil { 174 m.logger.Printf("Error building inode pointers for image: %s %s", 175 name, err) 176 return imageClient, nil, err 177 } 178 img.ReplaceStrings(m.deduper.DeDuplicate) 179 img.FileSystem = img.FileSystem.Filter(img.Filter) // Apply filter. 180 // Build cache data now to avoid potential concurrent builds later. 181 img.FileSystem.InodeToFilenamesTable() 182 img.FileSystem.FilenameToInodeTable() 183 img.FileSystem.HashToInodesTable() 184 img.FileSystem.ComputeTotalDataBytes() 185 img.FileSystem.BuildEntryMap() 186 m.logger.Printf("Got image: %s\n", name) 187 return imageClient, img, nil 188 } 189 190 func (m *Manager) rebuildDeDuper() { 191 for _, image := range m.imagesByName { 192 image.RegisterStrings(m.deduper.Register) 193 } 194 m.deduper.DeleteUnregistered() 195 } 196 197 func (m *Manager) scheduleExpiration(img *image.Image, name string) bool { 198 if img.ExpiresAt.IsZero() { 199 return false 200 } 201 duration := img.ExpiresAt.Sub(time.Now()) 202 if duration <= 0 { 203 return true 204 } 205 time.AfterFunc(duration, func() { 206 m.logger.Printf("Auto expiring (deleting) image: %s\n", name) 207 m.imageExpireChannel <- name 208 }) 209 return false 210 }