github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/provisioner/containerworker.go (about)

     1  // Copyright 2022 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner
     5  
     6  import (
     7  	"fmt"
     8  	"sync"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/worker/v3"
    12  	"github.com/juju/worker/v3/catacomb"
    13  
    14  	"github.com/juju/juju/core/watcher"
    15  )
    16  
    17  // NewContainerSetupAndProvisioner returns a ContainerSetupAndProvisioner.
    18  func NewContainerSetupAndProvisioner(cs *ContainerSetup, getContainerWatcherFunc GetContainerWatcherFunc) (worker.Worker, error) {
    19  	containerWatcher, err := getContainerWatcherFunc()
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  	w := &ContainerSetupAndProvisioner{
    24  		catacomb:         catacomb.Catacomb{},
    25  		containerWatcher: containerWatcher,
    26  		logger:           cs.logger,
    27  		cs:               cs,
    28  	}
    29  
    30  	if err := catacomb.Invoke(catacomb.Plan{
    31  		Site: &w.catacomb,
    32  		Work: w.work,
    33  		Init: []worker.Worker{w.containerWatcher},
    34  	}); err != nil {
    35  		return nil, errors.Trace(err)
    36  	}
    37  	return w, nil
    38  }
    39  
    40  // ContainerSetupAndProvisioner is a worker that waits for a container of type
    41  // defined in its config to be found for deployment. Then initializes
    42  // the container system and starts a container provisioner of that type.
    43  type ContainerSetupAndProvisioner struct {
    44  	catacomb catacomb.Catacomb
    45  
    46  	cs *ContainerSetup
    47  
    48  	containerWatcher watcher.StringsWatcher
    49  	logger           Logger
    50  	provisioner      Provisioner
    51  
    52  	// For introspection Report
    53  	mu sync.Mutex
    54  }
    55  
    56  func (w *ContainerSetupAndProvisioner) work() error {
    57  	// Wait for a container of w.ContainerType type to be
    58  	// found.
    59  	if err := w.waitForContainers(); err != nil {
    60  		return err
    61  	}
    62  	if err := w.checkDying(); err != nil {
    63  		return err
    64  	}
    65  
    66  	// The container watcher is no longer needed
    67  	if err := worker.Stop(w.containerWatcher); err != nil {
    68  		return err
    69  	}
    70  	// For introspection Report
    71  	w.mu.Lock()
    72  	w.containerWatcher = nil
    73  	w.mu.Unlock()
    74  
    75  	// Set up w.ContainerType provisioning dependencies
    76  	// on this machine.
    77  	if err := w.cs.initialiseContainers(w.catacomb.Dying()); err != nil {
    78  		return err
    79  	}
    80  	if err := w.checkDying(); err != nil {
    81  		return err
    82  	}
    83  
    84  	// Configure and Add the w.ContainerType Provisioner
    85  	provisioner, err := w.cs.initialiseContainerProvisioner()
    86  	if err != nil {
    87  		return err
    88  	}
    89  	if err := w.checkDying(); err != nil {
    90  		return err
    91  	}
    92  	if err := w.catacomb.Add(provisioner); err != nil {
    93  		return err
    94  	}
    95  
    96  	// For introspection Report
    97  	w.mu.Lock()
    98  	w.provisioner = provisioner
    99  	w.mu.Unlock()
   100  
   101  	// The container provisioner is now doing all the work, sit and wait
   102  	// to be shutdown.
   103  	select {
   104  	case <-w.catacomb.Dying():
   105  		return w.catacomb.ErrDying()
   106  	}
   107  }
   108  
   109  // checkDying, returns an error if this worker's catacomb
   110  // is dying. Needed as the work is not done in a single work.
   111  func (w *ContainerSetupAndProvisioner) checkDying() error {
   112  	select {
   113  	case <-w.catacomb.Dying():
   114  		return w.catacomb.ErrDying()
   115  	default:
   116  		return nil
   117  	}
   118  }
   119  
   120  // waitForContainers waits for a container of the type
   121  // configured in this worker to be deployed and returns.
   122  func (w *ContainerSetupAndProvisioner) waitForContainers() error {
   123  	for {
   124  		select {
   125  		case <-w.catacomb.Dying():
   126  			return w.catacomb.ErrDying()
   127  		case containerIds, ok := <-w.containerWatcher.Changes():
   128  			if !ok {
   129  				return errors.New("container watcher closed")
   130  			}
   131  			if len(containerIds) == 0 {
   132  				continue
   133  			}
   134  			return nil
   135  		}
   136  	}
   137  }
   138  
   139  // Kill is part of the worker.Worker interface.
   140  func (w *ContainerSetupAndProvisioner) Kill() {
   141  	w.catacomb.Kill(nil)
   142  }
   143  
   144  // Wait is part of the worker.Worker interface.
   145  func (w *ContainerSetupAndProvisioner) Wait() error {
   146  	return w.catacomb.Wait()
   147  }
   148  
   149  // Report provides information for the engine report.
   150  func (w *ContainerSetupAndProvisioner) Report() map[string]interface{} {
   151  	w.mu.Lock()
   152  
   153  	result := make(map[string]interface{}, 0)
   154  
   155  	if w.containerWatcher != nil {
   156  		watcherName := fmt.Sprintf("%s-container-watcher", string(w.cs.containerType))
   157  		result[watcherName] = fmt.Sprintf("waiting for containers")
   158  	}
   159  	if w.provisioner != nil {
   160  		provisionerName := fmt.Sprintf("%s-provisioner", string(w.cs.containerType))
   161  		result[provisionerName] = fmt.Sprintf("setup and running")
   162  	}
   163  
   164  	w.mu.Unlock()
   165  	return result
   166  }