blob: 7344f2ab3f33c495591b620e14ab6aa446881b07 [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package validation_test
import (
"os"
"path/filepath"
"reflect"
"strings"
"testing"
)
import (
wrappers "google.golang.org/protobuf/types/known/wrapperspb"
v1alpha12 "istio.io/api/operator/v1alpha1"
)
import (
"github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1"
"github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1/validation"
"github.com/apache/dubbo-go-pixiu/operator/pkg/helm"
"github.com/apache/dubbo-go-pixiu/operator/pkg/manifest"
"github.com/apache/dubbo-go-pixiu/operator/pkg/util"
"github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog"
"github.com/apache/dubbo-go-pixiu/pkg/test/env"
)
// nolint: lll
func TestValidateConfig(t *testing.T) {
tests := []struct {
name string
value *v1alpha12.IstioOperatorSpec
values string
errors string
warnings string
}{
{
name: "addons",
value: &v1alpha12.IstioOperatorSpec{
AddonComponents: map[string]*v1alpha12.ExternalComponentSpec{
"grafana": {
Enabled: &wrappers.BoolValue{Value: true},
},
},
Values: util.MustStruct(map[string]interface{}{
"grafana": map[string]interface{}{
"enabled": true,
},
}),
},
errors: `! values.grafana.enabled is deprecated; use the samples/addons/ deployments instead
, ! addonComponents.grafana.enabled is deprecated; use the samples/addons/ deployments instead
`,
},
{
name: "global",
value: &v1alpha12.IstioOperatorSpec{
Values: util.MustStruct(map[string]interface{}{
"global": map[string]interface{}{
"localityLbSetting": map[string]interface{}{"foo": "bar"},
},
}),
},
warnings: `! values.global.localityLbSetting is deprecated; use meshConfig.localityLbSetting instead`,
},
{
name: "unset target port",
values: `
components:
ingressGateways:
- name: istio-ingressgateway
enabled: true
- name: cluster-local-gateway
enabled: true
k8s:
service:
type: ClusterIP
ports:
- port: 15020
name: status-port
- port: 80
name: http2
`,
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`,
},
{
name: "explicitly invalid target port",
values: `
components:
ingressGateways:
- name: istio-ingressgateway
enabled: true
- name: cluster-local-gateway
enabled: true
k8s:
service:
type: ClusterIP
ports:
- port: 15020
name: status-port
- port: 80
name: http2
targetPort: 90
`,
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`,
},
{
name: "explicitly invalid target port for egress",
values: `
components:
egressGateways:
- name: egress-gateway
enabled: true
k8s:
service:
type: ClusterIP
ports:
- port: 15020
name: status-port
- port: 80
name: http2
targetPort: 90
`,
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`,
},
{
name: "low target port with root",
values: `
components:
ingressGateways:
- name: istio-ingressgateway
enabled: true
- name: cluster-local-gateway
enabled: true
k8s:
service:
type: ClusterIP
ports:
- port: 15020
name: status-port
- port: 80
name: http2
targetPort: 90
values:
gateways:
istio-ingressgateway:
runAsRoot: true
`,
errors: ``,
},
{
name: "legacy values ports config empty targetPort",
values: `
values:
gateways:
istio-ingressgateway:
ingressPorts:
- name: http
port: 80
`,
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`,
},
{
name: "legacy values ports config explicit targetPort",
values: `
values:
gateways:
istio-ingressgateway:
ingressPorts:
- name: http
port: 80
targetPort: 90
`,
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`,
},
{
name: "legacy values ports valid",
values: `
values:
gateways:
istio-ingressgateway:
ingressPorts:
- name: http
port: 80
targetPort: 8080
`,
errors: ``,
},
{
name: "replicaCount set when autoscaleEnabled is true",
values: `
values:
pilot:
autoscaleEnabled: true
gateways:
istio-ingressgateway:
autoscaleEnabled: true
istio-egressgateway:
autoscaleEnabled: true
components:
pilot:
k8s:
replicaCount: 2
ingressGateways:
- name: istio-ingressgateway
enabled: true
k8s:
replicaCount: 2
egressGateways:
- name: istio-egressgateway
enabled: true
k8s:
replicaCount: 2
`,
warnings: strings.TrimSpace(`
components.pilot.k8s.replicaCount should not be set when values.pilot.autoscaleEnabled is true
components.ingressGateways[name=istio-ingressgateway].k8s.replicaCount should not be set when values.gateways.istio-ingressgateway.autoscaleEnabled is true
components.egressGateways[name=istio-egressgateway].k8s.replicaCount should not be set when values.gateways.istio-egressgateway.autoscaleEnabled is true
`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
iop := tt.value
if tt.values != "" {
iop = &v1alpha12.IstioOperatorSpec{}
if err := util.UnmarshalWithJSONPB(tt.values, iop, true); err != nil {
t.Fatal(err)
}
}
err, warnings := validation.ValidateConfig(false, iop)
if tt.errors != err.String() {
t.Fatalf("expected errors: \n%q\n got: \n%q\n", tt.errors, err.String())
}
if tt.warnings != warnings {
t.Fatalf("expected warnings: \n%q\n got \n%q\n", tt.warnings, warnings)
}
})
}
}
func TestValidateProfiles(t *testing.T) {
manifests := filepath.Join(env.IstioSrc, helm.OperatorSubdirFilePath)
profiles, err := helm.ListProfiles(manifests)
if err != nil {
t.Fatal(err)
}
if len(profiles) < 2 {
// Just ensure we find some profiles, in case this code breaks
t.Fatalf("Maybe have failed getting profiles, got %v", profiles)
}
l := clog.NewConsoleLogger(os.Stdout, os.Stderr, nil)
for _, tt := range profiles {
t.Run(tt, func(t *testing.T) {
_, s, err := manifest.GenIOPFromProfile(tt, "", []string{"installPackagePath=" + manifests}, false, false, nil, l)
if err != nil {
t.Fatal(err)
}
verr, warnings := validation.ValidateConfig(false, s.Spec)
if verr != nil {
t.Fatalf("got error validating: %v", verr)
}
if warnings != "" {
t.Fatalf("got warning validating: %v", warnings)
}
})
}
}
func TestValidate(t *testing.T) {
tests := []struct {
name string
toValidate *v1alpha1.Values
validated bool
}{
{
name: "Empty struct",
toValidate: &v1alpha1.Values{},
validated: true,
},
{
name: "With CNI defined",
toValidate: &v1alpha1.Values{
Cni: &v1alpha1.CNIConfig{
Enabled: &wrappers.BoolValue{Value: true},
},
},
validated: true,
},
}
for _, tt := range tests {
err := validation.ValidateSubTypes(reflect.ValueOf(tt.toValidate).Elem(), false, tt.toValidate, nil)
if len(err) != 0 && tt.validated {
t.Fatalf("Test %s failed with errors: %+v but supposed to succeed", tt.name, err)
}
if len(err) == 0 && !tt.validated {
t.Fatalf("Test %s failed as it is supposed to fail but succeeded", tt.name)
}
}
}