github.com/Cloud-Foundations/Dominator@v0.3.4/imageserver/rpcd/replicator.go (about) 1 package rpcd 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "time" 8 9 imageclient "github.com/Cloud-Foundations/Dominator/imageserver/client" 10 "github.com/Cloud-Foundations/Dominator/lib/format" 11 "github.com/Cloud-Foundations/Dominator/lib/image" 12 "github.com/Cloud-Foundations/Dominator/lib/log" 13 "github.com/Cloud-Foundations/Dominator/lib/log/prefixlogger" 14 objectclient "github.com/Cloud-Foundations/Dominator/lib/objectserver/client" 15 "github.com/Cloud-Foundations/Dominator/lib/srpc" 16 "github.com/Cloud-Foundations/Dominator/proto/imageserver" 17 ) 18 19 func (t *srpcType) replicator(finishedReplication chan<- struct{}) { 20 initialTimeout := time.Second * 15 21 timeout := initialTimeout 22 var nextSleepStopTime time.Time 23 for { 24 nextSleepStopTime = time.Now().Add(timeout) 25 if client, err := srpc.DialHTTP("tcp", t.replicationMaster, 26 timeout); err != nil { 27 t.logger.Printf("Error dialing: %s %s\n", t.replicationMaster, err) 28 } else { 29 if conn, err := client.Call( 30 "ImageServer.GetImageUpdates"); err != nil { 31 t.logger.Println(err) 32 } else { 33 if err := t.getUpdates(conn, &finishedReplication); err != nil { 34 if err == io.EOF { 35 t.logger.Println( 36 "Connection to image replicator closed") 37 if nextSleepStopTime.Sub(time.Now()) < 1 { 38 timeout = initialTimeout 39 } 40 } else { 41 t.logger.Println(err) 42 } 43 } 44 conn.Close() 45 } 46 client.Close() 47 } 48 time.Sleep(nextSleepStopTime.Sub(time.Now())) 49 if timeout < time.Minute { 50 timeout *= 2 51 } 52 } 53 } 54 55 func (t *srpcType) getUpdates(conn *srpc.Conn, 56 finishedReplication *chan<- struct{}) error { 57 t.logger.Printf("Image replicator: connected to: %s\n", t.replicationMaster) 58 replicationStartTime := time.Now() 59 initialImages := make(map[string]struct{}) 60 if t.archiveMode { 61 initialImages = nil 62 } 63 someImagesFailed := false 64 for { 65 var imageUpdate imageserver.ImageUpdate 66 if err := conn.Decode(&imageUpdate); err != nil { 67 if err == io.EOF { 68 return err 69 } 70 return errors.New("decode err: " + err.Error()) 71 } 72 switch imageUpdate.Operation { 73 case imageserver.OperationAddImage: 74 if imageUpdate.Name == "" { // Initial list has been sent. 75 if initialImages != nil { 76 t.deleteMissingImages(initialImages) 77 initialImages = nil 78 } 79 if *finishedReplication != nil { 80 close(*finishedReplication) 81 *finishedReplication = nil 82 } 83 if someImagesFailed { 84 t.logger.Printf("Partially replicated images in %s\n", 85 format.Duration(time.Since(replicationStartTime))) 86 return nil 87 } 88 t.logger.Printf("Replicated all current images in %s\n", 89 format.Duration(time.Since(replicationStartTime))) 90 continue 91 } 92 if t.excludeFilter != nil && 93 t.excludeFilter.Match(imageUpdate.Name) { 94 t.logger.Debugf(0, "Excluding %s from replication\n", 95 imageUpdate.Name) 96 continue 97 } 98 if t.includeFilter != nil && 99 !t.includeFilter.Match(imageUpdate.Name) { 100 t.logger.Debugf(0, "Not including %s in replication\n", 101 imageUpdate.Name) 102 continue 103 } 104 if initialImages != nil { 105 initialImages[imageUpdate.Name] = struct{}{} 106 } 107 if err := t.addImage(imageUpdate.Name); err != nil { 108 t.logger.Printf("error adding image: %s: %s\n", 109 imageUpdate.Name, err) 110 someImagesFailed = true 111 } 112 case imageserver.OperationDeleteImage: 113 if t.archiveMode { 114 continue 115 } 116 t.logger.Printf("Replicator(%s): delete image\n", imageUpdate.Name) 117 err := t.imageDataBase.DeleteImage(imageUpdate.Name, 118 &srpc.AuthInformation{HaveMethodAccess: true}) 119 if err != nil { 120 return err 121 } 122 case imageserver.OperationMakeDirectory: 123 directory := imageUpdate.Directory 124 if directory == nil { 125 return errors.New("nil imageUpdate.Directory") 126 } 127 if err := t.imageDataBase.UpdateDirectory(*directory); err != nil { 128 return err 129 } 130 } 131 } 132 } 133 134 func (t *srpcType) deleteMissingImages(imagesToKeep map[string]struct{}) { 135 missingImages := make([]string, 0) 136 for _, imageName := range t.imageDataBase.ListImages() { 137 if _, ok := imagesToKeep[imageName]; !ok { 138 missingImages = append(missingImages, imageName) 139 } 140 } 141 for _, imageName := range missingImages { 142 t.logger.Printf("Replicator(%s): delete missing image\n", imageName) 143 err := t.imageDataBase.DeleteImage(imageName, 144 &srpc.AuthInformation{HaveMethodAccess: true}) 145 if err != nil { 146 t.logger.Println(err) 147 } 148 } 149 } 150 151 func (t *srpcType) extendImageExpiration(name string, 152 img *image.Image) (bool, error) { 153 timeout := time.Second * 60 154 client, err := t.imageserverResource.GetHTTP(nil, timeout) 155 if err != nil { 156 return false, err 157 } 158 defer client.Put() 159 expiresAt, err := imageclient.GetImageExpiration(client, name) 160 if err != nil { 161 if err == io.EOF { 162 client.Close() 163 } 164 return false, err 165 } 166 return t.imageDataBase.ChangeImageExpiration(name, expiresAt, 167 &srpc.AuthInformation{HaveMethodAccess: true}) 168 } 169 170 func (t *srpcType) addImage(name string) error { 171 timeout := time.Second * 60 172 if t.checkImageBeingInjected(name) { 173 return nil 174 } 175 logger := prefixlogger.New(fmt.Sprintf("Replicator(%s): ", name), t.logger) 176 if img := t.imageDataBase.GetImage(name); img != nil { 177 if img.ExpiresAt.IsZero() { 178 return nil 179 } 180 if changed, err := t.extendImageExpiration(name, img); err != nil { 181 logger.Println(err) 182 } else if changed { 183 logger.Println("extended expiration time") 184 } 185 return nil 186 } 187 logger.Println("add image") 188 client, err := t.imageserverResource.GetHTTP(nil, timeout) 189 if err != nil { 190 return err 191 } 192 defer client.Put() 193 request := imageserver.GetImageRequest{ 194 ImageName: name, 195 Timeout: timeout, 196 } 197 if t.archiveMode && !*archiveExpiringImages { 198 request.IgnoreFilesystemIfExpiring = true 199 } 200 var reply imageserver.GetImageResponse 201 err = client.RequestReply("ImageServer.GetImage", request, &reply) 202 if err != nil { 203 client.Close() 204 return err 205 } 206 img := reply.Image 207 if img == nil { 208 return errors.New(name + ": not found") 209 } 210 logger.Println("downloaded image") 211 if t.archiveMode && !img.ExpiresAt.IsZero() && !*archiveExpiringImages { 212 logger.Println("ignoring expiring image in archiver mode") 213 return nil 214 } 215 img.FileSystem.RebuildInodePointers() 216 err = t.imageDataBase.DoWithPendingImage(img, func() error { 217 if err := t.getMissingObjects(img, client, logger); err != nil { 218 client.Close() 219 return err 220 } 221 err := t.imageDataBase.AddImage(img, name, 222 &srpc.AuthInformation{HaveMethodAccess: true}) 223 if err != nil { 224 return err 225 } 226 return nil 227 }) 228 if err != nil { 229 return err 230 } 231 logger.Println("added image") 232 return nil 233 } 234 235 func (t *srpcType) checkImageBeingInjected(name string) bool { 236 t.imagesBeingInjectedLock.Lock() 237 defer t.imagesBeingInjectedLock.Unlock() 238 _, ok := t.imagesBeingInjected[name] 239 return ok 240 } 241 242 func (t *srpcType) getMissingObjects(img *image.Image, client *srpc.Client, 243 logger log.DebugLogger) error { 244 objClient := objectclient.AttachObjectClient(client) 245 defer objClient.Close() 246 return img.GetMissingObjects(t.objSrv, objClient, logger) 247 }