// charmSeries determine what series to use with a charm. // Order of preference is: // - user requested or defined by bundle when deploying // - default from charm metadata supported series // - model default // - charm store default func charmSeries( requestedSeries, seriesFromCharm string, supportedSeries []string, force bool, conf *config.Config, fromBundle bool, ) (string, string, error) { // User has requested a series and we have a new charm with series in metadata. if requestedSeries != "" && seriesFromCharm == "" { if !force && !isSeriesSupported(requestedSeries, supportedSeries) { return "", "", charm.NewUnsupportedSeriesError(requestedSeries, supportedSeries) } if fromBundle { return requestedSeries, msgBundleSeries, nil } else { return requestedSeries, msgUserRequestedSeries, nil } } // User has requested a series and it's an old charm for a single series. if seriesFromCharm != "" { if !force && requestedSeries != "" && requestedSeries != seriesFromCharm { return "", "", charm.NewUnsupportedSeriesError(requestedSeries, []string{seriesFromCharm}) } if requestedSeries != "" { if fromBundle { return requestedSeries, msgBundleSeries, nil } else { return requestedSeries, msgUserRequestedSeries, nil } } return seriesFromCharm, msgSingleCharmSeries, nil } // Use charm default. if len(supportedSeries) > 0 { return supportedSeries[0], msgDefaultCharmSeries, nil } // Use model default supported series. if defaultSeries, ok := conf.DefaultSeries(); ok { if !force && !isSeriesSupported(defaultSeries, supportedSeries) { return "", "", charm.NewUnsupportedSeriesError(defaultSeries, supportedSeries) } return defaultSeries, msgDefaultModelSeries, nil } // Use latest LTS. latestLtsSeries := config.LatestLtsSeries() if !force && !isSeriesSupported(latestLtsSeries, supportedSeries) { return "", "", charm.NewUnsupportedSeriesError(latestLtsSeries, supportedSeries) } return latestLtsSeries, msgLatestLTSSeries, nil }
// SeriesToUpload returns the supplied series with duplicates removed if // non-empty; otherwise it returns a default list of series we should // probably upload, based on cfg. func SeriesToUpload(cfg *config.Config, series []string) []string { unique := set.NewStrings(series...) if unique.IsEmpty() { unique.Add(version.Current.Series) for _, toolsSeries := range ToolsLtsSeries { unique.Add(toolsSeries) } if series, ok := cfg.DefaultSeries(); ok { unique.Add(series) } } return unique.SortedValues() }
func resolveCharm( resolveWithChannel func(*charm.URL) (*charm.URL, csparams.Channel, []string, error), conf *config.Config, url *charm.URL, ) (*charm.URL, csparams.Channel, []string, error) { if url.Schema != "cs" { return nil, csparams.NoChannel, nil, errors.Errorf("unknown schema for charm URL %q", url) } // If the user hasn't explicitly asked for a particular series, // query for the charm that matches the model's default series. // If this fails, we'll fall back to asking for whatever charm is available. defaultedSeries := false if url.Series == "" { if s, ok := conf.DefaultSeries(); ok { defaultedSeries = true // TODO(katco): Don't update the value passed in. Not only // is there no indication that this method will do so, we // return a charm.URL which signals to the developer that // we don't modify the original. url.Series = s } } resultURL, channel, supportedSeries, err := resolveWithChannel(url) if defaultedSeries && errors.Cause(err) == csparams.ErrNotFound { // we tried to use the model's default the series, but the store said it doesn't exist. // retry without the defaulted series, to take what we can get. url.Series = "" resultURL, channel, supportedSeries, err = resolveWithChannel(url) } if err != nil { return nil, csparams.NoChannel, nil, errors.Trace(err) } if resultURL.Series != "" && len(supportedSeries) == 0 { supportedSeries = []string{resultURL.Series} } return resultURL, channel, supportedSeries, nil }
// resolveCharmURL returns a resolved charm URL, given a charm location string. // If the series is not resolved, the environment default-series is used, or if // not set, the series is resolved with the state server. func resolveCharmURL(url string, client *api.Client, conf *config.Config) (*charm.URL, error) { ref, series, err := charm.ParseReference(url) if err != nil { return nil, err } // If series is not set, use configured default series if series == "" { if defaultSeries, ok := conf.DefaultSeries(); ok { series = defaultSeries } } // Otherwise, look up the best supported series for this charm if series == "" { if ref.Schema == "local" { possibleUrl := &charm.URL{Reference: ref, Series: "precise"} logger.Errorf(`The series is not specified in the environment (default-series) or with the charm. Did you mean: %s`, possibleUrl.String()) return nil, fmt.Errorf("cannot resolve series for charm: %q", ref) } return client.ResolveCharm(ref) } return &charm.URL{Reference: ref, Series: series}, nil }
// resolveCharmURL returns a resolved charm URL, given a charm location string. // If the series is not resolved, the environment default-series is used, or if // not set, the series is resolved with the state server. func resolveCharmURL(url string, client *api.Client, conf *config.Config) (*charm.URL, error) { ref, err := charm.ParseReference(url) if err != nil { return nil, err } // If series is not set, use configured default series if ref.Series == "" { if defaultSeries, ok := conf.DefaultSeries(); ok { ref.Series = defaultSeries } } if ref.Series != "" { return ref.URL("") } // Otherwise, look up the best supported series for this charm if ref.Schema != "local" { return client.ResolveCharm(ref) } possibleURL := *ref possibleURL.Series = "precise" logger.Errorf("The series is not specified in the environment (default-series) or with the charm. Did you mean:\n\t%s", &possibleURL) return nil, fmt.Errorf("cannot resolve series for charm: %q", ref) }
// resolveCharmURL resolves the given charm URL string // by looking it up in the appropriate charm repository. // If it is a charm store charm URL, the given csParams will // be used to access the charm store repository. // If it is a local charm URL, the local charm repository at // the given repoPath will be used. The given configuration // will be used to add any necessary attributes to the repo // and to resolve the default series if possible. // // resolveCharmURL also returns the charm repository holding // the charm. func resolveCharmURL(curlStr string, csParams charmrepo.NewCharmStoreParams, repoPath string, conf *config.Config) (*charm.URL, charmrepo.Interface, error) { ref, err := charm.ParseReference(curlStr) if err != nil { return nil, nil, errors.Trace(err) } repo, err := charmrepo.InferRepository(ref, csParams, repoPath) if err != nil { return nil, nil, errors.Trace(err) } repo = config.SpecializeCharmRepo(repo, conf) if ref.Series == "" { if defaultSeries, ok := conf.DefaultSeries(); ok { ref.Series = defaultSeries } } if ref.Schema == "local" && ref.Series == "" { possibleURL := *ref possibleURL.Series = "trusty" logger.Errorf("The series is not specified in the environment (default-series) or with the charm. Did you mean:\n\t%s", &possibleURL) return nil, nil, errors.Errorf("cannot resolve series for charm: %q", ref) } if ref.Series != "" && ref.Revision != -1 { // The URL is already fully resolved; do not // bother with an unnecessary round-trip to the // charm store. curl, err := ref.URL("") if err != nil { panic(err) } return curl, repo, nil } curl, err := repo.Resolve(ref) if err != nil { return nil, nil, errors.Trace(err) } return curl, repo, nil }