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  }