package validation
import (
"testing"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/diff"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/validation/field"
"github.com/openshift/origin/pkg/cmd/server/api"
configapi "github.com/openshift/origin/pkg/cmd/server/api"
)
func TestFailingAPIServerArgs(t *testing.T) {
args := configapi.ExtendedArguments{}
args["port"] = []string{"invalid-value"}
args["missing-key"] = []string{"value"}
// [port: invalid value '[invalid-value]': could not be set: strconv.ParseUint: parsing "invalid-value": invalid syntax flag: invalid value 'missing-key': is not a valid flag]
validationResults := ValidateAPIServerExtendedArguments(args, nil)
errs := validationResults.Errors
if len(errs) != 2 {
t.Fatalf("expected 2 errors, not %v", errs)
}
var (
portErr *field.Error
missingErr *field.Error
)
for _, err := range errs {
switch err.Field {
case "port":
portErr = err
case "flag":
missingErr = err
}
}
if portErr == nil {
t.Fatalf("missing port")
}
if missingErr == nil {
t.Fatalf("missing missing-key")
}
if e, a := "port", portErr.Field; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "invalid-value", portErr.BadValue.(string); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := `could not be set: strconv.ParseInt: parsing "invalid-value": invalid syntax`, portErr.Detail; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "flag", missingErr.Field; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "missing-key", missingErr.BadValue.(string); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := `is not a valid flag`, missingErr.Detail; e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
func TestFailingControllerArgs(t *testing.T) {
args := configapi.ExtendedArguments{}
args["port"] = []string{"invalid-value"}
args["missing-key"] = []string{"value"}
// [port: invalid value '[invalid-value]': could not be set: strconv.ParseUint: parsing "invalid-value": invalid syntax flag: invalid value 'missing-key': is not a valid flag]
errs := ValidateControllerExtendedArguments(args, nil)
if len(errs) != 2 {
t.Fatalf("expected 2 errors, not %v", errs)
}
var (
portErr *field.Error
missingErr *field.Error
)
for _, err := range errs {
switch err.Field {
case "port":
portErr = err
case "flag":
missingErr = err
}
}
if portErr == nil {
t.Fatalf("missing port")
}
if missingErr == nil {
t.Fatalf("missing missing-key")
}
if e, a := "port", portErr.Field; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "invalid-value", portErr.BadValue.(string); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := `could not be set: strconv.ParseInt: parsing "invalid-value": invalid syntax`, portErr.Detail; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "flag", missingErr.Field; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "missing-key", missingErr.BadValue.(string); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := `is not a valid flag`, missingErr.Detail; e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
func TestValidate_ValidateEtcdStorageConfig(t *testing.T) {
osField := "openShiftStorageVersion"
kubeField := "kubernetesStorageVersion"
tests := []struct {
label string
kubeStorageVersion string
openshiftStorageVersion string
name string
expected field.ErrorList
}{
{
label: "valid levels",
kubeStorageVersion: "v1",
openshiftStorageVersion: "v1",
expected: field.ErrorList{},
},
{
label: "unknown openshift level",
kubeStorageVersion: "v1",
openshiftStorageVersion: "bogus",
expected: field.ErrorList{
field.NotSupported(field.NewPath(osField), "bogus", []string{"v1"}),
},
},
{
label: "unsupported openshift level",
kubeStorageVersion: "v1",
openshiftStorageVersion: "v1beta3",
expected: field.ErrorList{
field.NotSupported(field.NewPath(osField), "v1beta3", []string{"v1"}),
},
},
{
label: "missing openshift level",
kubeStorageVersion: "v1",
openshiftStorageVersion: "",
expected: field.ErrorList{
field.Required(field.NewPath(osField), ""),
},
},
{
label: "unknown kube level",
kubeStorageVersion: "bogus",
openshiftStorageVersion: "v1",
expected: field.ErrorList{
field.NotSupported(field.NewPath(kubeField), "bogus", []string{"v1"}),
},
},
{
label: "unsupported kube level",
kubeStorageVersion: "v1beta3",
openshiftStorageVersion: "v1",
expected: field.ErrorList{
field.NotSupported(field.NewPath(kubeField), "v1beta3", []string{"v1"}),
},
},
{
label: "missing kube level",
kubeStorageVersion: "",
openshiftStorageVersion: "v1",
expected: field.ErrorList{
field.Required(field.NewPath(kubeField), ""),
},
},
}
for _, test := range tests {
t.Logf("evaluating test: %s", test.label)
config := api.EtcdStorageConfig{
OpenShiftStorageVersion: test.openshiftStorageVersion,
KubernetesStorageVersion: test.kubeStorageVersion,
}
results := ValidateEtcdStorageConfig(config, nil)
if !kapi.Semantic.DeepEqual(test.expected, results) {
t.Errorf("unexpected validation results; diff:\n%v", diff.ObjectDiff(test.expected, results))
return
}
}
}
func TestValidateAdmissionPluginConfig(t *testing.T) {
locationOnly := configapi.AdmissionPluginConfig{
Location: "/some/location",
}
configOnly := configapi.AdmissionPluginConfig{
Configuration: &configapi.NodeConfig{},
}
locationAndConfig := configapi.AdmissionPluginConfig{
Location: "/some/location",
Configuration: &configapi.NodeConfig{},
}
bothEmpty := configapi.AdmissionPluginConfig{}
tests := []struct {
config map[string]configapi.AdmissionPluginConfig
expectError bool
}{
{
config: map[string]configapi.AdmissionPluginConfig{
"one": locationOnly,
"two": configOnly,
},
},
{
config: map[string]configapi.AdmissionPluginConfig{
"one": locationOnly,
"two": locationAndConfig,
},
expectError: true,
},
{
config: map[string]configapi.AdmissionPluginConfig{
"one": configOnly,
"two": bothEmpty,
},
expectError: true,
},
}
for _, tc := range tests {
errs := ValidateAdmissionPluginConfig(tc.config, nil)
if len(errs) > 0 && !tc.expectError {
t.Errorf("Unexpected error for %#v: %v", tc.config, errs)
}
if len(errs) == 0 && tc.expectError {
t.Errorf("Did not get expected error for: %#v", tc.config)
}
}
}
func TestValidateAdmissionPluginConfigConflicts(t *testing.T) {
testCases := []struct {
name string
options configapi.MasterConfig
warningFields []string
}{
{
name: "stock everything",
},
{
name: "specified kube admission order 01",
options: configapi.MasterConfig{
KubernetesMasterConfig: &configapi.KubernetesMasterConfig{
AdmissionConfig: configapi.AdmissionConfig{
PluginOrderOverride: []string{"foo"},
},
},
},
warningFields: []string{"kubernetesMasterConfig.admissionConfig.pluginOrderOverride"},
},
{
name: "specified kube admission order 02",
options: configapi.MasterConfig{
KubernetesMasterConfig: &configapi.KubernetesMasterConfig{
APIServerArguments: configapi.ExtendedArguments{
"admission-control": []string{"foo"},
},
},
},
warningFields: []string{"kubernetesMasterConfig.apiServerArguments[admission-control]"},
},
{
name: "specified origin admission order",
options: configapi.MasterConfig{
AdmissionConfig: configapi.AdmissionConfig{
PluginOrderOverride: []string{"foo"},
},
},
warningFields: []string{"admissionConfig.pluginOrderOverride"},
},
{
name: "specified kube admission config file",
options: configapi.MasterConfig{
KubernetesMasterConfig: &configapi.KubernetesMasterConfig{
APIServerArguments: configapi.ExtendedArguments{
"admission-control-config-file": []string{"foo"},
},
},
},
warningFields: []string{"kubernetesMasterConfig.apiServerArguments[admission-control-config-file]"},
},
{
name: "specified, non-conflicting plugin configs 01",
options: configapi.MasterConfig{
AdmissionConfig: configapi.AdmissionConfig{
PluginConfig: map[string]configapi.AdmissionPluginConfig{
"foo": {
Location: "bar",
},
},
},
},
},
{
name: "specified, non-conflicting plugin configs 02",
options: configapi.MasterConfig{
KubernetesMasterConfig: &configapi.KubernetesMasterConfig{
AdmissionConfig: configapi.AdmissionConfig{
PluginConfig: map[string]configapi.AdmissionPluginConfig{
"foo": {
Location: "bar",
},
"third": {
Location: "bar",
},
},
},
},
AdmissionConfig: configapi.AdmissionConfig{
PluginConfig: map[string]configapi.AdmissionPluginConfig{
"foo": {
Location: "bar",
},
},
},
},
},
{
name: "specified, non-conflicting plugin configs 03",
options: configapi.MasterConfig{
KubernetesMasterConfig: &configapi.KubernetesMasterConfig{
AdmissionConfig: configapi.AdmissionConfig{
PluginConfig: map[string]configapi.AdmissionPluginConfig{
"foo": {
Location: "bar",
},
"third": {
Location: "bar",
},
},
},
},
},
},
{
name: "specified conflicting plugin configs 01",
options: configapi.MasterConfig{
KubernetesMasterConfig: &configapi.KubernetesMasterConfig{
AdmissionConfig: configapi.AdmissionConfig{
PluginConfig: map[string]configapi.AdmissionPluginConfig{
"foo": {
Location: "different",
},
},
},
},
AdmissionConfig: configapi.AdmissionConfig{
PluginConfig: map[string]configapi.AdmissionPluginConfig{
"foo": {
Location: "bar",
},
},
},
},
warningFields: []string{"kubernetesMasterConfig.admissionConfig.pluginConfig[foo]"},
},
}
// these fields have warnings in the empty case
defaultWarningFields := sets.NewString(
"serviceAccountConfig.managedNames", "serviceAccountConfig.publicKeyFiles", "serviceAccountConfig.privateKeyFile", "serviceAccountConfig.masterCA",
"projectConfig.securityAllocator", "kubernetesMasterConfig.proxyClientInfo", "auditConfig.auditFilePath")
for _, tc := range testCases {
results := ValidateMasterConfig(&tc.options, nil)
for _, result := range results.Warnings {
if defaultWarningFields.Has(result.Field) {
continue
}
found := false
for _, expectedField := range tc.warningFields {
if result.Field == expectedField {
found = true
break
}
}
if !found {
t.Errorf("%s: didn't expect %q", tc.name, result.Field)
}
}
for _, expectedField := range tc.warningFields {
found := false
for _, result := range results.Warnings {
if result.Field == expectedField {
found = true
break
}
}
if !found {
t.Errorf("%s: didn't find %q", tc.name, expectedField)
}
}
}
}
func TestValidateIngressIPNetworkCIDR(t *testing.T) {
testCases := []struct {
testName string
cidr string
serviceCIDR string
clusterCIDR string
cloudProvider string
errorCount int
}{
{
testName: "No CIDR",
},
{
testName: "Invalid CIDR",
cidr: "foo",
errorCount: 1,
},
{
testName: "No cloud provider and conflicting CIDRs",
cidr: "172.16.0.0/16",
serviceCIDR: "172.16.0.0/16",
clusterCIDR: "172.16.0.0/16",
errorCount: 2,
},
{
testName: "No cloud provider and unspecified CIDR",
cidr: "0.0.0.0/32",
},
{
testName: "No cloud provider and non-conflicting CIDR",
cidr: "172.16.0.0/16",
serviceCIDR: "172.17.0.0/16",
clusterCIDR: "172.18.0.0/16",
},
{
testName: "Cloud provider and unspecified CIDR",
cidr: "0.0.0.0/32",
cloudProvider: "foo",
},
{
testName: "Cloud provider and CIDR",
cidr: "172.16.0.0/16",
cloudProvider: "foo",
errorCount: 1,
},
}
for _, test := range testCases {
config := &configapi.MasterConfig{
KubernetesMasterConfig: &configapi.KubernetesMasterConfig{
ControllerArguments: configapi.ExtendedArguments{
"cloud-provider": []string{test.cloudProvider},
},
},
NetworkConfig: configapi.MasterNetworkConfig{
IngressIPNetworkCIDR: test.cidr,
ServiceNetworkCIDR: test.serviceCIDR,
ClusterNetworkCIDR: test.clusterCIDR,
},
}
errors := ValidateIngressIPNetworkCIDR(config, nil)
errorCount := len(errors)
if test.errorCount != errorCount {
t.Errorf("%s: expected %d errors, got %d", test.testName, test.errorCount, errorCount)
}
}
}