github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 "gopkg.in/juju/charm.v6" 8 9 "github.com/juju/juju/juju/version" 10 ) 11 12 const ( 13 msgUserRequestedSeries = "with the user specified series %q" 14 msgBundleSeries = "with the series %q defined by the bundle" 15 msgDefaultCharmSeries = "with the default charm metadata series %q" 16 msgDefaultModelSeries = "with the configured model default series %q" 17 msgLatestLTSSeries = "with the latest LTS series %q" 18 ) 19 20 type modelConfig interface { 21 DefaultSeries() (string, bool) 22 } 23 24 // seriesSelector is a helper type that determines what series the charm should 25 // be deployed to. 26 // 27 // TODO: This type should really have a Validate method, as the force flag is 28 // really only valid if the seriesFlag is specified. There is code and tests 29 // that allow the force flag when series isn't specified, but they should 30 // really be cleaned up. The `deploy` CLI command has tests to ensure that 31 // --force is only valid with --series. 32 type seriesSelector struct { 33 // seriesFlag is the series passed to the --series flag on the command line. 34 seriesFlag string 35 // charmURLSeries is the series specified as part of the charm URL, i.e. 36 // cs:trusty/ubuntu. 37 charmURLSeries string 38 // conf is the configuration for the model we're deploying to. 39 conf modelConfig 40 // supportedSeries is the list of series the charm supports. 41 supportedSeries []string 42 // force indicates the user explicitly wants to deploy to a requested 43 // series, regardless of whether the charm says it supports that series. 44 force bool 45 // from bundle specifies the deploy request comes from a bundle spec. 46 fromBundle bool 47 } 48 49 // charmSeries determines what series to use with a charm. 50 // Order of preference is: 51 // - user requested with --series or defined by bundle when deploying 52 // - user requested in charm's url (e.g. juju deploy precise/ubuntu) 53 // - model default (if it matches supported series) 54 // - default from charm metadata supported series / series in url 55 // - default LTS 56 func (s seriesSelector) charmSeries() (selectedSeries string, err error) { 57 // User has requested a series with --series. 58 if s.seriesFlag != "" { 59 return s.userRequested(s.seriesFlag) 60 } 61 62 // User specified a series in the charm URL, e.g. 63 // juju deploy precise/ubuntu. 64 if s.charmURLSeries != "" { 65 return s.userRequested(s.charmURLSeries) 66 } 67 68 // No series explicitly requested by the user. 69 // Use model default series, if explicitly set and supported by the charm. 70 if defaultSeries, explicit := s.conf.DefaultSeries(); explicit { 71 if _, err := charm.SeriesForCharm(defaultSeries, s.supportedSeries); err == nil { 72 logger.Infof(msgDefaultModelSeries, defaultSeries) 73 return defaultSeries, nil 74 } 75 } 76 77 // Use the charm's perferred series, if it has one. In a multi-series 78 // charm, the first series in the list is the preferred one. 79 defaultSeries, err := charm.SeriesForCharm("", s.supportedSeries) 80 if err == nil { 81 return defaultSeries, nil 82 } 83 84 // Charm hasn't specified a default (likely due to being a local charm 85 // deployed by path). Last chance, best we can do is default to LTS. 86 87 // At this point, because we have no idea what series the charm supports, 88 // *everything* requires --force. 89 if !s.force { 90 // We know err is not nil due to above, so return the error 91 // returned to us from the charm call. 92 return "", err 93 } 94 95 latestLTS := version.SupportedLTS() 96 logger.Infof(msgLatestLTSSeries, latestLTS) 97 return latestLTS, nil 98 } 99 100 // userRequested checks the series the user has requested, and returns it if it 101 // is supported, or if they used --force. 102 func (s seriesSelector) userRequested(requestedSeries string) (string, error) { 103 series, err := charm.SeriesForCharm(requestedSeries, s.supportedSeries) 104 if s.force { 105 series = requestedSeries 106 } else if err != nil { 107 return "", err 108 } 109 110 // either it's a supported series or the user used --force, so just 111 // give them what they asked for. 112 if s.fromBundle { 113 logger.Infof(msgBundleSeries, series) 114 return series, nil 115 } 116 logger.Infof(msgUserRequestedSeries, series) 117 return series, nil 118 }