github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/caas/specs/base_test.go (about) 1 // Copyright 2019 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package specs_test 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 gc "gopkg.in/check.v1" 9 10 "github.com/juju/juju/caas/specs" 11 "github.com/juju/juju/testing" 12 ) 13 14 type baseSuite struct { 15 testing.BaseSuite 16 } 17 18 var _ = gc.Suite(&baseSuite{}) 19 20 type validator interface { 21 Validate() error 22 } 23 24 type validateTc struct { 25 spec validator 26 errStr string 27 } 28 29 func (s *baseSuite) TestGetVersion(c *gc.C) { 30 type testcase struct { 31 strSpec string 32 version specs.Version 33 } 34 35 for i, tc := range []testcase{ 36 { 37 strSpec: ` 38 `[1:], 39 version: specs.Version(0), 40 }, 41 { 42 strSpec: ` 43 version: 0 44 `[1:], 45 version: specs.Version(0), 46 }, 47 { 48 strSpec: ` 49 version: 2 50 `[1:], 51 version: specs.Version(2), 52 }, 53 { 54 strSpec: ` 55 version: 3 56 `[1:], 57 version: specs.Version(3), 58 }, 59 } { 60 c.Logf("#%d: testing GetVersion: %d", i, tc.version) 61 v, err := specs.GetVersion(tc.strSpec) 62 c.Check(err, jc.ErrorIsNil) 63 c.Check(v, gc.DeepEquals, tc.version) 64 } 65 } 66 67 func (s *baseSuite) TestValidateServiceSpec(c *gc.C) { 68 spec := specs.ServiceSpec{ 69 ScalePolicy: "bar", 70 } 71 c.Assert(spec.Validate(), gc.ErrorMatches, `scale policy "bar" not supported`) 72 73 spec = specs.ServiceSpec{ 74 ScalePolicy: "parallel", 75 } 76 c.Assert(spec.Validate(), jc.ErrorIsNil) 77 78 spec = specs.ServiceSpec{ 79 ScalePolicy: "serial", 80 } 81 c.Assert(spec.Validate(), jc.ErrorIsNil) 82 83 spec = specs.ServiceSpec{ 84 UpdateStrategy: &specs.UpdateStrategy{ 85 Type: "Recreate", 86 RollingUpdate: &specs.RollingUpdateSpec{ 87 MaxUnavailable: &specs.IntOrString{Type: specs.String, StrVal: "10%"}, 88 MaxSurge: &specs.IntOrString{Type: specs.String, StrVal: "25%"}, 89 }, 90 }, 91 } 92 c.Assert(spec.Validate(), jc.ErrorIsNil) 93 94 spec = specs.ServiceSpec{ 95 UpdateStrategy: &specs.UpdateStrategy{ 96 Type: "", 97 RollingUpdate: &specs.RollingUpdateSpec{ 98 MaxUnavailable: &specs.IntOrString{Type: specs.String, StrVal: "10%"}, 99 MaxSurge: &specs.IntOrString{Type: specs.String, StrVal: "25%"}, 100 }, 101 }, 102 } 103 c.Assert(spec.Validate(), gc.ErrorMatches, `type is required`) 104 105 spec = specs.ServiceSpec{ 106 UpdateStrategy: &specs.UpdateStrategy{ 107 Type: "Recreate", 108 }, 109 } 110 c.Assert(spec.Validate(), jc.ErrorIsNil) 111 112 var partition int32 = 3 113 spec = specs.ServiceSpec{ 114 UpdateStrategy: &specs.UpdateStrategy{ 115 Type: "Recreate", 116 RollingUpdate: &specs.RollingUpdateSpec{ 117 Partition: &partition, 118 MaxSurge: &specs.IntOrString{Type: specs.String, StrVal: "25%"}, 119 }, 120 }, 121 } 122 c.Assert(spec.Validate(), gc.ErrorMatches, `partion can not be defined with maxUnavailable or maxSurge together`) 123 124 spec = specs.ServiceSpec{ 125 UpdateStrategy: &specs.UpdateStrategy{ 126 Type: "Recreate", 127 RollingUpdate: &specs.RollingUpdateSpec{ 128 Partition: &partition, 129 MaxUnavailable: &specs.IntOrString{Type: specs.String, StrVal: "10%"}, 130 }, 131 }, 132 } 133 c.Assert(spec.Validate(), gc.ErrorMatches, `partion can not be defined with maxUnavailable or maxSurge together`) 134 135 spec = specs.ServiceSpec{ 136 UpdateStrategy: &specs.UpdateStrategy{ 137 Type: "Recreate", 138 RollingUpdate: &specs.RollingUpdateSpec{ 139 Partition: &partition, 140 MaxUnavailable: &specs.IntOrString{Type: specs.String, StrVal: "10%"}, 141 MaxSurge: &specs.IntOrString{Type: specs.String, StrVal: "25%"}, 142 }, 143 }, 144 } 145 c.Assert(spec.Validate(), gc.ErrorMatches, `partion can not be defined with maxUnavailable or maxSurge together`) 146 } 147 148 func (s *baseSuite) TestValidateContainerSpec(c *gc.C) { 149 for i, tc := range []validateTc{ 150 { 151 spec: &specs.ContainerSpec{ 152 Name: "container1", 153 }, 154 errStr: `spec image details is missing`, 155 }, 156 { 157 spec: &specs.ContainerSpec{ 158 Image: "gitlab", 159 }, 160 errStr: `spec name is missing`, 161 }, 162 { 163 spec: &specs.ContainerSpec{ 164 ImageDetails: specs.ImageDetails{ 165 ImagePath: "gitlab", 166 }, 167 }, 168 errStr: `spec name is missing`, 169 }, 170 { 171 spec: &specs.ContainerSpec{ 172 Name: "container1", 173 Image: "gitlab", 174 }, 175 errStr: "", 176 }, 177 { 178 spec: &specs.ContainerSpec{ 179 Name: "container1", 180 ImageDetails: specs.ImageDetails{ 181 ImagePath: "gitlab", 182 }, 183 }, 184 errStr: "", 185 }, 186 } { 187 c.Logf("#%d: testing FileSet.Validate", i) 188 err := tc.spec.Validate() 189 if tc.errStr == "" { 190 c.Check(err, jc.ErrorIsNil) 191 } else { 192 c.Check(err, gc.ErrorMatches, tc.errStr) 193 } 194 } 195 } 196 197 func (s *baseSuite) TestValidatePodSpecBase(c *gc.C) { 198 minSpecs := specs.PodSpecBase{} 199 minSpecs.Containers = []specs.ContainerSpec{ 200 { 201 Name: "gitlab-helper", 202 Image: "gitlab-helper/latest", 203 Ports: []specs.ContainerPort{ 204 {ContainerPort: 8080, Protocol: "TCP"}, 205 }, 206 }, 207 } 208 c.Assert(minSpecs.Validate(specs.VersionLegacy), jc.ErrorIsNil) 209 minSpecs.Version = specs.VersionLegacy 210 c.Assert(minSpecs.Validate(specs.VersionLegacy), jc.ErrorIsNil) 211 212 c.Assert(minSpecs.Validate(specs.Version2), gc.ErrorMatches, `expected version 2, but found 0`) 213 minSpecs.Version = specs.Version2 214 c.Assert(minSpecs.Validate(specs.Version2), jc.ErrorIsNil) 215 } 216 217 func (s *baseSuite) TestValidateCaaSContainers(c *gc.C) { 218 k8sSpec := specs.CaasContainers{} 219 fileSet1 := specs.FileSet{ 220 Name: "file1", 221 MountPath: "/foo/file1", 222 VolumeSource: specs.VolumeSource{ 223 Files: []specs.File{ 224 {Path: "foo", Content: "bar"}, 225 }, 226 }, 227 } 228 fileSet2 := specs.FileSet{ 229 Name: "file2", 230 MountPath: "/foo/file2", 231 VolumeSource: specs.VolumeSource{ 232 Files: []specs.File{ 233 {Path: "foo", Content: "bar"}, 234 }, 235 }, 236 } 237 238 k8sSpec.Containers = []specs.ContainerSpec{ 239 { 240 Name: "gitlab-helper", 241 Image: "gitlab-helper/latest", 242 Ports: []specs.ContainerPort{ 243 {ContainerPort: 8080, Protocol: "TCP"}, 244 }, 245 VolumeConfig: []specs.FileSet{ 246 fileSet1, fileSet2, 247 }, 248 }, 249 } 250 c.Assert(k8sSpec.Validate(), jc.ErrorIsNil) 251 252 k8sSpec = specs.CaasContainers{} 253 k8sSpec.Containers = []specs.ContainerSpec{ 254 { 255 Name: "gitlab-helper", 256 Image: "gitlab-helper/latest", 257 Ports: []specs.ContainerPort{ 258 {ContainerPort: 8080, Protocol: "TCP"}, 259 }, 260 VolumeConfig: []specs.FileSet{ 261 fileSet1, fileSet1, 262 }, 263 }, 264 } 265 c.Assert(k8sSpec.Validate(), gc.ErrorMatches, `duplicated file "file1" in container "gitlab-helper" not valid`) 266 267 k8sSpec = specs.CaasContainers{} 268 k8sSpec.Containers = []specs.ContainerSpec{ 269 { 270 Name: "gitlab-helper", 271 Image: "gitlab-helper/latest", 272 Ports: []specs.ContainerPort{ 273 {ContainerPort: 8080, Protocol: "TCP"}, 274 }, 275 VolumeConfig: []specs.FileSet{ 276 { 277 Name: "file1", 278 MountPath: "/same-mount-path", 279 VolumeSource: specs.VolumeSource{ 280 Files: []specs.File{ 281 {Path: "foo", Content: "bar"}, 282 }, 283 }, 284 }, 285 { 286 Name: "file2", 287 MountPath: "/same-mount-path", 288 VolumeSource: specs.VolumeSource{ 289 HostPath: &specs.HostPathVol{ 290 Path: "/foo/bar", 291 }, 292 }, 293 }, 294 }, 295 }, 296 } 297 c.Assert(k8sSpec.Validate(), gc.ErrorMatches, `duplicated mount path "/same-mount-path" in container "gitlab-helper" not valid`) 298 299 k8sSpec = specs.CaasContainers{} 300 k8sSpec.Containers = []specs.ContainerSpec{ 301 { 302 Name: "gitlab-helper", 303 Image: "gitlab-helper/latest", 304 Ports: []specs.ContainerPort{ 305 {ContainerPort: 8080, Protocol: "TCP"}, 306 }, 307 VolumeConfig: []specs.FileSet{ 308 { 309 Name: "file1", 310 MountPath: "/etc/config", 311 VolumeSource: specs.VolumeSource{ 312 Files: []specs.File{ 313 {Path: "foo", Content: "bar"}, 314 }, 315 }, 316 }, 317 }, 318 }, 319 { 320 Name: "busybox", 321 Image: "busybox", 322 Ports: []specs.ContainerPort{ 323 {ContainerPort: 80, Protocol: "TCP"}, 324 }, 325 VolumeConfig: []specs.FileSet{ 326 { 327 Name: "file1", 328 MountPath: "/etc/config", 329 VolumeSource: specs.VolumeSource{ 330 HostPath: &specs.HostPathVol{ 331 Path: "/foo/bar", 332 }, 333 }, 334 }, 335 }, 336 }, 337 } 338 c.Assert(k8sSpec.Validate(), gc.ErrorMatches, `duplicated file "file1" with different volume spec not valid`) 339 340 k8sSpec = specs.CaasContainers{} 341 k8sSpec.Containers = []specs.ContainerSpec{ 342 { 343 Name: "gitlab-helper", 344 Image: "gitlab-helper/latest", 345 Ports: []specs.ContainerPort{ 346 {ContainerPort: 8080, Protocol: "TCP"}, 347 }, 348 VolumeConfig: []specs.FileSet{ 349 { 350 Name: "file1", 351 MountPath: "/foo/file1", 352 VolumeSource: specs.VolumeSource{ 353 Files: []specs.File{ 354 {Path: "foo", Content: "bar"}, 355 }, 356 }, 357 }, 358 { 359 Name: "file1", // same file in same container mount to different path. 360 MountPath: "/foo/another-file1", 361 VolumeSource: specs.VolumeSource{ 362 Files: []specs.File{ 363 {Path: "foo", Content: "bar"}, 364 }, 365 }, 366 }, 367 { 368 Name: "file2", 369 MountPath: "/foo/file2", 370 VolumeSource: specs.VolumeSource{ 371 Files: []specs.File{ 372 {Path: "foo", Content: "bar"}, 373 }, 374 }, 375 }, 376 { 377 Name: "host-path-1", 378 MountPath: "/etc/host-path", 379 VolumeSource: specs.VolumeSource{ 380 HostPath: &specs.HostPathVol{ 381 Path: "/foo/bar", 382 }, 383 }, 384 }, 385 { 386 Name: "empty-dir-1", 387 MountPath: "/etc/empty-dir", 388 VolumeSource: specs.VolumeSource{ 389 EmptyDir: &specs.EmptyDirVol{ 390 Medium: "Memory", 391 }, 392 }, 393 }, 394 { 395 Name: "config-map-1", 396 MountPath: "/etc/config", 397 VolumeSource: specs.VolumeSource{ 398 ConfigMap: &specs.ResourceRefVol{ 399 Name: "log-config", 400 Files: []specs.FileRef{ 401 { 402 Key: "log_level", 403 Path: "log_level", 404 }, 405 }, 406 }, 407 }, 408 }, 409 { 410 Name: "mysecret2", 411 MountPath: "/secrets", 412 VolumeSource: specs.VolumeSource{ 413 Secret: &specs.ResourceRefVol{ 414 Name: "mysecret2", 415 Files: []specs.FileRef{ 416 { 417 Key: "password", 418 Path: "my-group/my-password", 419 }, 420 }, 421 }, 422 }, 423 }, 424 }, 425 }, 426 { 427 Name: "busybox", 428 Image: "busybox", 429 Ports: []specs.ContainerPort{ 430 {ContainerPort: 80, Protocol: "TCP"}, 431 }, 432 VolumeConfig: []specs.FileSet{ 433 { 434 Name: "file1", // exact same file1 can be mounted to same path in a different container. 435 MountPath: "/foo/file1", 436 VolumeSource: specs.VolumeSource{ 437 Files: []specs.File{ 438 {Path: "foo", Content: "bar"}, 439 }, 440 }, 441 }, 442 }, 443 }, 444 } 445 c.Assert(k8sSpec.Validate(), jc.ErrorIsNil) 446 }