github.com/Cloud-Foundations/Dominator@v0.3.4/dom/images/impl.go (about)

     1  package images
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/Cloud-Foundations/Dominator/imageserver/client"
     7  	"github.com/Cloud-Foundations/Dominator/lib/image"
     8  	"github.com/Cloud-Foundations/Dominator/lib/log"
     9  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    10  	"github.com/Cloud-Foundations/Dominator/lib/stringutil"
    11  )
    12  
    13  func newManager(imageServerAddress string, logger log.Logger) *Manager {
    14  	imageInterestChannel := make(chan map[string]struct{})
    15  	imageRequestChannel := make(chan string)
    16  	imageExpireChannel := make(chan string, 16)
    17  	m := &Manager{
    18  		imageServerAddress:   imageServerAddress,
    19  		logger:               logger,
    20  		deduper:              stringutil.NewStringDeduplicator(false),
    21  		imageInterestChannel: imageInterestChannel,
    22  		imageRequestChannel:  imageRequestChannel,
    23  		imageExpireChannel:   imageExpireChannel,
    24  		imagesByName:         make(map[string]*image.Image),
    25  		missingImages:        make(map[string]error),
    26  	}
    27  	go m.manager(imageInterestChannel, imageRequestChannel, imageExpireChannel)
    28  	return m
    29  }
    30  
    31  func (m *Manager) getNoWait(name string) (*image.Image, error) {
    32  	m.RLock()
    33  	defer m.RUnlock()
    34  	if image := m.imagesByName[name]; image != nil {
    35  		return image, nil
    36  	}
    37  	if err, ok := m.missingImages[name]; ok {
    38  		return nil, err
    39  	}
    40  	return nil, nil
    41  }
    42  
    43  func (m *Manager) getWait(name string) (*image.Image, error) {
    44  	if image, err := m.getNoWait(name); err != nil {
    45  		return nil, err
    46  	} else if image != nil {
    47  		return image, nil
    48  	}
    49  	m.imageRequestChannel <- name
    50  	m.imageRequestChannel <- ""
    51  	return m.getNoWait(name)
    52  }
    53  
    54  func (m *Manager) setImageInterestList(images map[string]struct{}, wait bool) {
    55  	delete(images, "")
    56  	m.imageInterestChannel <- images
    57  	if wait {
    58  		m.imageRequestChannel <- ""
    59  	}
    60  }
    61  
    62  func (m *Manager) manager(imageInterestChannel <-chan map[string]struct{},
    63  	imageRequestChannel <-chan string,
    64  	imageExpireChannel <-chan string) {
    65  	var imageClient *srpc.Client
    66  	timer := time.NewTimer(time.Second)
    67  	for {
    68  		select {
    69  		case imageList := <-imageInterestChannel:
    70  			imageClient = m.setInterest(imageClient, imageList)
    71  		case name := <-imageRequestChannel:
    72  			if name == "" {
    73  				continue
    74  			}
    75  			imageClient = m.requestImage(imageClient, name)
    76  		case name := <-imageExpireChannel:
    77  			m.Lock()
    78  			delete(m.imagesByName, name)
    79  			m.missingImages[name] = nil // Try to get it again (expire extended)
    80  			m.Unlock()
    81  			m.rebuildDeDuper()
    82  		case <-timer.C:
    83  			// Loop over missing (pending) images. First obtain a copy.
    84  			missingImages := make(map[string]struct{})
    85  			m.RLock()
    86  			for name := range m.missingImages {
    87  				missingImages[name] = struct{}{}
    88  			}
    89  			m.RUnlock()
    90  			for name := range missingImages {
    91  				imageClient = m.requestImage(imageClient, name)
    92  			}
    93  		}
    94  		m.RLock()
    95  		if len(m.missingImages) > 0 {
    96  			timer.Reset(time.Second)
    97  		}
    98  		m.RUnlock()
    99  	}
   100  }
   101  
   102  func (m *Manager) setInterest(imageClient *srpc.Client,
   103  	imageList map[string]struct{}) *srpc.Client {
   104  	for name := range imageList {
   105  		imageClient = m.requestImage(imageClient, name)
   106  	}
   107  	deletedSome := false
   108  	// Clean up unreferenced images.
   109  	for name := range m.imagesByName {
   110  		if _, ok := imageList[name]; !ok {
   111  			m.Lock()
   112  			delete(m.imagesByName, name)
   113  			m.Unlock()
   114  			deletedSome = true
   115  		}
   116  	}
   117  	for name := range m.missingImages {
   118  		if _, ok := imageList[name]; !ok {
   119  			m.Lock()
   120  			delete(m.missingImages, name)
   121  			m.Unlock()
   122  		}
   123  	}
   124  	if deletedSome {
   125  		m.rebuildDeDuper()
   126  	}
   127  	return imageClient
   128  }
   129  
   130  func (m *Manager) requestImage(imageClient *srpc.Client,
   131  	name string) *srpc.Client {
   132  	if _, ok := m.imagesByName[name]; ok {
   133  		return imageClient
   134  	}
   135  	var img *image.Image
   136  	var err error
   137  	imageClient, img, err = m.loadImage(imageClient, name)
   138  	m.Lock()
   139  	defer m.Unlock()
   140  	if img != nil && err == nil {
   141  		delete(m.missingImages, name)
   142  		m.imagesByName[name] = img
   143  		return imageClient
   144  	}
   145  	delete(m.imagesByName, name)
   146  	m.missingImages[name] = err
   147  	return imageClient
   148  }
   149  
   150  func (m *Manager) loadImage(imageClient *srpc.Client, name string) (
   151  	*srpc.Client, *image.Image, error) {
   152  	if imageClient == nil {
   153  		var err error
   154  		imageClient, err = srpc.DialHTTP("tcp", m.imageServerAddress, 0)
   155  		if err != nil {
   156  			if !m.loggedDialFailure {
   157  				m.logger.Printf("Error dialing: %s: %s\n",
   158  					m.imageServerAddress, err)
   159  				m.loggedDialFailure = true
   160  			}
   161  			return nil, nil, err
   162  		}
   163  	}
   164  	img, err := client.GetImage(imageClient, name)
   165  	if err != nil {
   166  		m.logger.Printf("Error calling: %s\n", err)
   167  		imageClient.Close()
   168  		return nil, nil, err
   169  	}
   170  	if img == nil || m.scheduleExpiration(img, name) {
   171  		return imageClient, nil, nil
   172  	}
   173  	if err := img.FileSystem.RebuildInodePointers(); err != nil {
   174  		m.logger.Printf("Error building inode pointers for image: %s %s",
   175  			name, err)
   176  		return imageClient, nil, err
   177  	}
   178  	img.ReplaceStrings(m.deduper.DeDuplicate)
   179  	img.FileSystem = img.FileSystem.Filter(img.Filter) // Apply filter.
   180  	// Build cache data now to avoid potential concurrent builds later.
   181  	img.FileSystem.InodeToFilenamesTable()
   182  	img.FileSystem.FilenameToInodeTable()
   183  	img.FileSystem.HashToInodesTable()
   184  	img.FileSystem.ComputeTotalDataBytes()
   185  	img.FileSystem.BuildEntryMap()
   186  	m.logger.Printf("Got image: %s\n", name)
   187  	return imageClient, img, nil
   188  }
   189  
   190  func (m *Manager) rebuildDeDuper() {
   191  	for _, image := range m.imagesByName {
   192  		image.RegisterStrings(m.deduper.Register)
   193  	}
   194  	m.deduper.DeleteUnregistered()
   195  }
   196  
   197  func (m *Manager) scheduleExpiration(img *image.Image, name string) bool {
   198  	if img.ExpiresAt.IsZero() {
   199  		return false
   200  	}
   201  	duration := img.ExpiresAt.Sub(time.Now())
   202  	if duration <= 0 {
   203  		return true
   204  	}
   205  	time.AfterFunc(duration, func() {
   206  		m.logger.Printf("Auto expiring (deleting) image: %s\n", name)
   207  		m.imageExpireChannel <- name
   208  	})
   209  	return false
   210  }