github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/utils/clock"
    10  	"github.com/juju/utils/voyeur"
    11  
    12  	coreagent "github.com/juju/juju/agent"
    13  	"github.com/juju/juju/cmd/jujud/agent/util"
    14  	"github.com/juju/juju/core/life"
    15  	"github.com/juju/juju/environs"
    16  	"github.com/juju/juju/worker"
    17  	"github.com/juju/juju/worker/addresser"
    18  	"github.com/juju/juju/worker/agent"
    19  	"github.com/juju/juju/worker/apicaller"
    20  	"github.com/juju/juju/worker/apiconfigwatcher"
    21  	"github.com/juju/juju/worker/charmrevision"
    22  	"github.com/juju/juju/worker/charmrevision/charmrevisionmanifold"
    23  	"github.com/juju/juju/worker/cleaner"
    24  	"github.com/juju/juju/worker/dependency"
    25  	"github.com/juju/juju/worker/discoverspaces"
    26  	"github.com/juju/juju/worker/environ"
    27  	"github.com/juju/juju/worker/firewaller"
    28  	"github.com/juju/juju/worker/fortress"
    29  	"github.com/juju/juju/worker/gate"
    30  	"github.com/juju/juju/worker/instancepoller"
    31  	"github.com/juju/juju/worker/lifeflag"
    32  	"github.com/juju/juju/worker/metricworker"
    33  	"github.com/juju/juju/worker/migrationmaster"
    34  	"github.com/juju/juju/worker/provisioner"
    35  	"github.com/juju/juju/worker/servicescaler"
    36  	"github.com/juju/juju/worker/singular"
    37  	"github.com/juju/juju/worker/statushistorypruner"
    38  	"github.com/juju/juju/worker/storageprovisioner"
    39  	"github.com/juju/juju/worker/undertaker"
    40  	"github.com/juju/juju/worker/unitassigner"
    41  )
    42  
    43  // ManifoldsConfig holds the dependencies and configuration options for a
    44  // model agent: that is, for the set of interdependent workers that observe
    45  // and manipulate a single model.
    46  type ManifoldsConfig struct {
    47  
    48  	// Agent identifies, and exposes configuration for, the controller
    49  	// machine running these manifolds and the model the manifolds
    50  	// should administer.
    51  	//
    52  	// You should almost certainly set this value to one created by
    53  	// model.WrapAgent.
    54  	Agent coreagent.Agent
    55  
    56  	// AgentConfigChanged will be set whenever the agent's api config
    57  	// is updated
    58  	AgentConfigChanged *voyeur.Value
    59  
    60  	// Clock supplies timing services to any manifolds that need them.
    61  	// Only a few workers have been converted to use them fo far.
    62  	Clock clock.Clock
    63  
    64  	// RunFlagDuration defines for how long this controller will ask
    65  	// for model administration rights; most of the workers controlled
    66  	// by this agent will only be started when the run flag is known
    67  	// to be held.
    68  	RunFlagDuration time.Duration
    69  
    70  	// CharmRevisionUpdateInterval determines how often the charm-
    71  	// revision worker will check for new revisions of known charms.
    72  	CharmRevisionUpdateInterval time.Duration
    73  
    74  	// EntityStatusHistory* values control status-history pruning
    75  	// behaviour per entity.
    76  	EntityStatusHistoryCount    uint
    77  	EntityStatusHistoryInterval time.Duration
    78  
    79  	// SpacesImportedGate will be unlocked when spaces are known to
    80  	// have been imported.
    81  	SpacesImportedGate gate.Lock
    82  }
    83  
    84  // Manifolds returns a set of interdependent dependency manifolds that will
    85  // run together to administer a model, as configured.
    86  func Manifolds(config ManifoldsConfig) dependency.Manifolds {
    87  	modelTag := config.Agent.CurrentConfig().Model()
    88  	return dependency.Manifolds{
    89  
    90  		// The first group are foundational; the agent and clock
    91  		// which wrap those supplied in config, and the api-caller
    92  		// through which everything else communicates with the
    93  		// controller.
    94  		agentName: agent.Manifold(config.Agent),
    95  		clockName: clockManifold(config.Clock),
    96  		apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{
    97  			AgentName:          agentName,
    98  			AgentConfigChanged: config.AgentConfigChanged,
    99  		}),
   100  		apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{
   101  			AgentName:     agentName,
   102  			APIOpen:       apicaller.APIOpen,
   103  			NewConnection: apicaller.OnlyConnect,
   104  		}),
   105  
   106  		// The spaces-imported gate will be unlocked when space
   107  		// discovery is known to be complete. Various manifolds
   108  		// should also come to depend upon it (or rather, on a
   109  		// Flag depending on it) in the future.
   110  		spacesImportedGateName: gate.ManifoldEx(config.SpacesImportedGate),
   111  
   112  		// All other manifolds should depend on at least one of these
   113  		// three, which handle all the tasks that are safe and sane
   114  		// to run in *all* controller machines.
   115  		notDeadFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{
   116  			APICallerName: apiCallerName,
   117  			Entity:        modelTag,
   118  			Result:        life.IsNotDead,
   119  			Filter:        lifeFilter,
   120  
   121  			NewFacade: lifeflag.NewFacade,
   122  			NewWorker: lifeflag.NewWorker,
   123  		}),
   124  		notAliveFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{
   125  			APICallerName: apiCallerName,
   126  			Entity:        modelTag,
   127  			Result:        life.IsNotAlive,
   128  			Filter:        lifeFilter,
   129  
   130  			NewFacade: lifeflag.NewFacade,
   131  			NewWorker: lifeflag.NewWorker,
   132  		}),
   133  		isResponsibleFlagName: singular.Manifold(singular.ManifoldConfig{
   134  			ClockName:     clockName,
   135  			AgentName:     agentName,
   136  			APICallerName: apiCallerName,
   137  			Duration:      config.RunFlagDuration,
   138  
   139  			NewFacade: singular.NewFacade,
   140  			NewWorker: singular.NewWorker,
   141  		}),
   142  
   143  		migrationFortressName: ifNotDead(fortress.Manifold()),
   144  		migrationMasterName: ifNotDead(migrationmaster.Manifold(migrationmaster.ManifoldConfig{
   145  			APICallerName: apiCallerName,
   146  			FortressName:  migrationFortressName,
   147  
   148  			NewFacade: migrationmaster.NewFacade,
   149  			NewWorker: migrationmaster.NewWorker,
   150  		})),
   151  
   152  		// Everything else should be wrapped in ifResponsible,
   153  		// ifNotAlive, or ifNotDead, to ensure that only a single
   154  		// controller is administering this model at a time.
   155  		//
   156  		// NOTE: not perfectly reliable at this stage? i.e. a worker
   157  		// that ignores its stop signal for "too long" might continue
   158  		// to take admin actions after the window of responsibility
   159  		// closes. This *is* a pre-existing problem, but demands some
   160  		// thought/care: e.g. should we make sure the apiserver also
   161  		// closes any connections that lose responsibility..? can we
   162  		// make sure all possible environ operations are either time-
   163  		// bounded or interruptible? etc
   164  		//
   165  		// On the other hand, all workers *should* be written in the
   166  		// expectation of dealing with a sucky infrastructure running
   167  		// things in parallel unexpectedly, just because the universe
   168  		// hates us and will engineer matters such that it happens
   169  		// sometimes, even when we try to avoid it.
   170  
   171  		// The environ tracker could/should be used by several other
   172  		// workers (firewaller, provisioners, address-cleaner?).
   173  		environTrackerName: ifResponsible(environ.Manifold(environ.ManifoldConfig{
   174  			APICallerName:  apiCallerName,
   175  			NewEnvironFunc: environs.New,
   176  		})),
   177  
   178  		// The undertaker is currently the only ifNotAlive worker.
   179  		undertakerName: ifNotAlive(undertaker.Manifold(undertaker.ManifoldConfig{
   180  			APICallerName: apiCallerName,
   181  			EnvironName:   environTrackerName,
   182  
   183  			NewFacade: undertaker.NewFacade,
   184  			NewWorker: undertaker.NewWorker,
   185  		})),
   186  
   187  		// All the rest depend on ifNotDead.
   188  		spaceImporterName: ifNotDead(discoverspaces.Manifold(discoverspaces.ManifoldConfig{
   189  			EnvironName:   environTrackerName,
   190  			APICallerName: apiCallerName,
   191  			UnlockerName:  spacesImportedGateName,
   192  
   193  			NewFacade: discoverspaces.NewFacade,
   194  			NewWorker: discoverspaces.NewWorker,
   195  		})),
   196  
   197  		computeProvisionerName: ifNotDead(provisioner.Manifold(provisioner.ManifoldConfig{
   198  			AgentName:     agentName,
   199  			APICallerName: apiCallerName,
   200  		})),
   201  		storageProvisionerName: ifNotDead(storageprovisioner.ModelManifold(storageprovisioner.ModelManifoldConfig{
   202  			APICallerName: apiCallerName,
   203  			ClockName:     clockName,
   204  			Scope:         modelTag,
   205  		})),
   206  		firewallerName: ifNotDead(firewaller.Manifold(firewaller.ManifoldConfig{
   207  			APICallerName: apiCallerName,
   208  		})),
   209  		unitAssignerName: ifNotDead(unitassigner.Manifold(unitassigner.ManifoldConfig{
   210  			APICallerName: apiCallerName,
   211  		})),
   212  		serviceScalerName: ifNotDead(servicescaler.Manifold(servicescaler.ManifoldConfig{
   213  			APICallerName: apiCallerName,
   214  			NewFacade:     servicescaler.NewFacade,
   215  			NewWorker:     servicescaler.New,
   216  		})),
   217  		instancePollerName: ifNotDead(instancepoller.Manifold(instancepoller.ManifoldConfig{
   218  			APICallerName: apiCallerName,
   219  			EnvironName:   environTrackerName,
   220  		})),
   221  		charmRevisionUpdaterName: ifNotDead(charmrevisionmanifold.Manifold(charmrevisionmanifold.ManifoldConfig{
   222  			APICallerName: apiCallerName,
   223  			ClockName:     clockName,
   224  			Period:        config.CharmRevisionUpdateInterval,
   225  
   226  			NewFacade: charmrevisionmanifold.NewAPIFacade,
   227  			NewWorker: charmrevision.NewWorker,
   228  		})),
   229  		metricWorkerName: ifNotDead(metricworker.Manifold(metricworker.ManifoldConfig{
   230  			APICallerName: apiCallerName,
   231  		})),
   232  		stateCleanerName: ifNotDead(cleaner.Manifold(cleaner.ManifoldConfig{
   233  			APICallerName: apiCallerName,
   234  		})),
   235  		addressCleanerName: ifNotDead(addresser.Manifold(addresser.ManifoldConfig{
   236  			APICallerName: apiCallerName,
   237  		})),
   238  		statusHistoryPrunerName: ifNotDead(statushistorypruner.Manifold(statushistorypruner.ManifoldConfig{
   239  			APICallerName:    apiCallerName,
   240  			MaxLogsPerEntity: config.EntityStatusHistoryCount,
   241  			PruneInterval:    config.EntityStatusHistoryInterval,
   242  			// TODO(fwereade): 2016-03-17 lp:1558657
   243  			NewTimer: worker.NewTimer,
   244  		})),
   245  	}
   246  }
   247  
   248  // clockManifold expresses a Clock as a ValueWorker manifold.
   249  func clockManifold(clock clock.Clock) dependency.Manifold {
   250  	return dependency.Manifold{
   251  		Start: func(_ dependency.Context) (worker.Worker, error) {
   252  			return util.NewValueWorker(clock)
   253  		},
   254  		Output: util.ValueWorkerOutput,
   255  	}
   256  }
   257  
   258  var (
   259  	// ifResponsible wraps a manifold such that it only runs if the
   260  	// responsibility flag is set.
   261  	ifResponsible = util.Housing{
   262  		Flags: []string{
   263  			isResponsibleFlagName,
   264  		},
   265  	}.Decorate
   266  
   267  	// ifNotAlive wraps a manifold such that it only runs if the
   268  	// responsibility flag is set and the model is Dying or Dead.
   269  	ifNotAlive = util.Housing{
   270  		Flags: []string{
   271  			isResponsibleFlagName,
   272  			notAliveFlagName,
   273  		},
   274  	}.Decorate
   275  
   276  	// ifNotDead wraps a manifold such that it only runs if the
   277  	// responsibility flag is set and the model is Alive or Dying.
   278  	ifNotDead = util.Housing{
   279  		Flags: []string{
   280  			isResponsibleFlagName,
   281  			notDeadFlagName,
   282  		},
   283  	}.Decorate
   284  )
   285  
   286  const (
   287  	agentName            = "agent"
   288  	clockName            = "clock"
   289  	apiConfigWatcherName = "api-config-watcher"
   290  	apiCallerName        = "api-caller"
   291  
   292  	spacesImportedGateName = "spaces-imported-gate"
   293  	isResponsibleFlagName  = "is-responsible-flag"
   294  	notDeadFlagName        = "not-dead-flag"
   295  	notAliveFlagName       = "not-alive-flag"
   296  
   297  	migrationFortressName = "migration-fortress"
   298  	migrationMasterName   = "migration-master"
   299  
   300  	environTrackerName       = "environ-tracker"
   301  	undertakerName           = "undertaker"
   302  	spaceImporterName        = "space-importer"
   303  	computeProvisionerName   = "compute-provisioner"
   304  	storageProvisionerName   = "storage-provisioner"
   305  	firewallerName           = "firewaller"
   306  	unitAssignerName         = "unit-assigner"
   307  	serviceScalerName        = "service-scaler"
   308  	instancePollerName       = "instance-poller"
   309  	charmRevisionUpdaterName = "charm-revision-updater"
   310  	metricWorkerName         = "metric-worker"
   311  	stateCleanerName         = "state-cleaner"
   312  	addressCleanerName       = "address-cleaner"
   313  	statusHistoryPrunerName  = "status-history-pruner"
   314  )