github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/jujud/agent/model/manifolds.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package model
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/juju/api"
    10  	"github.com/juju/utils/clock"
    11  	"github.com/juju/utils/voyeur"
    12  
    13  	coreagent "github.com/juju/juju/agent"
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/cmd/jujud/agent/engine"
    16  	"github.com/juju/juju/core/life"
    17  	"github.com/juju/juju/environs"
    18  	"github.com/juju/juju/worker"
    19  	"github.com/juju/juju/worker/agent"
    20  	"github.com/juju/juju/worker/apicaller"
    21  	"github.com/juju/juju/worker/apiconfigwatcher"
    22  	"github.com/juju/juju/worker/applicationscaler"
    23  	"github.com/juju/juju/worker/charmrevision"
    24  	"github.com/juju/juju/worker/charmrevision/charmrevisionmanifold"
    25  	"github.com/juju/juju/worker/cleaner"
    26  	"github.com/juju/juju/worker/dependency"
    27  	"github.com/juju/juju/worker/discoverspaces"
    28  	"github.com/juju/juju/worker/environ"
    29  	"github.com/juju/juju/worker/firewaller"
    30  	"github.com/juju/juju/worker/fortress"
    31  	"github.com/juju/juju/worker/gate"
    32  	"github.com/juju/juju/worker/instancepoller"
    33  	"github.com/juju/juju/worker/lifeflag"
    34  	"github.com/juju/juju/worker/machineundertaker"
    35  	"github.com/juju/juju/worker/metricworker"
    36  	"github.com/juju/juju/worker/migrationflag"
    37  	"github.com/juju/juju/worker/migrationmaster"
    38  	"github.com/juju/juju/worker/provisioner"
    39  	"github.com/juju/juju/worker/singular"
    40  	"github.com/juju/juju/worker/statushistorypruner"
    41  	"github.com/juju/juju/worker/storageprovisioner"
    42  	"github.com/juju/juju/worker/undertaker"
    43  	"github.com/juju/juju/worker/unitassigner"
    44  )
    45  
    46  // ManifoldsConfig holds the dependencies and configuration options for a
    47  // model agent: that is, for the set of interdependent workers that observe
    48  // and manipulate a single model.
    49  type ManifoldsConfig struct {
    50  
    51  	// Agent identifies, and exposes configuration for, the controller
    52  	// machine running these manifolds and the model the manifolds
    53  	// should administer.
    54  	//
    55  	// You should almost certainly set this value to one created by
    56  	// model.WrapAgent.
    57  	Agent coreagent.Agent
    58  
    59  	// AgentConfigChanged will be set whenever the agent's api config
    60  	// is updated
    61  	AgentConfigChanged *voyeur.Value
    62  
    63  	// Clock supplies timing services to any manifolds that need them.
    64  	// Only a few workers have been converted to use them fo far.
    65  	Clock clock.Clock
    66  
    67  	// InstPollerAggregationDelay is the delay before sending a batch of
    68  	// requests in the instancpoller.Worker's aggregate loop.
    69  	InstPollerAggregationDelay time.Duration
    70  
    71  	// RunFlagDuration defines for how long this controller will ask
    72  	// for model administration rights; most of the workers controlled
    73  	// by this agent will only be started when the run flag is known
    74  	// to be held.
    75  	RunFlagDuration time.Duration
    76  
    77  	// CharmRevisionUpdateInterval determines how often the charm-
    78  	// revision worker will check for new revisions of known charms.
    79  	CharmRevisionUpdateInterval time.Duration
    80  
    81  	// StatusHistoryPruner* values control status-history pruning
    82  	// behaviour.
    83  	StatusHistoryPrunerMaxHistoryTime time.Duration
    84  	StatusHistoryPrunerMaxHistoryMB   uint
    85  	StatusHistoryPrunerInterval       time.Duration
    86  
    87  	// SpacesImportedGate will be unlocked when spaces are known to
    88  	// have been imported.
    89  	SpacesImportedGate gate.Lock
    90  
    91  	// NewEnvironFunc is a function opens a provider "environment"
    92  	// (typically environs.New).
    93  	NewEnvironFunc environs.NewEnvironFunc
    94  
    95  	// NewMigrationMaster is called to create a new migrationmaster
    96  	// worker.
    97  	NewMigrationMaster func(migrationmaster.Config) (worker.Worker, error)
    98  }
    99  
   100  // Manifolds returns a set of interdependent dependency manifolds that will
   101  // run together to administer a model, as configured.
   102  func Manifolds(config ManifoldsConfig) dependency.Manifolds {
   103  	modelTag := config.Agent.CurrentConfig().Model()
   104  	return dependency.Manifolds{
   105  
   106  		// The first group are foundational; the agent and clock
   107  		// which wrap those supplied in config, and the api-caller
   108  		// through which everything else communicates with the
   109  		// controller.
   110  		agentName: agent.Manifold(config.Agent),
   111  		clockName: clockManifold(config.Clock),
   112  		apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{
   113  			AgentName:          agentName,
   114  			AgentConfigChanged: config.AgentConfigChanged,
   115  		}),
   116  		apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{
   117  			AgentName:     agentName,
   118  			APIOpen:       api.Open,
   119  			NewConnection: apicaller.OnlyConnect,
   120  			Filter:        apiConnectFilter,
   121  		}),
   122  
   123  		// The spaces-imported gate will be unlocked when space
   124  		// discovery is known to be complete. Various manifolds
   125  		// should also come to depend upon it (or rather, on a
   126  		// Flag depending on it) in the future.
   127  		spacesImportedGateName: gate.ManifoldEx(config.SpacesImportedGate),
   128  
   129  		// All other manifolds should depend on at least one of these
   130  		// three, which handle all the tasks that are safe and sane
   131  		// to run in *all* controller machines.
   132  		notDeadFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{
   133  			APICallerName: apiCallerName,
   134  			Entity:        modelTag,
   135  			Result:        life.IsNotDead,
   136  			Filter:        LifeFilter,
   137  
   138  			NewFacade: lifeflag.NewFacade,
   139  			NewWorker: lifeflag.NewWorker,
   140  		}),
   141  		notAliveFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{
   142  			APICallerName: apiCallerName,
   143  			Entity:        modelTag,
   144  			Result:        life.IsNotAlive,
   145  			Filter:        LifeFilter,
   146  
   147  			NewFacade: lifeflag.NewFacade,
   148  			NewWorker: lifeflag.NewWorker,
   149  		}),
   150  		isResponsibleFlagName: singular.Manifold(singular.ManifoldConfig{
   151  			ClockName:     clockName,
   152  			AgentName:     agentName,
   153  			APICallerName: apiCallerName,
   154  			Duration:      config.RunFlagDuration,
   155  
   156  			NewFacade: singular.NewFacade,
   157  			NewWorker: singular.NewWorker,
   158  		}),
   159  
   160  		// The migration workers collaborate to run migrations;
   161  		// and to create a mechanism for running other workers
   162  		// so they can't accidentally interfere with a migration
   163  		// in progress. Such a manifold should (1) depend on the
   164  		// migration-inactive flag, to know when to start or die;
   165  		// and (2) occupy the migration-fortress, so as to avoid
   166  		// possible interference with the minion (which will not
   167  		// take action until it's gained sole control of the
   168  		// fortress).
   169  		//
   170  		// Note that the fortress and flag will only exist while
   171  		// the model is not dead; this frees their dependencies
   172  		// from model-lifetime concerns.
   173  		migrationFortressName: ifNotDead(fortress.Manifold()),
   174  		migrationInactiveFlagName: ifNotDead(migrationflag.Manifold(migrationflag.ManifoldConfig{
   175  			APICallerName: apiCallerName,
   176  			Check:         migrationflag.IsTerminal,
   177  			NewFacade:     migrationflag.NewFacade,
   178  			NewWorker:     migrationflag.NewWorker,
   179  		})),
   180  		migrationMasterName: ifNotDead(migrationmaster.Manifold(migrationmaster.ManifoldConfig{
   181  			AgentName:     agentName,
   182  			APICallerName: apiCallerName,
   183  			FortressName:  migrationFortressName,
   184  			Clock:         config.Clock,
   185  			NewFacade:     migrationmaster.NewFacade,
   186  			NewWorker:     config.NewMigrationMaster,
   187  		})),
   188  
   189  		// Everything else should be wrapped in ifResponsible,
   190  		// ifNotAlive, ifNotDead, or ifNotMigrating (which also
   191  		// implies NotDead), to ensure that only a single
   192  		// controller is attempting to administer this model at
   193  		// any one time.
   194  		//
   195  		// NOTE: not perfectly reliable at this stage? i.e. a
   196  		// worker that ignores its stop signal for "too long"
   197  		// might continue to take admin actions after the window
   198  		// of responsibility closes. This *is* a pre-existing
   199  		// problem, but demands some thought/care: e.g. should
   200  		// we make sure the apiserver also closes any
   201  		// connections that lose responsibility..? can we make
   202  		// sure all possible environ operations are either time-
   203  		// bounded or interruptible? etc
   204  		//
   205  		// On the other hand, all workers *should* be written in
   206  		// the expectation of dealing with sucky infrastructure
   207  		// running things in parallel unexpectedly, just because
   208  		// the universe hates us and will engineer matters such
   209  		// that it happens sometimes, even when we try to avoid
   210  		// it.
   211  
   212  		// The environ tracker could/should be used by several other
   213  		// workers (firewaller, provisioners, address-cleaner?).
   214  		environTrackerName: ifResponsible(environ.Manifold(environ.ManifoldConfig{
   215  			APICallerName:  apiCallerName,
   216  			NewEnvironFunc: config.NewEnvironFunc,
   217  		})),
   218  
   219  		// The undertaker is currently the only ifNotAlive worker.
   220  		undertakerName: ifNotAlive(undertaker.Manifold(undertaker.ManifoldConfig{
   221  			APICallerName: apiCallerName,
   222  			EnvironName:   environTrackerName,
   223  
   224  			NewFacade: undertaker.NewFacade,
   225  			NewWorker: undertaker.NewWorker,
   226  		})),
   227  
   228  		// All the rest depend on ifNotMigrating.
   229  		spaceImporterName: ifNotMigrating(discoverspaces.Manifold(discoverspaces.ManifoldConfig{
   230  			EnvironName:   environTrackerName,
   231  			APICallerName: apiCallerName,
   232  			UnlockerName:  spacesImportedGateName,
   233  
   234  			NewFacade: discoverspaces.NewFacade,
   235  			NewWorker: discoverspaces.NewWorker,
   236  		})),
   237  		computeProvisionerName: ifNotMigrating(provisioner.Manifold(provisioner.ManifoldConfig{
   238  			AgentName:          agentName,
   239  			APICallerName:      apiCallerName,
   240  			EnvironName:        environTrackerName,
   241  			NewProvisionerFunc: provisioner.NewEnvironProvisioner,
   242  		})),
   243  		storageProvisionerName: ifNotMigrating(storageprovisioner.ModelManifold(storageprovisioner.ModelManifoldConfig{
   244  			APICallerName: apiCallerName,
   245  			ClockName:     clockName,
   246  			EnvironName:   environTrackerName,
   247  			Scope:         modelTag,
   248  		})),
   249  		firewallerName: ifNotMigrating(firewaller.Manifold(firewaller.ManifoldConfig{
   250  			APICallerName: apiCallerName,
   251  		})),
   252  		unitAssignerName: ifNotMigrating(unitassigner.Manifold(unitassigner.ManifoldConfig{
   253  			APICallerName: apiCallerName,
   254  		})),
   255  		applicationScalerName: ifNotMigrating(applicationscaler.Manifold(applicationscaler.ManifoldConfig{
   256  			APICallerName: apiCallerName,
   257  			NewFacade:     applicationscaler.NewFacade,
   258  			NewWorker:     applicationscaler.New,
   259  		})),
   260  		instancePollerName: ifNotMigrating(instancepoller.Manifold(instancepoller.ManifoldConfig{
   261  			APICallerName: apiCallerName,
   262  			EnvironName:   environTrackerName,
   263  			ClockName:     clockName,
   264  			Delay:         config.InstPollerAggregationDelay,
   265  		})),
   266  		charmRevisionUpdaterName: ifNotMigrating(charmrevisionmanifold.Manifold(charmrevisionmanifold.ManifoldConfig{
   267  			APICallerName: apiCallerName,
   268  			ClockName:     clockName,
   269  			Period:        config.CharmRevisionUpdateInterval,
   270  
   271  			NewFacade: charmrevisionmanifold.NewAPIFacade,
   272  			NewWorker: charmrevision.NewWorker,
   273  		})),
   274  		metricWorkerName: ifNotMigrating(metricworker.Manifold(metricworker.ManifoldConfig{
   275  			APICallerName: apiCallerName,
   276  		})),
   277  		stateCleanerName: ifNotMigrating(cleaner.Manifold(cleaner.ManifoldConfig{
   278  			APICallerName: apiCallerName,
   279  		})),
   280  		statusHistoryPrunerName: ifNotMigrating(statushistorypruner.Manifold(statushistorypruner.ManifoldConfig{
   281  			APICallerName:  apiCallerName,
   282  			MaxHistoryTime: config.StatusHistoryPrunerMaxHistoryTime,
   283  			MaxHistoryMB:   config.StatusHistoryPrunerMaxHistoryMB,
   284  			PruneInterval:  config.StatusHistoryPrunerInterval,
   285  			// TODO(fwereade): 2016-03-17 lp:1558657
   286  			NewTimer: worker.NewTimer,
   287  		})),
   288  		machineUndertakerName: ifNotMigrating(machineundertaker.Manifold(machineundertaker.ManifoldConfig{
   289  			APICallerName: apiCallerName,
   290  			EnvironName:   environTrackerName,
   291  			NewWorker:     machineundertaker.NewWorker,
   292  		})),
   293  	}
   294  }
   295  
   296  // clockManifold expresses a Clock as a ValueWorker manifold.
   297  func clockManifold(clock clock.Clock) dependency.Manifold {
   298  	return dependency.Manifold{
   299  		Start: func(_ dependency.Context) (worker.Worker, error) {
   300  			return engine.NewValueWorker(clock)
   301  		},
   302  		Output: engine.ValueWorkerOutput,
   303  	}
   304  }
   305  
   306  func apiConnectFilter(err error) error {
   307  	// If the model is no longer there, then convert to ErrRemoved so
   308  	// that the dependency engine for the model is stopped.
   309  	// See http://pad.lv/1614809
   310  	if params.IsCodeModelNotFound(err) {
   311  		return ErrRemoved
   312  	}
   313  	return err
   314  }
   315  
   316  var (
   317  	// ifResponsible wraps a manifold such that it only runs if the
   318  	// responsibility flag is set.
   319  	ifResponsible = engine.Housing{
   320  		Flags: []string{
   321  			isResponsibleFlagName,
   322  		},
   323  	}.Decorate
   324  
   325  	// ifNotAlive wraps a manifold such that it only runs if the
   326  	// responsibility flag is set and the model is Dying or Dead.
   327  	ifNotAlive = engine.Housing{
   328  		Flags: []string{
   329  			isResponsibleFlagName,
   330  			notAliveFlagName,
   331  		},
   332  	}.Decorate
   333  
   334  	// ifNotDead wraps a manifold such that it only runs if the
   335  	// responsibility flag is set and the model is Alive or Dying.
   336  	ifNotDead = engine.Housing{
   337  		Flags: []string{
   338  			isResponsibleFlagName,
   339  			notDeadFlagName,
   340  		},
   341  	}.Decorate
   342  
   343  	// ifNotMigrating wraps a manifold such that it only runs if the
   344  	// migration-inactive flag is set; and then runs workers only
   345  	// within Visits to the migration fortress. To avoid redundancy,
   346  	// it takes advantage of the fact that those migration manifolds
   347  	// themselves depend on ifNotDead, and eschews repeating those
   348  	// dependencies.
   349  	ifNotMigrating = engine.Housing{
   350  		Flags: []string{
   351  			migrationInactiveFlagName,
   352  		},
   353  		Occupy: migrationFortressName,
   354  	}.Decorate
   355  )
   356  
   357  const (
   358  	agentName            = "agent"
   359  	clockName            = "clock"
   360  	apiConfigWatcherName = "api-config-watcher"
   361  	apiCallerName        = "api-caller"
   362  
   363  	spacesImportedGateName = "spaces-imported-gate"
   364  	isResponsibleFlagName  = "is-responsible-flag"
   365  	notDeadFlagName        = "not-dead-flag"
   366  	notAliveFlagName       = "not-alive-flag"
   367  
   368  	migrationFortressName     = "migration-fortress"
   369  	migrationInactiveFlagName = "migration-inactive-flag"
   370  	migrationMasterName       = "migration-master"
   371  
   372  	environTrackerName       = "environ-tracker"
   373  	undertakerName           = "undertaker"
   374  	spaceImporterName        = "space-importer"
   375  	computeProvisionerName   = "compute-provisioner"
   376  	storageProvisionerName   = "storage-provisioner"
   377  	firewallerName           = "firewaller"
   378  	unitAssignerName         = "unit-assigner"
   379  	applicationScalerName    = "application-scaler"
   380  	instancePollerName       = "instance-poller"
   381  	charmRevisionUpdaterName = "charm-revision-updater"
   382  	metricWorkerName         = "metric-worker"
   383  	stateCleanerName         = "state-cleaner"
   384  	statusHistoryPrunerName  = "status-history-pruner"
   385  	machineUndertakerName    = "machine-undertaker"
   386  )