github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/cmd/imagetool/addReplaceImage.go (about) 1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "strconv" 8 "strings" 9 10 "github.com/Cloud-Foundations/Dominator/imageserver/client" 11 "github.com/Cloud-Foundations/Dominator/lib/filesystem" 12 "github.com/Cloud-Foundations/Dominator/lib/log" 13 objectclient "github.com/Cloud-Foundations/Dominator/lib/objectserver/client" 14 "github.com/Cloud-Foundations/Dominator/lib/srpc" 15 ) 16 17 func addReplaceImageSubcommand(args []string, logger log.DebugLogger) error { 18 imageSClient, objectClient := getClients() 19 err := addReplaceImage(imageSClient, objectClient, args[0], args[1], 20 args[2:]) 21 if err != nil { 22 return fmt.Errorf("Error adding image: \"%s\": %s", args[0], err) 23 } 24 return nil 25 } 26 27 func bulkAddReplaceImagesSubcommand(args []string, 28 logger log.DebugLogger) error { 29 imageSClient, objectClient := getClients() 30 err := bulkAddReplaceImages(imageSClient, objectClient, args) 31 if err != nil { 32 return fmt.Errorf("Error adding image: \"%s\": %s", args[0], err) 33 } 34 return nil 35 } 36 37 func addReplaceImage(imageSClient *srpc.Client, 38 objectClient *objectclient.ObjectClient, 39 name, baseImageName string, layerImageNames []string) error { 40 imageExists, err := client.CheckImage(imageSClient, name) 41 if err != nil { 42 return errors.New("error checking for image existence: " + err.Error()) 43 } 44 if imageExists { 45 return errors.New("image exists") 46 } 47 newImage, err := getImage(imageSClient, baseImageName) 48 if err != nil { 49 return err 50 } 51 if !newImage.ExpiresAt.IsZero() { 52 fmt.Fprintf(os.Stderr, "Skipping expiring image: %s\n", baseImageName) 53 return nil 54 } 55 for _, layerImageName := range layerImageNames { 56 fs, err := buildImage(imageSClient, newImage.Filter, layerImageName) 57 if err != nil { 58 return err 59 } 60 if err := layerImages(newImage.FileSystem, fs); err != nil { 61 return err 62 } 63 } 64 if err := spliceComputedFiles(newImage.FileSystem); err != nil { 65 return err 66 } 67 return addImage(imageSClient, name, newImage) 68 } 69 70 func bulkAddReplaceImages(imageSClient *srpc.Client, 71 objectClient *objectclient.ObjectClient, layerImageNames []string) error { 72 imageNames, err := client.ListImages(imageSClient) 73 if err != nil { 74 return err 75 } 76 err = bulkAddReplaceImagesSep(imageSClient, objectClient, layerImageNames, 77 imageNames, "/") 78 if err != nil { 79 return err 80 } 81 return bulkAddReplaceImagesSep(imageSClient, objectClient, layerImageNames, 82 imageNames, ".") 83 } 84 85 func bulkAddReplaceImagesSep(imageSClient *srpc.Client, 86 objectClient *objectclient.ObjectClient, layerImageNames []string, 87 imageNames []string, separator string) error { 88 baseNames := make(map[string]uint64) 89 for _, name := range imageNames { 90 fields := strings.Split(name, separator) 91 nFields := len(fields) 92 if nFields < 2 { 93 continue 94 } 95 lastField := fields[nFields-1] 96 if version, err := strconv.ParseUint(lastField, 10, 64); err != nil { 97 continue 98 } else { 99 name := strings.Join(fields[:nFields-1], separator) 100 if oldVersion := baseNames[name]; version >= oldVersion { 101 baseNames[name] = version 102 } 103 } 104 } 105 for baseName, version := range baseNames { 106 oldName := fmt.Sprintf("%s%s%d", baseName, separator, version) 107 newName := fmt.Sprintf("%s%s%d", baseName, separator, version+1) 108 if err := addReplaceImage(imageSClient, objectClient, 109 newName, oldName, layerImageNames); err != nil { 110 return err 111 } 112 } 113 return nil 114 } 115 116 func layerImages(baseFS *filesystem.FileSystem, 117 layerFS *filesystem.FileSystem) error { 118 for filename, layerInum := range layerFS.FilenameToInodeTable() { 119 layerInode := layerFS.InodeTable[layerInum] 120 if _, ok := layerInode.(*filesystem.DirectoryInode); ok { 121 continue 122 } 123 baseInum, ok := baseFS.FilenameToInodeTable()[filename] 124 if !ok { 125 return errors.New(filename + " missing in base image") 126 } 127 baseInode := baseFS.InodeTable[baseInum] 128 sameType, sameMetadata, sameData := filesystem.CompareInodes(baseInode, 129 layerInode, nil) 130 if !sameType { 131 return errors.New(filename + " changed type") 132 } 133 if sameMetadata && sameData { 134 continue 135 } 136 baseFS.InodeTable[baseInum] = layerInode 137 } 138 return nil 139 }