github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/caas/kubernetes/provider/specs/container_env_test.go (about) 1 // Copyright 2020 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 core "k8s.io/api/core/v1" 10 "k8s.io/apimachinery/pkg/api/resource" 11 "k8s.io/utils/pointer" 12 13 k8sspecs "github.com/juju/juju/caas/kubernetes/provider/specs" 14 "github.com/juju/juju/testing" 15 ) 16 17 type containerEnvSuite struct { 18 testing.BaseSuite 19 } 20 21 var _ = gc.Suite(&containerEnvSuite{}) 22 23 func (s *containerEnvSuite) TestContainerConfigToK8sEnvConfig(c *gc.C) { 24 25 specStr := version3Header + ` 26 containers: 27 - name: gitlab 28 image: gitlab/latest 29 envConfig: 30 attr: foo=bar; name["fred"]="blogs"; 31 foo: bar 32 brackets: '["hello", "world"]' 33 restricted: 'yes' 34 switch: on 35 bar: true 36 special: p@ssword's 37 foo: bar 38 float: 111.11111111 39 int: 111 40 MY_NODE_NAME: 41 field: 42 path: spec.nodeName 43 api-version: v1 44 my-resource-limit: 45 resource: 46 container-name: container1 47 resource: requests.cpu 48 divisor: 1m 49 thing: 50 secret: 51 name: foo 52 key: bar 53 a-secret: 54 secret: 55 name: secret1 56 optional: true 57 another-secret: 58 secret: 59 name: secret2 60 thing1: 61 config-map: 62 name: foo 63 key: bar 64 a-configmap: 65 config-map: 66 name: configmap1 67 optional: true 68 another-configmap: 69 config-map: 70 name: configmap2 71 `[1:] 72 73 envVarThing := core.EnvVar{ 74 Name: "thing", 75 ValueFrom: &core.EnvVarSource{ 76 SecretKeyRef: &core.SecretKeySelector{Key: "bar"}, 77 }, 78 } 79 envVarThing.ValueFrom.SecretKeyRef.Name = "foo" 80 81 envVarThing1 := core.EnvVar{ 82 Name: "thing1", 83 ValueFrom: &core.EnvVarSource{ 84 ConfigMapKeyRef: &core.ConfigMapKeySelector{Key: "bar"}, 85 }, 86 } 87 envVarThing1.ValueFrom.ConfigMapKeyRef.Name = "foo" 88 89 envFromSourceSecret1 := core.EnvFromSource{ 90 SecretRef: &core.SecretEnvSource{Optional: pointer.BoolPtr(true)}, 91 } 92 envFromSourceSecret1.SecretRef.Name = "secret1" 93 94 envFromSourceSecret2 := core.EnvFromSource{ 95 SecretRef: &core.SecretEnvSource{}, 96 } 97 envFromSourceSecret2.SecretRef.Name = "secret2" 98 99 envFromSourceConfigmap1 := core.EnvFromSource{ 100 ConfigMapRef: &core.ConfigMapEnvSource{Optional: pointer.BoolPtr(true)}, 101 } 102 envFromSourceConfigmap1.ConfigMapRef.Name = "configmap1" 103 104 envFromSourceConfigmap2 := core.EnvFromSource{ 105 ConfigMapRef: &core.ConfigMapEnvSource{}, 106 } 107 envFromSourceConfigmap2.ConfigMapRef.Name = "configmap2" 108 109 specs, err := k8sspecs.ParsePodSpec(specStr) 110 c.Assert(err, jc.ErrorIsNil) 111 envVars, envFromSource, err := k8sspecs.ContainerConfigToK8sEnvConfig(specs.Containers[0].EnvConfig) 112 c.Assert(err, jc.ErrorIsNil) 113 expectedEnvVar := []core.EnvVar{ 114 { 115 Name: "MY_NODE_NAME", 116 ValueFrom: &core.EnvVarSource{ 117 FieldRef: &core.ObjectFieldSelector{ 118 FieldPath: "spec.nodeName", 119 APIVersion: "v1", 120 }, 121 }, 122 }, 123 {Name: "attr", Value: `foo=bar; name["fred"]="blogs";`}, 124 {Name: "bar", Value: "true"}, 125 {Name: "brackets", Value: `["hello", "world"]`}, 126 {Name: "float", Value: "111.11111111"}, 127 {Name: "foo", Value: "bar"}, 128 {Name: "int", Value: "111"}, 129 { 130 Name: "my-resource-limit", 131 ValueFrom: &core.EnvVarSource{ 132 ResourceFieldRef: &core.ResourceFieldSelector{ 133 ContainerName: "container1", 134 Resource: "requests.cpu", 135 Divisor: resource.MustParse("1m"), 136 }, 137 }, 138 }, 139 {Name: "restricted", Value: "yes"}, 140 {Name: "special", Value: "p@ssword's"}, 141 {Name: "switch", Value: "true"}, 142 envVarThing, 143 envVarThing1, 144 } 145 expectedEnvFromSource := []core.EnvFromSource{ 146 envFromSourceConfigmap1, 147 envFromSourceConfigmap2, 148 envFromSourceSecret1, 149 envFromSourceSecret2, 150 } 151 for i := range envVars { 152 c.Check(envVars[i], jc.DeepEquals, expectedEnvVar[i]) 153 } 154 for i := range envFromSource { 155 c.Check(envFromSource[i], jc.DeepEquals, expectedEnvFromSource[i]) 156 } 157 } 158 159 func (s *containerEnvSuite) TestContainerConfigToK8sEnvConfigSliceNotSupported(c *gc.C) { 160 _, _, err := k8sspecs.ContainerConfigToK8sEnvConfig(map[string]interface{}{ 161 "a-slice": []interface{}{}, 162 }) 163 c.Assert(err, gc.ErrorMatches, `config "a-slice" with type .* not supported`) 164 } 165 166 func (s *containerEnvSuite) TestContainerConfigToK8sEnvConfigFailedBadField(c *gc.C) { 167 _, _, err := k8sspecs.ContainerConfigToK8sEnvConfig(map[string]interface{}{ 168 "a-bad-config-map": map[string]interface{}{ 169 "config-map": map[string]interface{}{ 170 "a-bad-field": "", 171 }, 172 }, 173 }) 174 c.Assert(err, gc.ErrorMatches, `json: unknown field "a-bad-field"`) 175 } 176 177 func (s *containerEnvSuite) TestContainerConfigToK8sEnvConfigFailedRequiredFieldMissing(c *gc.C) { 178 type tc struct { 179 resourceType string 180 optionalField string 181 } 182 for i, t := range []tc{ 183 {resourceType: "secret", optionalField: "key"}, 184 {resourceType: "config-map", optionalField: "key"}, 185 {resourceType: "resource", optionalField: "container-name"}, 186 {resourceType: "field", optionalField: "api-version"}, 187 } { 188 c.Logf("checking %d: %q", i, t.resourceType) 189 _, _, err := k8sspecs.ContainerConfigToK8sEnvConfig(map[string]interface{}{ 190 "empty": map[string]interface{}{ 191 t.resourceType: map[string]interface{}{}, 192 }, 193 }) 194 c.Check(err, gc.ErrorMatches, `config format of "empty" not supported`) 195 196 _, _, err = k8sspecs.ContainerConfigToK8sEnvConfig(map[string]interface{}{ 197 "empty": map[string]interface{}{ 198 t.resourceType: map[string]interface{}{ 199 t.optionalField: "foo", 200 }, 201 }, 202 }) 203 c.Check(err, gc.ErrorMatches, `config format of "empty" not supported`) 204 } 205 }