istio.io/istio@v0.0.0-20240520182934-d79c90f27776/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package validation_test 16 17 import ( 18 "os" 19 "path/filepath" 20 "reflect" 21 "strings" 22 "testing" 23 24 wrappers "google.golang.org/protobuf/types/known/wrapperspb" 25 26 v1alpha12 "istio.io/api/operator/v1alpha1" 27 "istio.io/istio/operator/pkg/apis/istio/v1alpha1" 28 "istio.io/istio/operator/pkg/apis/istio/v1alpha1/validation" 29 "istio.io/istio/operator/pkg/helm" 30 "istio.io/istio/operator/pkg/manifest" 31 "istio.io/istio/operator/pkg/util" 32 "istio.io/istio/operator/pkg/util/clog" 33 "istio.io/istio/pkg/test/env" 34 ) 35 36 const operatorSubdirFilePath = "manifests" 37 38 // nolint: lll 39 func TestValidateConfig(t *testing.T) { 40 tests := []struct { 41 name string 42 value *v1alpha12.IstioOperatorSpec 43 values string 44 errors string 45 warnings string 46 }{ 47 { 48 name: "addons", 49 value: &v1alpha12.IstioOperatorSpec{ 50 AddonComponents: map[string]*v1alpha12.ExternalComponentSpec{ 51 "grafana": { 52 Enabled: &wrappers.BoolValue{Value: true}, 53 }, 54 }, 55 Values: util.MustStruct(map[string]any{ 56 "grafana": map[string]any{ 57 "enabled": true, 58 }, 59 }), 60 }, 61 errors: `! values.grafana.enabled is deprecated; use the samples/addons/ deployments instead 62 , ! addonComponents.grafana.enabled is deprecated; use the samples/addons/ deployments instead 63 `, 64 }, 65 { 66 name: "global", 67 value: &v1alpha12.IstioOperatorSpec{ 68 Values: util.MustStruct(map[string]any{ 69 "global": map[string]any{ 70 "localityLbSetting": map[string]any{"foo": "bar"}, 71 }, 72 }), 73 }, 74 warnings: `! values.global.localityLbSetting is deprecated; use meshConfig.localityLbSetting instead`, 75 }, 76 { 77 name: "unset target port", 78 values: ` 79 components: 80 ingressGateways: 81 - name: istio-ingressgateway 82 enabled: true 83 - name: cluster-local-gateway 84 enabled: true 85 k8s: 86 service: 87 type: ClusterIP 88 ports: 89 - port: 15020 90 name: status-port 91 - port: 80 92 name: http2 93 `, 94 errors: `port http2/80 in gateway cluster-local-gateway invalid: targetPort is set to 0, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, 95 }, 96 { 97 name: "explicitly invalid target port", 98 values: ` 99 components: 100 ingressGateways: 101 - name: istio-ingressgateway 102 enabled: true 103 - name: cluster-local-gateway 104 enabled: true 105 k8s: 106 service: 107 type: ClusterIP 108 ports: 109 - port: 15020 110 name: status-port 111 - port: 80 112 name: http2 113 targetPort: 90 114 `, 115 errors: `port http2/80 in gateway cluster-local-gateway invalid: targetPort is set to 90, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, 116 }, 117 { 118 name: "explicitly invalid target port for egress", 119 values: ` 120 components: 121 egressGateways: 122 - name: egress-gateway 123 enabled: true 124 k8s: 125 service: 126 type: ClusterIP 127 ports: 128 - port: 15020 129 name: status-port 130 - port: 80 131 name: http2 132 targetPort: 90 133 `, 134 errors: `port http2/80 in gateway egress-gateway invalid: targetPort is set to 90, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-egressgateway.runAsRoot=true`, 135 }, 136 { 137 name: "low target port with root", 138 values: ` 139 components: 140 ingressGateways: 141 - name: istio-ingressgateway 142 enabled: true 143 - name: cluster-local-gateway 144 enabled: true 145 k8s: 146 service: 147 type: ClusterIP 148 ports: 149 - port: 15020 150 name: status-port 151 - port: 80 152 name: http2 153 targetPort: 90 154 values: 155 gateways: 156 istio-ingressgateway: 157 runAsRoot: true 158 `, 159 errors: ``, 160 }, 161 { 162 name: "legacy values ports config empty targetPort", 163 values: ` 164 values: 165 gateways: 166 istio-ingressgateway: 167 ingressPorts: 168 - name: http 169 port: 80 170 `, 171 errors: `port 80 is invalid: targetPort is set to 0, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, 172 }, 173 { 174 name: "legacy values ports config explicit targetPort", 175 values: ` 176 values: 177 gateways: 178 istio-ingressgateway: 179 ingressPorts: 180 - name: http 181 port: 80 182 targetPort: 90 183 `, 184 errors: `port 80 is invalid: targetPort is set to 90, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, 185 }, 186 { 187 name: "legacy values ports valid", 188 values: ` 189 values: 190 gateways: 191 istio-ingressgateway: 192 ingressPorts: 193 - name: http 194 port: 80 195 targetPort: 8080 196 `, 197 errors: ``, 198 }, 199 { 200 name: "replicaCount set when autoscaleEnabled is true", 201 values: ` 202 values: 203 pilot: 204 autoscaleEnabled: true 205 gateways: 206 istio-ingressgateway: 207 autoscaleEnabled: true 208 istio-egressgateway: 209 autoscaleEnabled: true 210 components: 211 pilot: 212 k8s: 213 replicaCount: 2 214 ingressGateways: 215 - name: istio-ingressgateway 216 enabled: true 217 k8s: 218 replicaCount: 2 219 egressGateways: 220 - name: istio-egressgateway 221 enabled: true 222 k8s: 223 replicaCount: 2 224 `, 225 warnings: strings.TrimSpace(` 226 components.pilot.k8s.replicaCount should not be set when values.pilot.autoscaleEnabled is true 227 components.ingressGateways[name=istio-ingressgateway].k8s.replicaCount should not be set when values.gateways.istio-ingressgateway.autoscaleEnabled is true 228 components.egressGateways[name=istio-egressgateway].k8s.replicaCount should not be set when values.gateways.istio-egressgateway.autoscaleEnabled is true 229 `), 230 }, 231 { 232 name: "pilot.k8s.replicaCount is default value set when autoscaleEnabled is true", 233 values: ` 234 values: 235 pilot: 236 autoscaleEnabled: true 237 gateways: 238 istio-ingressgateway: 239 autoscaleEnabled: true 240 istio-egressgateway: 241 autoscaleEnabled: true 242 components: 243 pilot: 244 k8s: 245 replicaCount: 1 246 `, 247 warnings: strings.TrimSpace(``), 248 }, 249 } 250 for _, tt := range tests { 251 t.Run(tt.name, func(t *testing.T) { 252 iop := tt.value 253 if tt.values != "" { 254 iop = &v1alpha12.IstioOperatorSpec{} 255 if err := util.UnmarshalWithJSONPB(tt.values, iop, true); err != nil { 256 t.Fatal(err) 257 } 258 } 259 err, warnings := validation.ValidateConfig(false, iop) 260 if tt.errors != err.String() { 261 t.Fatalf("expected errors: \n%q\n got: \n%q\n", tt.errors, err.String()) 262 } 263 if tt.warnings != warnings { 264 t.Fatalf("expected warnings: \n%q\n got \n%q\n", tt.warnings, warnings) 265 } 266 }) 267 } 268 } 269 270 func TestValidateProfiles(t *testing.T) { 271 manifests := filepath.Join(env.IstioSrc, operatorSubdirFilePath) 272 profiles, err := helm.ListProfiles(manifests) 273 if err != nil { 274 t.Fatal(err) 275 } 276 if len(profiles) < 2 { 277 // Just ensure we find some profiles, in case this code breaks 278 t.Fatalf("Maybe have failed getting profiles, got %v", profiles) 279 } 280 l := clog.NewConsoleLogger(os.Stdout, os.Stderr, nil) 281 for _, tt := range profiles { 282 t.Run(tt, func(t *testing.T) { 283 _, s, err := manifest.GenIOPFromProfile(tt, "", []string{"installPackagePath=" + manifests}, false, false, nil, l) 284 if err != nil { 285 t.Fatal(err) 286 } 287 verr, warnings := validation.ValidateConfig(false, s.Spec) 288 if verr != nil { 289 t.Fatalf("got error validating: %v", verr) 290 } 291 if warnings != "" { 292 t.Fatalf("got warning validating: %v", warnings) 293 } 294 }) 295 } 296 } 297 298 func TestValidate(t *testing.T) { 299 tests := []struct { 300 name string 301 toValidate *v1alpha1.Values 302 validated bool 303 }{ 304 { 305 name: "Empty struct", 306 toValidate: &v1alpha1.Values{}, 307 validated: true, 308 }, 309 { 310 name: "With CNI defined", 311 toValidate: &v1alpha1.Values{ 312 Cni: &v1alpha1.CNIConfig{ 313 Enabled: &wrappers.BoolValue{Value: true}, 314 }, 315 }, 316 validated: true, 317 }, 318 } 319 320 for _, tt := range tests { 321 err := validation.ValidateSubTypes(reflect.ValueOf(tt.toValidate).Elem(), false, tt.toValidate, nil) 322 if len(err) != 0 && tt.validated { 323 t.Fatalf("Test %s failed with errors: %+v but supposed to succeed", tt.name, err) 324 } 325 if len(err) == 0 && !tt.validated { 326 t.Fatalf("Test %s failed as it is supposed to fail but succeeded", tt.name) 327 } 328 } 329 }