github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/core/charm/computedbase.go (about)

     1  // Copyright 2023 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package charm
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/juju/charm/v12"
    11  	"github.com/juju/collections/set"
    12  	"github.com/juju/collections/transform"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/loggo"
    15  
    16  	"github.com/juju/juju/core/base"
    17  )
    18  
    19  var logger = loggo.GetLogger("juju.core.charm")
    20  
    21  // BaseForCharm takes a requested base and a list of bases supported by a
    22  // charm and returns the base which is relevant.
    23  // If the requested base is empty, then the first supported base is used,
    24  // otherwise the requested base is validated against the supported bases.
    25  func BaseForCharm(requestedBase base.Base, supportedBases []base.Base) (base.Base, error) {
    26  	// Old local charm with no supported bases, use the
    27  	// requestedBase. If none specified error.
    28  	if len(supportedBases) == 0 {
    29  		if requestedBase.Empty() {
    30  			return base.Base{}, MissingBaseError
    31  		}
    32  		return requestedBase, nil
    33  	}
    34  	// Use the charm default.
    35  	if requestedBase.Empty() {
    36  		return supportedBases[0], nil
    37  	}
    38  	for _, s := range supportedBases {
    39  		if s.IsCompatible(requestedBase) {
    40  			return requestedBase, nil
    41  		}
    42  	}
    43  	return base.Base{}, NewUnsupportedBaseError(requestedBase, supportedBases)
    44  }
    45  
    46  // MissingBaseError is used to denote that BaseForCharm could not determine
    47  // a base because a legacy charm did not declare any.
    48  var MissingBaseError = errors.ConstError("charm does not define any bases")
    49  
    50  // ComputedBases of a charm, preserving legacy behaviour. For charms prior to v2,
    51  // fall back the metadata series can convert to bases
    52  func ComputedBases(c charm.CharmMeta) ([]base.Base, error) {
    53  	manifest := c.Manifest()
    54  	if manifest != nil {
    55  		computedBases := make([]base.Base, len(manifest.Bases))
    56  		for i, b := range manifest.Bases {
    57  			computedBase, err := base.ParseBase(b.Name, b.Channel.String())
    58  			if err != nil {
    59  				return nil, errors.Trace(err)
    60  			}
    61  			computedBases[i] = computedBase
    62  		}
    63  		return computedBases, nil
    64  	}
    65  	if charm.MetaFormat(c) < charm.FormatV2 {
    66  		return transform.SliceOrErr(c.Meta().Series, func(s string) (base.Base, error) {
    67  			if s == base.Kubernetes.String() {
    68  				return base.LegacyKubernetesBase(), nil
    69  			}
    70  			return base.GetBaseFromSeries(s)
    71  		})
    72  	}
    73  	return []base.Base{}, nil
    74  }
    75  
    76  // BaseIsCompatibleWithCharm returns nil if the provided charm is compatible
    77  // with the provided base. Otherwise, return an UnsupportedBaseError
    78  func BaseIsCompatibleWithCharm(b base.Base, c charm.CharmMeta) error {
    79  	supportedBases, err := ComputedBases(c)
    80  	if err != nil {
    81  		return errors.Trace(err)
    82  	}
    83  	_, err = BaseForCharm(b, supportedBases)
    84  	return err
    85  }
    86  
    87  // OSIsCompatibleWithCharm returns nil is any of the bases the charm supports
    88  // has an os which matched the provided os. Otherwise, return a NotSupported error
    89  func OSIsCompatibleWithCharm(os string, c charm.CharmMeta) error {
    90  	supportedBases, err := ComputedBases(c)
    91  	if err != nil {
    92  		return errors.Trace(err)
    93  	}
    94  	if len(supportedBases) == 0 {
    95  		return MissingBaseError
    96  	}
    97  	oses := set.NewStrings(transform.Slice(supportedBases, func(b base.Base) string { return b.OS })...)
    98  	if oses.Contains(os) {
    99  		return nil
   100  	}
   101  	osesStr := strings.Join(oses.SortedValues(), "")
   102  	errStr := fmt.Sprintf("OS %q not supported by charm %q, supported OSes are: %s", os, c.Meta().Name, osesStr)
   103  	return errors.NewNotSupported(nil, errStr)
   104  }