github.com/Cloud-Foundations/Dominator@v0.3.4/imageserver/rpcd/replicator.go (about)

     1  package rpcd
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"time"
     8  
     9  	imageclient "github.com/Cloud-Foundations/Dominator/imageserver/client"
    10  	"github.com/Cloud-Foundations/Dominator/lib/format"
    11  	"github.com/Cloud-Foundations/Dominator/lib/image"
    12  	"github.com/Cloud-Foundations/Dominator/lib/log"
    13  	"github.com/Cloud-Foundations/Dominator/lib/log/prefixlogger"
    14  	objectclient "github.com/Cloud-Foundations/Dominator/lib/objectserver/client"
    15  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    16  	"github.com/Cloud-Foundations/Dominator/proto/imageserver"
    17  )
    18  
    19  func (t *srpcType) replicator(finishedReplication chan<- struct{}) {
    20  	initialTimeout := time.Second * 15
    21  	timeout := initialTimeout
    22  	var nextSleepStopTime time.Time
    23  	for {
    24  		nextSleepStopTime = time.Now().Add(timeout)
    25  		if client, err := srpc.DialHTTP("tcp", t.replicationMaster,
    26  			timeout); err != nil {
    27  			t.logger.Printf("Error dialing: %s %s\n", t.replicationMaster, err)
    28  		} else {
    29  			if conn, err := client.Call(
    30  				"ImageServer.GetImageUpdates"); err != nil {
    31  				t.logger.Println(err)
    32  			} else {
    33  				if err := t.getUpdates(conn, &finishedReplication); err != nil {
    34  					if err == io.EOF {
    35  						t.logger.Println(
    36  							"Connection to image replicator closed")
    37  						if nextSleepStopTime.Sub(time.Now()) < 1 {
    38  							timeout = initialTimeout
    39  						}
    40  					} else {
    41  						t.logger.Println(err)
    42  					}
    43  				}
    44  				conn.Close()
    45  			}
    46  			client.Close()
    47  		}
    48  		time.Sleep(nextSleepStopTime.Sub(time.Now()))
    49  		if timeout < time.Minute {
    50  			timeout *= 2
    51  		}
    52  	}
    53  }
    54  
    55  func (t *srpcType) getUpdates(conn *srpc.Conn,
    56  	finishedReplication *chan<- struct{}) error {
    57  	t.logger.Printf("Image replicator: connected to: %s\n", t.replicationMaster)
    58  	replicationStartTime := time.Now()
    59  	initialImages := make(map[string]struct{})
    60  	if t.archiveMode {
    61  		initialImages = nil
    62  	}
    63  	someImagesFailed := false
    64  	for {
    65  		var imageUpdate imageserver.ImageUpdate
    66  		if err := conn.Decode(&imageUpdate); err != nil {
    67  			if err == io.EOF {
    68  				return err
    69  			}
    70  			return errors.New("decode err: " + err.Error())
    71  		}
    72  		switch imageUpdate.Operation {
    73  		case imageserver.OperationAddImage:
    74  			if imageUpdate.Name == "" { // Initial list has been sent.
    75  				if initialImages != nil {
    76  					t.deleteMissingImages(initialImages)
    77  					initialImages = nil
    78  				}
    79  				if *finishedReplication != nil {
    80  					close(*finishedReplication)
    81  					*finishedReplication = nil
    82  				}
    83  				if someImagesFailed {
    84  					t.logger.Printf("Partially replicated images in %s\n",
    85  						format.Duration(time.Since(replicationStartTime)))
    86  					return nil
    87  				}
    88  				t.logger.Printf("Replicated all current images in %s\n",
    89  					format.Duration(time.Since(replicationStartTime)))
    90  				continue
    91  			}
    92  			if t.excludeFilter != nil &&
    93  				t.excludeFilter.Match(imageUpdate.Name) {
    94  				t.logger.Debugf(0, "Excluding %s from replication\n",
    95  					imageUpdate.Name)
    96  				continue
    97  			}
    98  			if t.includeFilter != nil &&
    99  				!t.includeFilter.Match(imageUpdate.Name) {
   100  				t.logger.Debugf(0, "Not including %s in replication\n",
   101  					imageUpdate.Name)
   102  				continue
   103  			}
   104  			if initialImages != nil {
   105  				initialImages[imageUpdate.Name] = struct{}{}
   106  			}
   107  			if err := t.addImage(imageUpdate.Name); err != nil {
   108  				t.logger.Printf("error adding image: %s: %s\n",
   109  					imageUpdate.Name, err)
   110  				someImagesFailed = true
   111  			}
   112  		case imageserver.OperationDeleteImage:
   113  			if t.archiveMode {
   114  				continue
   115  			}
   116  			t.logger.Printf("Replicator(%s): delete image\n", imageUpdate.Name)
   117  			err := t.imageDataBase.DeleteImage(imageUpdate.Name,
   118  				&srpc.AuthInformation{HaveMethodAccess: true})
   119  			if err != nil {
   120  				return err
   121  			}
   122  		case imageserver.OperationMakeDirectory:
   123  			directory := imageUpdate.Directory
   124  			if directory == nil {
   125  				return errors.New("nil imageUpdate.Directory")
   126  			}
   127  			if err := t.imageDataBase.UpdateDirectory(*directory); err != nil {
   128  				return err
   129  			}
   130  		}
   131  	}
   132  }
   133  
   134  func (t *srpcType) deleteMissingImages(imagesToKeep map[string]struct{}) {
   135  	missingImages := make([]string, 0)
   136  	for _, imageName := range t.imageDataBase.ListImages() {
   137  		if _, ok := imagesToKeep[imageName]; !ok {
   138  			missingImages = append(missingImages, imageName)
   139  		}
   140  	}
   141  	for _, imageName := range missingImages {
   142  		t.logger.Printf("Replicator(%s): delete missing image\n", imageName)
   143  		err := t.imageDataBase.DeleteImage(imageName,
   144  			&srpc.AuthInformation{HaveMethodAccess: true})
   145  		if err != nil {
   146  			t.logger.Println(err)
   147  		}
   148  	}
   149  }
   150  
   151  func (t *srpcType) extendImageExpiration(name string,
   152  	img *image.Image) (bool, error) {
   153  	timeout := time.Second * 60
   154  	client, err := t.imageserverResource.GetHTTP(nil, timeout)
   155  	if err != nil {
   156  		return false, err
   157  	}
   158  	defer client.Put()
   159  	expiresAt, err := imageclient.GetImageExpiration(client, name)
   160  	if err != nil {
   161  		if err == io.EOF {
   162  			client.Close()
   163  		}
   164  		return false, err
   165  	}
   166  	return t.imageDataBase.ChangeImageExpiration(name, expiresAt,
   167  		&srpc.AuthInformation{HaveMethodAccess: true})
   168  }
   169  
   170  func (t *srpcType) addImage(name string) error {
   171  	timeout := time.Second * 60
   172  	if t.checkImageBeingInjected(name) {
   173  		return nil
   174  	}
   175  	logger := prefixlogger.New(fmt.Sprintf("Replicator(%s): ", name), t.logger)
   176  	if img := t.imageDataBase.GetImage(name); img != nil {
   177  		if img.ExpiresAt.IsZero() {
   178  			return nil
   179  		}
   180  		if changed, err := t.extendImageExpiration(name, img); err != nil {
   181  			logger.Println(err)
   182  		} else if changed {
   183  			logger.Println("extended expiration time")
   184  		}
   185  		return nil
   186  	}
   187  	logger.Println("add image")
   188  	client, err := t.imageserverResource.GetHTTP(nil, timeout)
   189  	if err != nil {
   190  		return err
   191  	}
   192  	defer client.Put()
   193  	request := imageserver.GetImageRequest{
   194  		ImageName: name,
   195  		Timeout:   timeout,
   196  	}
   197  	if t.archiveMode && !*archiveExpiringImages {
   198  		request.IgnoreFilesystemIfExpiring = true
   199  	}
   200  	var reply imageserver.GetImageResponse
   201  	err = client.RequestReply("ImageServer.GetImage", request, &reply)
   202  	if err != nil {
   203  		client.Close()
   204  		return err
   205  	}
   206  	img := reply.Image
   207  	if img == nil {
   208  		return errors.New(name + ": not found")
   209  	}
   210  	logger.Println("downloaded image")
   211  	if t.archiveMode && !img.ExpiresAt.IsZero() && !*archiveExpiringImages {
   212  		logger.Println("ignoring expiring image in archiver mode")
   213  		return nil
   214  	}
   215  	img.FileSystem.RebuildInodePointers()
   216  	err = t.imageDataBase.DoWithPendingImage(img, func() error {
   217  		if err := t.getMissingObjects(img, client, logger); err != nil {
   218  			client.Close()
   219  			return err
   220  		}
   221  		err := t.imageDataBase.AddImage(img, name,
   222  			&srpc.AuthInformation{HaveMethodAccess: true})
   223  		if err != nil {
   224  			return err
   225  		}
   226  		return nil
   227  	})
   228  	if err != nil {
   229  		return err
   230  	}
   231  	logger.Println("added image")
   232  	return nil
   233  }
   234  
   235  func (t *srpcType) checkImageBeingInjected(name string) bool {
   236  	t.imagesBeingInjectedLock.Lock()
   237  	defer t.imagesBeingInjectedLock.Unlock()
   238  	_, ok := t.imagesBeingInjected[name]
   239  	return ok
   240  }
   241  
   242  func (t *srpcType) getMissingObjects(img *image.Image, client *srpc.Client,
   243  	logger log.DebugLogger) error {
   244  	objClient := objectclient.AttachObjectClient(client)
   245  	defer objClient.Close()
   246  	return img.GetMissingObjects(t.objSrv, objClient, logger)
   247  }