github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/upgrades/upgrade.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package upgrades
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/loggo"
    10  	"github.com/juju/version"
    11  )
    12  
    13  var logger = loggo.GetLogger("juju.upgrade")
    14  
    15  // Step defines an idempotent operation that is run to perform
    16  // a specific upgrade step.
    17  type Step interface {
    18  	// Description is a human readable description of what the upgrade step does.
    19  	Description() string
    20  
    21  	// Targets returns the target machine types for which the upgrade step is applicable.
    22  	Targets() []Target
    23  
    24  	// Run executes the upgrade business logic.
    25  	Run(Context) error
    26  }
    27  
    28  // Operation defines what steps to perform to upgrade to a target version.
    29  type Operation interface {
    30  	// The Juju version for which this operation is applicable.
    31  	// Upgrade operations designed for versions of Juju earlier
    32  	// than we are upgrading from are not run since such steps would
    33  	// already have been used to get to the version we are running now.
    34  	TargetVersion() version.Number
    35  
    36  	// Steps to perform during an upgrade.
    37  	Steps() []Step
    38  }
    39  
    40  // Target defines the type of machine for which a particular upgrade
    41  // step can be run.
    42  type Target string
    43  
    44  const (
    45  	// AllMachines applies to any machine.
    46  	AllMachines = Target("allMachines")
    47  
    48  	// HostMachine is a machine on which units are deployed.
    49  	HostMachine = Target("hostMachine")
    50  
    51  	// Controller is a machine participating in a Juju controller cluster.
    52  	Controller = Target("controller")
    53  
    54  	// DatabaseMaster is a Controller that has the master database, and as such
    55  	// is the only target that should run database schema upgrade steps.
    56  	DatabaseMaster = Target("databaseMaster")
    57  )
    58  
    59  // upgradeToVersion encapsulates the steps which need to be run to
    60  // upgrade any prior version of Juju to targetVersion.
    61  type upgradeToVersion struct {
    62  	targetVersion version.Number
    63  	steps         []Step
    64  }
    65  
    66  // Steps is defined on the Operation interface.
    67  func (u upgradeToVersion) Steps() []Step {
    68  	return u.steps
    69  }
    70  
    71  // TargetVersion is defined on the Operation interface.
    72  func (u upgradeToVersion) TargetVersion() version.Number {
    73  	return u.targetVersion
    74  }
    75  
    76  // upgradeError records a description of the step being performed and the error.
    77  type upgradeError struct {
    78  	description string
    79  	err         error
    80  }
    81  
    82  func (e *upgradeError) Error() string {
    83  	return fmt.Sprintf("%s: %v", e.description, e.err)
    84  }
    85  
    86  // AreUpgradesDefined returns true if there are upgrade operations
    87  // defined between the version supplied and the running software
    88  // version.
    89  func AreUpgradesDefined(from version.Number) bool {
    90  	return newUpgradeOpsIterator(from).Next() || newStateUpgradeOpsIterator(from).Next()
    91  }
    92  
    93  // PerformUpgrade runs the business logic needed to upgrade the current "from" version to this
    94  // version of Juju on the "target" type of machine.
    95  func PerformUpgrade(from version.Number, targets []Target, context Context) error {
    96  	if hasStateTarget(targets) {
    97  		ops := newStateUpgradeOpsIterator(from)
    98  		if err := runUpgradeSteps(ops, targets, context.StateContext()); err != nil {
    99  			return err
   100  		}
   101  	}
   102  
   103  	ops := newUpgradeOpsIterator(from)
   104  	if err := runUpgradeSteps(ops, targets, context.APIContext()); err != nil {
   105  		return err
   106  	}
   107  
   108  	logger.Infof("All upgrade steps completed successfully")
   109  	return nil
   110  }
   111  
   112  func hasStateTarget(targets []Target) bool {
   113  	for _, target := range targets {
   114  		if target == Controller || target == DatabaseMaster {
   115  			return true
   116  		}
   117  	}
   118  	return false
   119  }
   120  
   121  // runUpgradeSteps finds all the upgrade operations relevant to
   122  // the targets given and runs the associated upgrade steps.
   123  //
   124  // As soon as any error is encountered, the operation is aborted since
   125  // subsequent steps may required successful completion of earlier
   126  // ones. The steps must be idempotent so that the entire upgrade
   127  // operation can be retried.
   128  func runUpgradeSteps(ops *opsIterator, targets []Target, context Context) error {
   129  	for ops.Next() {
   130  		for _, step := range ops.Get().Steps() {
   131  			if targetsMatch(targets, step.Targets()) {
   132  				logger.Infof("running upgrade step: %v", step.Description())
   133  				if err := step.Run(context); err != nil {
   134  					logger.Errorf("upgrade step %q failed: %v", step.Description(), err)
   135  					return &upgradeError{
   136  						description: step.Description(),
   137  						err:         err,
   138  					}
   139  				}
   140  			}
   141  		}
   142  	}
   143  	return nil
   144  }
   145  
   146  // targetsMatch returns true if any machineTargets match any of
   147  // stepTargets.
   148  func targetsMatch(machineTargets []Target, stepTargets []Target) bool {
   149  	for _, machineTarget := range machineTargets {
   150  		for _, stepTarget := range stepTargets {
   151  			if machineTarget == stepTarget || stepTarget == AllMachines {
   152  				return true
   153  			}
   154  		}
   155  	}
   156  	return false
   157  }
   158  
   159  // upgradeStep is a default Step implementation.
   160  type upgradeStep struct {
   161  	description string
   162  	targets     []Target
   163  	run         func(Context) error
   164  }
   165  
   166  var _ Step = (*upgradeStep)(nil)
   167  
   168  // Description is defined on the Step interface.
   169  func (step *upgradeStep) Description() string {
   170  	return step.description
   171  }
   172  
   173  // Targets is defined on the Step interface.
   174  func (step *upgradeStep) Targets() []Target {
   175  	return step.targets
   176  }
   177  
   178  // Run is defined on the Step interface.
   179  func (step *upgradeStep) Run(context Context) error {
   180  	return step.run(context)
   181  }