github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/juju/application/series_selector.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package application
     5  
     6  import (
     7  	"github.com/juju/utils/series"
     8  	"gopkg.in/juju/charm.v6-unstable"
     9  )
    10  
    11  const (
    12  	msgUserRequestedSeries = "with the user specified series %q"
    13  	msgBundleSeries        = "with the series %q defined by the bundle"
    14  	msgDefaultCharmSeries  = "with the default charm metadata series %q"
    15  	msgDefaultModelSeries  = "with the configured model default series %q"
    16  	msgLatestLTSSeries     = "with the latest LTS series %q"
    17  )
    18  
    19  type modelConfig interface {
    20  	DefaultSeries() (string, bool)
    21  }
    22  
    23  // seriesSelector is a helper type that determines what series the charm should
    24  // be deployed to.
    25  type seriesSelector struct {
    26  	// seriesFlag is the series passed to the --series flag on the command line.
    27  	seriesFlag string
    28  	// charmURLSeries is the series specified as part of the charm URL, i.e.
    29  	// cs:trusty/ubuntu.
    30  	charmURLSeries string
    31  	// conf is the configuration for the model we're deploying to.
    32  	conf modelConfig
    33  	// supportedSeries is the list of series the charm supports.
    34  	supportedSeries []string
    35  	// force indicates the user explicitly wants to deploy to a requested
    36  	// series, regardless of whether the charm says it supports that series.
    37  	force bool
    38  	// from bundle specifies the deploy request comes from a bundle spec.
    39  	fromBundle bool
    40  }
    41  
    42  // charmSeries determines what series to use with a charm.
    43  // Order of preference is:
    44  // - user requested with --series or defined by bundle when deploying
    45  // - user requested in charm's url (e.g. juju deploy precise/ubuntu)
    46  // - model default (if it matches supported series)
    47  // - default from charm metadata supported series / series in url
    48  // - default LTS
    49  func (s seriesSelector) charmSeries() (selectedSeries string, err error) {
    50  	// User has requested a series with --series.
    51  	if s.seriesFlag != "" {
    52  		return s.userRequested(s.seriesFlag)
    53  	}
    54  
    55  	// User specified a series in the charm URL, e.g.
    56  	// juju deploy precise/ubuntu.
    57  	if s.charmURLSeries != "" {
    58  		return s.userRequested(s.charmURLSeries)
    59  	}
    60  
    61  	// No series explicitly requested by the user.
    62  	// Use model default series, if explicitly set and supported by the charm.
    63  	if defaultSeries, explicit := s.conf.DefaultSeries(); explicit {
    64  		if isSeriesSupported(defaultSeries, s.supportedSeries) {
    65  			logger.Infof(msgDefaultModelSeries, defaultSeries)
    66  			return defaultSeries, nil
    67  		}
    68  	}
    69  
    70  	// Use the charm's perferred series, if it has one.  In a multi-series
    71  	// charm, the first series in the list is the preferred one.
    72  	if len(s.supportedSeries) > 0 {
    73  		logger.Infof(msgDefaultCharmSeries, s.supportedSeries[0])
    74  		return s.supportedSeries[0], nil
    75  	}
    76  
    77  	// Charm hasn't specified a default (likely due to being a local charm
    78  	// deployed by path).  Last chance, best we can do is default to LTS.
    79  
    80  	// At this point, because we have no idea what series the charm supports,
    81  	// *everything* requires --force.
    82  	if !s.force {
    83  		return "", s.unsupportedSeries(series.LatestLts())
    84  	}
    85  
    86  	latestLTS := series.LatestLts()
    87  	logger.Infof(msgLatestLTSSeries, latestLTS)
    88  	return latestLTS, nil
    89  }
    90  
    91  // userRequested checks the series the user has requested, and returns it if it
    92  // is supported, or if they used --force.
    93  func (s seriesSelector) userRequested(series string) (selectedSeries string, err error) {
    94  	if !s.force && !isSeriesSupported(series, s.supportedSeries) {
    95  		return "", s.unsupportedSeries(series)
    96  	}
    97  
    98  	// either it's a supported series or the user used --force, so just
    99  	// give them what they asked for.
   100  	if s.fromBundle {
   101  		logger.Infof(msgBundleSeries, series)
   102  		return series, nil
   103  	}
   104  	logger.Infof(msgUserRequestedSeries, series)
   105  	return series, nil
   106  }
   107  
   108  func (s seriesSelector) unsupportedSeries(series string) error {
   109  	supp := s.supportedSeries
   110  	if len(supp) == 0 {
   111  		supp = []string{"<none defined>"}
   112  	}
   113  	return charm.NewUnsupportedSeriesError(series, supp)
   114  }