github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/cmd/subtool/pushImage.go (about) 1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "time" 8 9 "github.com/Cloud-Foundations/Dominator/dom/lib" 10 imgclient "github.com/Cloud-Foundations/Dominator/imageserver/client" 11 "github.com/Cloud-Foundations/Dominator/lib/filesystem" 12 "github.com/Cloud-Foundations/Dominator/lib/filesystem/scanner" 13 "github.com/Cloud-Foundations/Dominator/lib/filter" 14 "github.com/Cloud-Foundations/Dominator/lib/format" 15 "github.com/Cloud-Foundations/Dominator/lib/hash" 16 "github.com/Cloud-Foundations/Dominator/lib/image" 17 "github.com/Cloud-Foundations/Dominator/lib/log" 18 "github.com/Cloud-Foundations/Dominator/lib/objectcache" 19 "github.com/Cloud-Foundations/Dominator/lib/srpc" 20 "github.com/Cloud-Foundations/Dominator/lib/triggers" 21 "github.com/Cloud-Foundations/Dominator/proto/sub" 22 "github.com/Cloud-Foundations/Dominator/sub/client" 23 ) 24 25 type nullObjectGetterType struct{} 26 27 type timedImageFetch struct { 28 image *image.Image 29 duration time.Duration 30 } 31 32 func (getter nullObjectGetterType) GetObject(hashVal hash.Hash) ( 33 uint64, io.ReadCloser, error) { 34 return 0, nil, errors.New("no computed files") 35 } 36 37 func pushImageSubcommand(args []string, logger log.DebugLogger) error { 38 startTime := showStart("getSubClient()") 39 srpcClient := getSubClientRetry(logger) 40 defer srpcClient.Close() 41 showTimeTaken(startTime) 42 if err := pushImage(srpcClient, args[0]); err != nil { 43 return fmt.Errorf("Error pushing image: %s: %s", args[0], err) 44 } 45 return nil 46 } 47 48 func pushImage(srpcClient *srpc.Client, imageName string) error { 49 computedInodes := make(map[string]*filesystem.RegularInode) 50 // Start querying the imageserver for the image. 51 imageServerAddress := fmt.Sprintf("%s:%d", 52 *imageServerHostname, *imageServerPortNum) 53 imgChannel := getImageChannel(imageServerAddress, imageName, timeoutTime) 54 subObj := lib.Sub{ 55 Hostname: *subHostname, 56 Client: srpcClient, 57 ComputedInodes: computedInodes} 58 deleteMissingComputedFiles := true 59 ignoreMissingComputedFiles := false 60 if *computedFilesRoot == "" { 61 subObj.ObjectGetter = nullObjectGetterType{} 62 deleteMissingComputedFiles = false 63 ignoreMissingComputedFiles = true 64 } else { 65 fs, err := scanner.ScanFileSystem(*computedFilesRoot, nil, nil, nil, 66 nil, nil) 67 if err != nil { 68 return err 69 } 70 subObj.ObjectGetter = fs 71 for filename, inum := range fs.FilenameToInodeTable() { 72 if inode, ok := fs.InodeTable[inum].(*filesystem.RegularInode); ok { 73 computedInodes[filename] = inode 74 } 75 } 76 } 77 startTime := showStart("<-imgChannel") 78 imageResult := <-imgChannel 79 showTimeTaken(startTime) 80 logger.Printf("Background image fetch took %s\n", 81 format.Duration(imageResult.duration)) 82 img := imageResult.image 83 var err error 84 if *filterFile != "" { 85 img.Filter, err = filter.Load(*filterFile) 86 if err != nil { 87 return err 88 } 89 } 90 if *triggersFile != "" { 91 img.Triggers, err = triggers.Load(*triggersFile) 92 if err != nil { 93 return err 94 } 95 } else if *triggersString != "" { 96 img.Triggers, err = triggers.Decode([]byte(*triggersString)) 97 if err != nil { 98 return err 99 } 100 } 101 if err := pollFetchAndPush(&subObj, img, imageServerAddress, timeoutTime, 102 logger); err != nil { 103 return err 104 } 105 var updateRequest sub.UpdateRequest 106 var updateReply sub.UpdateResponse 107 startTime = showStart("lib.BuildUpdateRequest()") 108 if lib.BuildUpdateRequest(subObj, img, &updateRequest, 109 deleteMissingComputedFiles, ignoreMissingComputedFiles, logger) { 110 showBlankLine() 111 return errors.New("missing computed file(s)") 112 } 113 showTimeTaken(startTime) 114 updateRequest.ImageName = imageName 115 updateRequest.Wait = true 116 startTime = showStart("Subd.Update()") 117 err = client.CallUpdate(srpcClient, updateRequest, &updateReply) 118 if err != nil { 119 showBlankLine() 120 return err 121 } 122 showTimeTaken(startTime) 123 return nil 124 } 125 126 func getImageChannel(clientName, imageName string, 127 timeoutTime time.Time) <-chan timedImageFetch { 128 resultChannel := make(chan timedImageFetch, 1) 129 go func() { 130 startTime := time.Now() 131 img, err := getImageRetry(clientName, imageName, timeoutTime) 132 if err != nil { 133 logger.Fatalf("Error getting image: %s\n", err) 134 } 135 resultChannel <- timedImageFetch{img, time.Since(startTime)} 136 }() 137 return resultChannel 138 } 139 140 func getImageRetry(clientName, imageName string, 141 timeoutTime time.Time) (*image.Image, error) { 142 imageSrpcClient, err := srpc.DialHTTP("tcp", clientName, 0) 143 if err != nil { 144 return nil, err 145 } 146 defer imageSrpcClient.Close() 147 for ; time.Now().Before(timeoutTime); time.Sleep(time.Second) { 148 img, err := imgclient.GetImage(imageSrpcClient, imageName) 149 if err != nil { 150 return nil, err 151 } else if img != nil { 152 if err := img.FileSystem.RebuildInodePointers(); err != nil { 153 return nil, err 154 } 155 img.FileSystem.InodeToFilenamesTable() 156 img.FileSystem.FilenameToInodeTable() 157 img.FileSystem.HashToInodesTable() 158 img.FileSystem.ComputeTotalDataBytes() 159 img.FileSystem.BuildEntryMap() 160 return img, nil 161 } 162 } 163 return nil, errors.New("timed out getting image") 164 } 165 166 func pollFetchAndPush(subObj *lib.Sub, img *image.Image, 167 imageServerAddress string, timeoutTime time.Time, 168 logger log.DebugLogger) error { 169 var generationCount uint64 170 deleteEarly := *deleteBeforeFetch 171 ignoreMissingComputedFiles := true 172 pushComputedFiles := true 173 if *computedFilesRoot == "" { 174 ignoreMissingComputedFiles = false 175 pushComputedFiles = false 176 } 177 for ; time.Now().Before(timeoutTime); time.Sleep(time.Second) { 178 var pollReply sub.PollResponse 179 if err := pollAndBuildPointers(subObj.Client, &generationCount, 180 &pollReply); err != nil { 181 return err 182 } 183 if pollReply.FileSystem == nil { 184 continue 185 } 186 if deleteEarly { 187 deleteEarly = false 188 if deleteUnneededFiles(subObj.Client, pollReply.FileSystem, 189 img.FileSystem, logger) { 190 continue 191 } 192 } 193 subObj.FileSystem = pollReply.FileSystem 194 subObj.ObjectCache = pollReply.ObjectCache 195 startTime := showStart("lib.BuildMissingLists()") 196 objectsToFetch, objectsToPush := lib.BuildMissingLists(*subObj, img, 197 pushComputedFiles, ignoreMissingComputedFiles, logger) 198 showTimeTaken(startTime) 199 if len(objectsToFetch) < 1 && len(objectsToPush) < 1 { 200 return nil 201 } 202 if len(objectsToFetch) > 0 { 203 startTime := showStart("Fetch()") 204 err := fetchUntil(subObj, sub.FetchRequest{ 205 ServerAddress: imageServerAddress, 206 Wait: true, 207 Hashes: objectcache.ObjectMapToCache(objectsToFetch)}, 208 timeoutTime, logger) 209 if err != nil { 210 logger.Printf("Error calling %s:Subd.Fetch(%s): %s\n", 211 subObj.Hostname, imageServerAddress, err) 212 return err 213 } 214 showTimeTaken(startTime) 215 } 216 if len(objectsToPush) > 0 { 217 startTime := showStart("lib.PushObjects()") 218 err := lib.PushObjects(*subObj, objectsToPush, logger) 219 if err != nil { 220 showBlankLine() 221 return err 222 } 223 showTimeTaken(startTime) 224 } 225 } 226 return errors.New("timed out fetching and pushing objects") 227 } 228 229 func fetchUntil(subObj *lib.Sub, request sub.FetchRequest, 230 timeoutTime time.Time, logger log.DebugLogger) error { 231 showBlank := true 232 for ; time.Now().Before(timeoutTime); time.Sleep(time.Second) { 233 err := subObj.Client.RequestReply("Subd.Fetch", request, 234 &sub.FetchResponse{}) 235 if err == nil { 236 return nil 237 } 238 if showBlank { 239 showBlankLine() 240 showBlank = false 241 } 242 logger.Printf("Error calling %s:Subd.Fetch(): %s\n", 243 subObj.Hostname, err) 244 } 245 if showBlank { 246 showBlankLine() 247 } 248 return errors.New("timed out fetching objects") 249 } 250 251 func pollAndBuildPointers(srpcClient *srpc.Client, generationCount *uint64, 252 pollReply *sub.PollResponse) error { 253 pollRequest := sub.PollRequest{HaveGeneration: *generationCount} 254 startTime := showStart("Poll()") 255 err := client.CallPoll(srpcClient, pollRequest, pollReply) 256 if err != nil { 257 showBlankLine() 258 return err 259 } 260 showTimeTaken(startTime) 261 *generationCount = pollReply.GenerationCount 262 fs := pollReply.FileSystem 263 if fs == nil { 264 return nil 265 } 266 startTime = showStart("FileSystem.RebuildInodePointers()") 267 if err := fs.RebuildInodePointers(); err != nil { 268 showBlankLine() 269 return err 270 } 271 showTimeTaken(startTime) 272 fs.BuildEntryMap() 273 return nil 274 } 275 276 func showStart(operation string) time.Time { 277 if *showTimes { 278 logger.Print(operation, " ") 279 } 280 return time.Now() 281 } 282 283 func showTimeTaken(startTime time.Time) { 284 if *showTimes { 285 stopTime := time.Now() 286 logger.Printf("took %s\n", format.Duration(stopTime.Sub(startTime))) 287 } 288 } 289 290 func showBlankLine() { 291 if *showTimes { 292 logger.Println() 293 } 294 } 295 296 func deleteUnneededFiles(srpcClient *srpc.Client, subFS *filesystem.FileSystem, 297 imgFS *filesystem.FileSystem, logger log.DebugLogger) bool { 298 startTime := showStart("compute early files to delete") 299 pathsToDelete := make([]string, 0) 300 imgHashToInodesTable := imgFS.HashToInodesTable() 301 imgFilenameToInodeTable := imgFS.FilenameToInodeTable() 302 for pathname, inum := range subFS.FilenameToInodeTable() { 303 if inode, ok := subFS.InodeTable[inum].(*filesystem.RegularInode); ok { 304 if inode.Size > 0 { 305 if _, ok := imgHashToInodesTable[inode.Hash]; !ok { 306 pathsToDelete = append(pathsToDelete, pathname) 307 } 308 } else { 309 if _, ok := imgFilenameToInodeTable[pathname]; !ok { 310 pathsToDelete = append(pathsToDelete, pathname) 311 } 312 } 313 } 314 } 315 showTimeTaken(startTime) 316 if len(pathsToDelete) < 1 { 317 return false 318 } 319 updateRequest := sub.UpdateRequest{ 320 Wait: true, 321 PathsToDelete: pathsToDelete} 322 var updateReply sub.UpdateResponse 323 startTime = showStart("Subd.Update() for early files to delete") 324 err := client.CallUpdate(srpcClient, updateRequest, &updateReply) 325 showTimeTaken(startTime) 326 if err != nil { 327 logger.Println(err) 328 } 329 return true 330 }