github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/charm/baseselector_test.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 "github.com/juju/collections/set" 8 "github.com/juju/testing" 9 jc "github.com/juju/testing/checkers" 10 "go.uber.org/mock/gomock" 11 gc "gopkg.in/check.v1" 12 13 "github.com/juju/juju/core/base" 14 "github.com/juju/juju/version" 15 ) 16 17 type baseSelectorSuite struct { 18 testing.IsolationSuite 19 20 logger *MockSelectorLogger 21 cfg *MockSelectorModelConfig 22 } 23 24 var _ = gc.Suite(&baseSelectorSuite{}) 25 26 var ( 27 bionic = base.MustParseBaseFromString("ubuntu@18.04/stable") 28 cosmic = base.MustParseBaseFromString("ubuntu@18.10/stable") 29 disco = base.MustParseBaseFromString("ubuntu@19.04") 30 jammy = base.MustParseBaseFromString("ubuntu@22.04") 31 focal = base.MustParseBaseFromString("ubuntu@20.04") 32 precise = base.MustParseBaseFromString("ubuntu@14.04") 33 utopic = base.MustParseBaseFromString("ubuntu@16.10") 34 vivid = base.MustParseBaseFromString("ubuntu@17.04") 35 jujuDefault = version.DefaultSupportedLTSBase() 36 ) 37 38 func (s *baseSelectorSuite) setup(c *gc.C) *gomock.Controller { 39 ctrl := gomock.NewController(c) 40 s.logger = NewMockSelectorLogger(ctrl) 41 s.logger.EXPECT().Infof(gomock.Any(), gomock.Any()).AnyTimes() 42 s.logger.EXPECT().Tracef(gomock.Any(), gomock.Any()).AnyTimes() 43 44 s.cfg = NewMockSelectorModelConfig(ctrl) 45 46 return ctrl 47 } 48 49 func (s *baseSelectorSuite) TestCharmBase(c *gc.C) { 50 defer s.setup(c).Finish() 51 52 deployBasesTests := []struct { 53 title string 54 selector BaseSelector 55 expectedBase base.Base 56 err string 57 }{ 58 { 59 title: "juju deploy simple # default base set", 60 selector: BaseSelector{ 61 defaultBase: focal, 62 explicitDefaultBase: true, 63 supportedBases: []base.Base{bionic, focal}, 64 }, 65 expectedBase: focal, 66 }, 67 { 68 title: "juju deploy simple --base=ubuntu@14.04 # no supported base", 69 selector: BaseSelector{ 70 requestedBase: precise, 71 supportedBases: []base.Base{bionic, cosmic}, 72 }, 73 err: `base: ubuntu@14.04/stable`, 74 }, 75 { 76 title: "juju deploy simple --base=ubuntu@18.04 # user provided base takes precedence over default base ", 77 selector: BaseSelector{ 78 requestedBase: bionic, 79 defaultBase: focal, 80 explicitDefaultBase: true, 81 supportedBases: []base.Base{bionic, focal, cosmic}, 82 }, 83 expectedBase: bionic, 84 }, 85 86 // Now charms with supported base. 87 88 { 89 title: "juju deploy multiseries # use charm default, nothing specified, no default base", 90 selector: BaseSelector{ 91 supportedBases: []base.Base{bionic, cosmic}, 92 }, 93 expectedBase: bionic, 94 }, 95 { 96 title: "juju deploy multiseries # use charm defaults used if default base doesn't match, nothing specified", 97 selector: BaseSelector{ 98 supportedBases: []base.Base{bionic, cosmic}, 99 defaultBase: precise, 100 explicitDefaultBase: true, 101 }, 102 err: `base: ubuntu@14.04/stable`, 103 }, 104 { 105 title: "juju deploy multiseries # use model base defaults if supported by charm", 106 selector: BaseSelector{ 107 supportedBases: []base.Base{bionic, cosmic, disco}, 108 defaultBase: disco, 109 explicitDefaultBase: true, 110 }, 111 expectedBase: disco, 112 }, 113 { 114 title: "juju deploy multiseries --base=ubuntu@18.04 # use supported requested", 115 selector: BaseSelector{ 116 requestedBase: bionic, 117 supportedBases: []base.Base{cosmic, bionic}, 118 }, 119 expectedBase: bionic, 120 }, 121 { 122 title: "juju deploy multiseries --base=ubuntu@18.04 # unsupported requested", 123 selector: BaseSelector{ 124 requestedBase: bionic, 125 supportedBases: []base.Base{utopic, vivid}, 126 }, 127 err: `base: ubuntu@18.04/stable`, 128 }, 129 { 130 title: "juju deploy multiseries # fallback to version.DefaultSupportedLTSBase()", 131 selector: BaseSelector{ 132 supportedBases: []base.Base{utopic, vivid, jujuDefault}, 133 }, 134 expectedBase: jujuDefault, 135 }, 136 } 137 138 for i, test := range deployBasesTests { 139 c.Logf("test %d [%s]", i, test.title) 140 test.selector.logger = s.logger 141 base, err := test.selector.CharmBase() 142 if test.err != "" { 143 c.Check(err, gc.ErrorMatches, test.err) 144 } else { 145 c.Check(err, jc.ErrorIsNil) 146 c.Check(base.IsCompatible(test.expectedBase), jc.IsTrue, 147 gc.Commentf("%q compatible with %q", base, test.expectedBase)) 148 } 149 } 150 } 151 152 func (s *baseSelectorSuite) TestValidate(c *gc.C) { 153 defer s.setup(c).Finish() 154 155 deploySeriesTests := []struct { 156 title string 157 selector BaseSelector 158 supportedCharmBases []base.Base 159 supportedJujuBases []base.Base 160 err string 161 }{{ 162 // Simple selectors first, no supported bases, check we're validating 163 title: "juju deploy simple # no default base, no supported base", 164 selector: BaseSelector{}, 165 err: "charm does not define any bases, not valid", 166 }, { 167 title: "should fail when image-id constraint is used and no base is explicitly set", 168 selector: BaseSelector{ 169 usingImageID: true, 170 }, 171 err: "base must be explicitly provided when image-id constraint is used", 172 }, { 173 title: "should return no errors when using image-id and base flag", 174 selector: BaseSelector{ 175 requestedBase: jammy, 176 usingImageID: true, 177 }, 178 supportedJujuBases: []base.Base{jammy, bionic}, 179 supportedCharmBases: []base.Base{jammy, bionic}, 180 }, { 181 title: "should return no errors when using image-id and explicit base from Config", 182 selector: BaseSelector{ 183 explicitDefaultBase: true, 184 usingImageID: true, 185 }, 186 supportedJujuBases: []base.Base{jammy, bionic}, 187 supportedCharmBases: []base.Base{jammy, bionic}, 188 }, { 189 title: "juju deploy multiseries with invalid series # use charm default, nothing specified, no default base", 190 selector: BaseSelector{}, 191 supportedCharmBases: []base.Base{precise}, 192 supportedJujuBases: []base.Base{jammy}, 193 err: `the charm defined bases "ubuntu@14.04" not supported`, 194 }} 195 196 for i, test := range deploySeriesTests { 197 c.Logf("test %d [%s]", i, test.title) 198 test.selector.logger = s.logger 199 test.selector.jujuSupportedBases = set.NewStrings() 200 _, err := test.selector.validate(test.supportedCharmBases, test.supportedJujuBases) 201 if test.err != "" { 202 c.Check(err, gc.ErrorMatches, test.err) 203 } else { 204 c.Check(err, jc.ErrorIsNil) 205 } 206 } 207 } 208 209 func (s *baseSelectorSuite) TestConfigureBaseSelector(c *gc.C) { 210 defer s.setup(c).Finish() 211 212 s.cfg.EXPECT().DefaultBase() 213 cfg := SelectorConfig{ 214 Config: s.cfg, 215 Force: false, 216 Logger: s.logger, 217 RequestedBase: base.Base{}, 218 SupportedCharmBases: []base.Base{jammy, focal, bionic}, 219 WorkloadBases: []base.Base{jammy, focal}, 220 UsingImageID: false, 221 } 222 223 obtained, err := ConfigureBaseSelector(cfg) 224 c.Assert(err, jc.ErrorIsNil) 225 c.Check(obtained.supportedBases, gc.DeepEquals, []base.Base{jammy, focal}) 226 } 227 228 func (s *baseSelectorSuite) TestConfigureBaseSelectorCentos(c *gc.C) { 229 defer s.setup(c).Finish() 230 231 s.cfg.EXPECT().DefaultBase() 232 c7 := base.MustParseBaseFromString("centos@7/stable") 233 c8 := base.MustParseBaseFromString("centos@8/stable") 234 c6 := base.MustParseBaseFromString("centos@6/stable") 235 cfg := SelectorConfig{ 236 Config: s.cfg, 237 Force: false, 238 Logger: s.logger, 239 RequestedBase: base.Base{}, 240 SupportedCharmBases: []base.Base{c6, c7, c8}, 241 WorkloadBases: []base.Base{c7, c8}, 242 UsingImageID: false, 243 } 244 245 obtained, err := ConfigureBaseSelector(cfg) 246 c.Assert(err, jc.ErrorIsNil) 247 c.Check(obtained.supportedBases, gc.DeepEquals, []base.Base{c7, c8}) 248 } 249 250 func (s *baseSelectorSuite) TestConfigureBaseSelectorDefaultBase(c *gc.C) { 251 defer s.setup(c).Finish() 252 253 s.cfg.EXPECT().DefaultBase().Return("ubuntu@20.04", true) 254 cfg := SelectorConfig{ 255 Config: s.cfg, 256 Force: false, 257 Logger: s.logger, 258 RequestedBase: base.Base{}, 259 SupportedCharmBases: []base.Base{jammy, focal, bionic}, 260 WorkloadBases: []base.Base{jammy, focal}, 261 UsingImageID: false, 262 } 263 264 baseSelector, err := ConfigureBaseSelector(cfg) 265 c.Assert(err, jc.ErrorIsNil) 266 c.Check(baseSelector.supportedBases, jc.SameContents, []base.Base{jammy, focal}) 267 c.Check(baseSelector.defaultBase, gc.DeepEquals, focal) 268 c.Check(baseSelector.explicitDefaultBase, jc.IsTrue) 269 270 obtained, err := baseSelector.CharmBase() 271 c.Assert(err, jc.ErrorIsNil) 272 expectedBase := base.MustParseBaseFromString("ubuntu@20.04") 273 c.Check(obtained.IsCompatible(expectedBase), jc.IsTrue, gc.Commentf("obtained: %q, expected %q", obtained, expectedBase)) 274 } 275 276 func (s *baseSelectorSuite) TestConfigureBaseSelectorDefaultBaseFail(c *gc.C) { 277 defer s.setup(c).Finish() 278 279 s.cfg.EXPECT().DefaultBase().Return("ubuntu@18.04", true) 280 cfg := SelectorConfig{ 281 Config: s.cfg, 282 Force: false, 283 Logger: s.logger, 284 RequestedBase: base.Base{}, 285 SupportedCharmBases: []base.Base{jammy, focal, bionic}, 286 WorkloadBases: []base.Base{jammy, focal}, 287 UsingImageID: false, 288 } 289 290 baseSelector, err := ConfigureBaseSelector(cfg) 291 c.Assert(err, jc.ErrorIsNil) 292 _, err = baseSelector.CharmBase() 293 c.Assert(err, gc.ErrorMatches, `base: ubuntu@18.04/stable`) 294 }