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  }