github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/cmd/imagetool/diffImages.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "encoding/gob" 6 "fmt" 7 "io/ioutil" 8 "net" 9 "os" 10 "os/exec" 11 "time" 12 13 hyperclient "github.com/Cloud-Foundations/Dominator/hypervisor/client" 14 imgclient "github.com/Cloud-Foundations/Dominator/imageserver/client" 15 "github.com/Cloud-Foundations/Dominator/lib/constants" 16 "github.com/Cloud-Foundations/Dominator/lib/errors" 17 "github.com/Cloud-Foundations/Dominator/lib/filesystem" 18 "github.com/Cloud-Foundations/Dominator/lib/image" 19 "github.com/Cloud-Foundations/Dominator/lib/log" 20 "github.com/Cloud-Foundations/Dominator/lib/srpc" 21 fm_proto "github.com/Cloud-Foundations/Dominator/proto/fleetmanager" 22 "github.com/Cloud-Foundations/Dominator/proto/sub" 23 subclient "github.com/Cloud-Foundations/Dominator/sub/client" 24 ) 25 26 func diffSubcommand(args []string, logger log.DebugLogger) error { 27 return diffTypedImages(args[0], args[1], args[2]) 28 } 29 30 func diffTypedImages(tool string, lName string, rName string) error { 31 lfs, err := getTypedImage(lName) 32 if err != nil { 33 return fmt.Errorf("Error getting left image: %s", err) 34 } 35 if lfs, err = applyDeleteFilter(lfs); err != nil { 36 return fmt.Errorf("Error filtering left image: %s", err) 37 } 38 rfs, err := getTypedImage(rName) 39 if err != nil { 40 return fmt.Errorf("Error getting right image: %s", err) 41 } 42 if rfs, err = applyDeleteFilter(rfs); err != nil { 43 return fmt.Errorf("Error filtering right image: %s", err) 44 } 45 err = diffImages(tool, lfs, rfs) 46 if err != nil { 47 return fmt.Errorf("Error diffing images: %s", err) 48 } 49 return nil 50 } 51 52 func getTypedImage(typedName string) (*filesystem.FileSystem, error) { 53 if len(typedName) < 3 || typedName[1] != ':' { 54 imageSClient, _ := getClients() 55 return getFsOfImage(imageSClient, typedName) 56 } 57 switch name := typedName[2:]; typedName[0] { 58 case 'd': 59 return scanDirectory(name) 60 case 'f': 61 return readFileSystem(name) 62 case 'i': 63 imageSClient, _ := getClients() 64 return getFsOfImage(imageSClient, name) 65 case 'l': 66 return readFsOfImage(name) 67 case 's': 68 return pollImage(name) 69 case 'v': 70 return scanVm(name) 71 default: 72 return nil, errors.New("unknown image type: " + typedName[:1]) 73 } 74 } 75 76 func scanDirectory(name string) (*filesystem.FileSystem, error) { 77 fs, err := buildImageWithHasher(nil, nil, name, nil) 78 if err != nil { 79 return nil, err 80 } 81 return fs, nil 82 } 83 84 func readFileSystem(name string) (*filesystem.FileSystem, error) { 85 file, err := os.Open(name) 86 if err != nil { 87 return nil, err 88 } 89 defer file.Close() 90 var fileSystem filesystem.FileSystem 91 if err := gob.NewDecoder(file).Decode(&fileSystem); err != nil { 92 return nil, err 93 } 94 fileSystem.RebuildInodePointers() 95 return &fileSystem, nil 96 } 97 98 func getImage(client *srpc.Client, name string) (*image.Image, error) { 99 img, err := imgclient.GetImageWithTimeout(client, name, *timeout) 100 if err != nil { 101 return nil, err 102 } 103 if img == nil { 104 return nil, errors.New(name + ": not found") 105 } 106 if err := img.FileSystem.RebuildInodePointers(); err != nil { 107 return nil, err 108 } 109 return img, nil 110 } 111 112 func getFsOfImage(client *srpc.Client, name string) ( 113 *filesystem.FileSystem, error) { 114 if image, err := getImage(client, name); err != nil { 115 return nil, err 116 } else { 117 return image.FileSystem, nil 118 } 119 } 120 121 func readFsOfImage(name string) (*filesystem.FileSystem, error) { 122 file, err := os.Open(name) 123 if err != nil { 124 return nil, err 125 } 126 defer file.Close() 127 var image image.Image 128 if err := gob.NewDecoder(file).Decode(&image); err != nil { 129 return nil, err 130 } 131 image.FileSystem.RebuildInodePointers() 132 return image.FileSystem, nil 133 } 134 135 func pollImage(name string) (*filesystem.FileSystem, error) { 136 clientName := fmt.Sprintf("%s:%d", name, constants.SubPortNumber) 137 srpcClient, err := srpc.DialHTTP("tcp", clientName, 0) 138 if err != nil { 139 return nil, fmt.Errorf("Error dialing %s", err) 140 } 141 defer srpcClient.Close() 142 var request sub.PollRequest 143 var reply sub.PollResponse 144 if err = subclient.CallPoll(srpcClient, request, &reply); err != nil { 145 return nil, err 146 } 147 if reply.FileSystem == nil { 148 return nil, errors.New("no poll data") 149 } 150 reply.FileSystem.RebuildInodePointers() 151 return reply.FileSystem, nil 152 } 153 154 func scanVm(name string) (*filesystem.FileSystem, error) { 155 vmIpAddr, srpcClient, err := getVmIpAndHypervisor(name) 156 if err != nil { 157 return nil, err 158 } 159 defer srpcClient.Close() 160 fs, err := hyperclient.ScanVmRoot(srpcClient, vmIpAddr, nil) 161 if err != nil { 162 return nil, err 163 } 164 fs.RebuildInodePointers() 165 return fs, nil 166 } 167 168 func getVmIpAndHypervisor(vmHostname string) (net.IP, *srpc.Client, error) { 169 vmIpAddr, err := lookupIP(vmHostname) 170 if err != nil { 171 return nil, nil, err 172 } 173 hypervisorAddress, err := findHypervisor(vmIpAddr) 174 if err != nil { 175 return nil, nil, err 176 } 177 client, err := srpc.DialHTTP("tcp", hypervisorAddress, time.Second*10) 178 if err != nil { 179 return nil, nil, err 180 } 181 return vmIpAddr, client, nil 182 } 183 184 func findHypervisor(vmIpAddr net.IP) (string, error) { 185 if *hypervisorHostname != "" { 186 return fmt.Sprintf("%s:%d", *hypervisorHostname, *hypervisorPortNum), 187 nil 188 } else if *fleetManagerHostname != "" { 189 fm := fmt.Sprintf("%s:%d", *fleetManagerHostname, *fleetManagerPortNum) 190 client, err := srpc.DialHTTP("tcp", fm, time.Second*10) 191 if err != nil { 192 return "", err 193 } 194 defer client.Close() 195 return findHypervisorClient(client, vmIpAddr) 196 } else { 197 return fmt.Sprintf("localhost:%d", *hypervisorPortNum), nil 198 } 199 } 200 201 func findHypervisorClient(client *srpc.Client, 202 vmIpAddr net.IP) (string, error) { 203 request := fm_proto.GetHypervisorForVMRequest{vmIpAddr} 204 var reply fm_proto.GetHypervisorForVMResponse 205 err := client.RequestReply("FleetManager.GetHypervisorForVM", request, 206 &reply) 207 if err != nil { 208 return "", err 209 } 210 if err := errors.New(reply.Error); err != nil { 211 return "", err 212 } 213 return reply.HypervisorAddress, nil 214 } 215 216 func lookupIP(vmHostname string) (net.IP, error) { 217 if ips, err := net.LookupIP(vmHostname); err != nil { 218 return nil, err 219 } else if len(ips) != 1 { 220 return nil, fmt.Errorf("num IPs: %d != 1", len(ips)) 221 } else { 222 return ips[0], nil 223 } 224 } 225 226 func diffImages(tool string, lfs, rfs *filesystem.FileSystem) error { 227 lname, err := writeImage(lfs) 228 defer os.Remove(lname) 229 if err != nil { 230 return err 231 } 232 rname, err := writeImage(rfs) 233 defer os.Remove(rname) 234 if err != nil { 235 return err 236 } 237 cmd := exec.Command(tool, lname, rname) 238 cmd.Stdout = os.Stdout 239 return cmd.Run() 240 } 241 242 func writeImage(fs *filesystem.FileSystem) (string, error) { 243 file, err := ioutil.TempFile("", "imagetool") 244 if err != nil { 245 return "", err 246 } 247 defer file.Close() 248 writer := bufio.NewWriter(file) 249 defer writer.Flush() 250 return file.Name(), fs.Listf(writer, listSelector, listFilter) 251 }