mirror of https://github.com/kubernetes/kops.git
Merge pull request #9217 from johngmyers/refactor-validation
Refactor and improve API validation
This commit is contained in:
commit
10bb3cf334
|
|
@ -26,6 +26,11 @@ var SupportedTopologies = []string{
|
|||
TopologyPrivate,
|
||||
}
|
||||
|
||||
var SupportedDnsTypes = []string{
|
||||
string(DNSTypePublic),
|
||||
string(DNSTypePrivate),
|
||||
}
|
||||
|
||||
type TopologySpec struct {
|
||||
// The environment to launch the Kubernetes masters in public|private
|
||||
Masters string `json:"masters,omitempty"`
|
||||
|
|
|
|||
|
|
@ -19,9 +19,7 @@ package validation
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/apis/kops/util"
|
||||
|
|
@ -48,25 +46,6 @@ func ValidateCluster(c *kops.Cluster, strict bool) field.ErrorList {
|
|||
return allErrs
|
||||
}
|
||||
|
||||
if c.ObjectMeta.Name == "" {
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("objectMeta", "name"), "Cluster Name is required (e.g. --name=mycluster.myzone.com)"))
|
||||
} else {
|
||||
// Must be a dns name
|
||||
errs := validation.IsDNS1123Subdomain(c.ObjectMeta.Name)
|
||||
if len(errs) != 0 {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("objectMeta", "name"), c.ObjectMeta.Name, fmt.Sprintf("Cluster Name must be a valid DNS name (e.g. --name=mycluster.myzone.com) errors: %s", strings.Join(errs, ", "))))
|
||||
} else if !strings.Contains(c.ObjectMeta.Name, ".") {
|
||||
// Tolerate if this is a cluster we are importing for upgrade
|
||||
if c.ObjectMeta.Annotations[kops.AnnotationNameManagement] != kops.AnnotationValueManagementImported {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("objectMeta", "name"), c.ObjectMeta.Name, "Cluster Name must be a fully-qualified DNS name (e.g. --name=mycluster.myzone.com)"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.Spec.Assets != nil && c.Spec.Assets.ContainerProxy != nil && c.Spec.Assets.ContainerRegistry != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(fieldSpec.Child("Assets", "ContainerProxy"), "ContainerProxy cannot be used in conjunction with ContainerRegistry as represent mutually exclusive concepts. Please consult the documentation for details."))
|
||||
}
|
||||
|
||||
requiresSubnets := true
|
||||
requiresNetworkCIDR := true
|
||||
requiresSubnetCIDR := true
|
||||
|
|
@ -376,116 +355,6 @@ func ValidateCluster(c *kops.Cluster, strict bool) field.ErrorList {
|
|||
}
|
||||
}
|
||||
|
||||
// NodeAuthorization
|
||||
if c.Spec.NodeAuthorization != nil {
|
||||
// @check the feature gate is enabled for this
|
||||
if !featureflag.EnableNodeAuthorization.Enabled() {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "nodeAuthorization"), "node authorization is experimental feature; set `export KOPS_FEATURE_FLAGS=EnableNodeAuthorization`"))
|
||||
} else {
|
||||
if c.Spec.NodeAuthorization.NodeAuthorizer == nil {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "nodeAuthorization"), "no node authorization policy has been set"))
|
||||
} else {
|
||||
path := field.NewPath("spec", "nodeAuthorization").Child("nodeAuthorizer")
|
||||
if c.Spec.NodeAuthorization.NodeAuthorizer.Port < 0 || c.Spec.NodeAuthorization.NodeAuthorizer.Port >= 65535 {
|
||||
allErrs = append(allErrs, field.Invalid(path.Child("port"), c.Spec.NodeAuthorization.NodeAuthorizer.Port, "invalid port"))
|
||||
}
|
||||
if c.Spec.NodeAuthorization.NodeAuthorizer.Timeout != nil && c.Spec.NodeAuthorization.NodeAuthorizer.Timeout.Duration <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(path.Child("timeout"), c.Spec.NodeAuthorization.NodeAuthorizer.Timeout, "must be greater than zero"))
|
||||
}
|
||||
if c.Spec.NodeAuthorization.NodeAuthorizer.TokenTTL != nil && c.Spec.NodeAuthorization.NodeAuthorizer.TokenTTL.Duration < 0 {
|
||||
allErrs = append(allErrs, field.Invalid(path.Child("tokenTTL"), c.Spec.NodeAuthorization.NodeAuthorizer.TokenTTL, "must be greater than or equal to zero"))
|
||||
}
|
||||
|
||||
// @question: we could probably just default these settings in the model when the node-authorizer is enabled??
|
||||
if c.Spec.KubeAPIServer == nil {
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("spec", "kubeAPIServer"), "bootstrap token authentication is not enabled in the kube-apiserver"))
|
||||
} else if c.Spec.KubeAPIServer.EnableBootstrapAuthToken == nil {
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("spec", "kubeAPIServer", "enableBootstrapAuthToken"), "kube-apiserver has not been configured to use bootstrap tokens"))
|
||||
} else if !fi.BoolValue(c.Spec.KubeAPIServer.EnableBootstrapAuthToken) {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "kubeAPIServer", "enableBootstrapAuthToken"), "bootstrap tokens in the kube-apiserver has been disabled"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpdatePolicy
|
||||
if c.Spec.UpdatePolicy != nil {
|
||||
switch *c.Spec.UpdatePolicy {
|
||||
case kops.UpdatePolicyExternal:
|
||||
// Valid
|
||||
default:
|
||||
allErrs = append(allErrs, field.NotSupported(fieldSpec.Child("updatePolicy"), *c.Spec.UpdatePolicy, []string{kops.UpdatePolicyExternal}))
|
||||
}
|
||||
}
|
||||
|
||||
// KubeProxy
|
||||
if c.Spec.KubeProxy != nil {
|
||||
kubeProxyPath := fieldSpec.Child("kubeProxy")
|
||||
master := c.Spec.KubeProxy.Master
|
||||
|
||||
for i, x := range c.Spec.KubeProxy.IPVSExcludeCIDRS {
|
||||
if _, _, err := net.ParseCIDR(x); err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(kubeProxyPath.Child("ipvsExcludeCidrs").Index(i), x, "Invalid network CIDR"))
|
||||
}
|
||||
}
|
||||
|
||||
if master != "" && !isValidAPIServersURL(master) {
|
||||
allErrs = append(allErrs, field.Invalid(kubeProxyPath.Child("master"), master, "Not a valid APIServer URL"))
|
||||
}
|
||||
}
|
||||
|
||||
// KubeAPIServer
|
||||
if c.Spec.KubeAPIServer != nil {
|
||||
if len(c.Spec.KubeAPIServer.AdmissionControl) > 0 {
|
||||
if len(c.Spec.KubeAPIServer.DisableAdmissionPlugins) > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fieldSpec.Child("kubeAPIServer", "disableAdmissionPlugins"),
|
||||
"disableAdmissionPlugins is mutually exclusive, you cannot use both admissionControl and disableAdmissionPlugins together"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kubelet
|
||||
allErrs = append(allErrs, validateKubelet(c.Spec.Kubelet, c, fieldSpec.Child("kubelet"))...)
|
||||
allErrs = append(allErrs, validateKubelet(c.Spec.MasterKubelet, c, fieldSpec.Child("masterKubelet"))...)
|
||||
|
||||
// Topology support
|
||||
if c.Spec.Topology != nil {
|
||||
if c.Spec.Topology.Masters != "" && c.Spec.Topology.Nodes != "" {
|
||||
allErrs = append(allErrs, IsValidValue(fieldSpec.Child("topology", "masters"), &c.Spec.Topology.Masters, kops.SupportedTopologies)...)
|
||||
allErrs = append(allErrs, IsValidValue(fieldSpec.Child("topology", "nodes"), &c.Spec.Topology.Nodes, kops.SupportedTopologies)...)
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Required(fieldSpec.Child("masters"), "topology requires non-nil values for masters and nodes"))
|
||||
}
|
||||
if c.Spec.Topology.Bastion != nil {
|
||||
bastion := c.Spec.Topology.Bastion
|
||||
if c.Spec.Topology.Masters == kops.TopologyPublic || c.Spec.Topology.Nodes == kops.TopologyPublic {
|
||||
allErrs = append(allErrs, field.Forbidden(fieldSpec.Child("topology", "bastion"), "bastion requires masters and nodes to have private topology"))
|
||||
}
|
||||
if bastion.IdleTimeoutSeconds != nil && *bastion.IdleTimeoutSeconds <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fieldSpec.Child("topology", "bastion", "idleTimeoutSeconds"), *bastion.IdleTimeoutSeconds, "bastion idleTimeoutSeconds should be greater than zero"))
|
||||
}
|
||||
if bastion.IdleTimeoutSeconds != nil && *bastion.IdleTimeoutSeconds > 3600 {
|
||||
allErrs = append(allErrs, field.Invalid(fieldSpec.Child("topology", "bastion", "idleTimeoutSeconds"), *bastion.IdleTimeoutSeconds, "bastion idleTimeoutSeconds cannot be greater than one hour"))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// Egress specification support
|
||||
{
|
||||
for i, s := range c.Spec.Subnets {
|
||||
if s.Egress == "" {
|
||||
continue
|
||||
}
|
||||
fieldSubnet := fieldSpec.Child("subnets").Index(i)
|
||||
if !strings.HasPrefix(s.Egress, "nat-") && !strings.HasPrefix(s.Egress, "i-") && s.Egress != kops.EgressExternal {
|
||||
allErrs = append(allErrs, field.Invalid(fieldSubnet.Child("egress"), s.Egress, "egress must be of type NAT Gateway or NAT EC2 Instance or 'External'"))
|
||||
}
|
||||
if s.Egress != kops.EgressExternal && s.Type != "Private" {
|
||||
allErrs = append(allErrs, field.Forbidden(fieldSubnet.Child("egress"), "egress can only be specified for private subnets"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, newValidateCluster(c)...)
|
||||
|
||||
return allErrs
|
||||
|
|
@ -550,46 +419,6 @@ func DeepValidate(c *kops.Cluster, groups []*kops.InstanceGroup, strict bool, cl
|
|||
return nil
|
||||
}
|
||||
|
||||
func validateKubelet(k *kops.KubeletConfigSpec, c *kops.Cluster, kubeletPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if k != nil {
|
||||
|
||||
{
|
||||
// Flag removed in 1.6
|
||||
if k.APIServers != "" {
|
||||
allErrs = append(allErrs, field.Forbidden(
|
||||
kubeletPath.Child("apiServers"),
|
||||
"api-servers flag was removed in 1.6"))
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Flag removed in 1.10
|
||||
if k.RequireKubeconfig != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(
|
||||
kubeletPath.Child("requireKubeconfig"),
|
||||
"require-kubeconfig flag was removed in 1.10. (Please be sure you are not using a cluster config from `kops get cluster --full`)"))
|
||||
}
|
||||
}
|
||||
|
||||
if k.BootstrapKubeconfig != "" {
|
||||
if c.Spec.KubeAPIServer == nil {
|
||||
allErrs = append(allErrs, field.Required(kubeletPath.Root().Child("spec").Child("kubeAPIServer"), "bootstrap token require the NodeRestriction admissions controller"))
|
||||
}
|
||||
}
|
||||
|
||||
if k.TopologyManagerPolicy != "" {
|
||||
allErrs = append(allErrs, IsValidValue(kubeletPath.Child("topologyManagerPolicy"), &k.TopologyManagerPolicy, []string{"none", "best-effort", "restricted", "single-numa-node"})...)
|
||||
if !c.IsKubernetesGTE("1.18") {
|
||||
allErrs = append(allErrs, field.Forbidden(kubeletPath.Child("topologyManagerPolicy"), "topologyManagerPolicy requires at least Kubernetes 1.18"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func isExperimentalClusterDNS(k *kops.KubeletConfigSpec, dns *kops.KubeDNSConfig) bool {
|
||||
|
||||
return k != nil && k.ClusterDNS != dns.ServerIP && dns.NodeLocalDNS != nil && k.ClusterDNS != dns.NodeLocalDNS.LocalIP
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/kops/pkg/featureflag"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/validation"
|
||||
|
|
@ -41,6 +42,23 @@ import (
|
|||
|
||||
func newValidateCluster(cluster *kops.Cluster) field.ErrorList {
|
||||
allErrs := validation.ValidateObjectMeta(&cluster.ObjectMeta, false, validation.NameIsDNSSubdomain, field.NewPath("metadata"))
|
||||
|
||||
clusterName := cluster.ObjectMeta.Name
|
||||
if clusterName == "" {
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("objectMeta", "name"), "Cluster Name is required (e.g. --name=mycluster.myzone.com)"))
|
||||
} else {
|
||||
// Must be a dns name
|
||||
errs := utilvalidation.IsDNS1123Subdomain(clusterName)
|
||||
if len(errs) != 0 {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("objectMeta", "name"), clusterName, fmt.Sprintf("Cluster Name must be a valid DNS name (e.g. --name=mycluster.myzone.com) errors: %s", strings.Join(errs, ", "))))
|
||||
} else if !strings.Contains(clusterName, ".") {
|
||||
// Tolerate if this is a cluster we are importing for upgrade
|
||||
if cluster.ObjectMeta.Annotations[kops.AnnotationNameManagement] != kops.AnnotationValueManagementImported {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("objectMeta", "name"), clusterName, "Cluster Name must be a fully-qualified DNS name (e.g. --name=mycluster.myzone.com)"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, validateClusterSpec(&cluster.Spec, cluster, field.NewPath("spec"))...)
|
||||
|
||||
// Additional cloud-specific validation rules
|
||||
|
|
@ -79,6 +97,13 @@ func validateClusterSpec(spec *kops.ClusterSpec, c *kops.Cluster, fieldPath *fie
|
|||
allErrs = append(allErrs, validateCIDR(cidr, fieldPath.Child("additionalNetworkCIDRs").Index(i))...)
|
||||
}
|
||||
|
||||
if spec.Topology != nil {
|
||||
allErrs = append(allErrs, validateTopology(spec.Topology, fieldPath.Child("topology"))...)
|
||||
}
|
||||
|
||||
// UpdatePolicy
|
||||
allErrs = append(allErrs, IsValidValue(fieldPath.Child("updatePolicy"), spec.UpdatePolicy, []string{kops.UpdatePolicyExternal})...)
|
||||
|
||||
// Hooks
|
||||
for i := range spec.Hooks {
|
||||
allErrs = append(allErrs, validateHookSpec(&spec.Hooks[i], fieldPath.Child("hooks").Index(i))...)
|
||||
|
|
@ -91,7 +116,19 @@ func validateClusterSpec(spec *kops.ClusterSpec, c *kops.Cluster, fieldPath *fie
|
|||
}
|
||||
|
||||
if spec.KubeAPIServer != nil {
|
||||
allErrs = append(allErrs, validateKubeAPIServer(spec.KubeAPIServer, fieldPath.Child("kubeAPIServer"))...)
|
||||
allErrs = append(allErrs, validateKubeAPIServer(spec.KubeAPIServer, c, fieldPath.Child("kubeAPIServer"))...)
|
||||
}
|
||||
|
||||
if spec.KubeProxy != nil {
|
||||
allErrs = append(allErrs, validateKubeProxy(spec.KubeProxy, fieldPath.Child("kubeProxy"))...)
|
||||
}
|
||||
|
||||
if spec.Kubelet != nil {
|
||||
allErrs = append(allErrs, validateKubelet(spec.Kubelet, c, fieldPath.Child("kubelet"))...)
|
||||
}
|
||||
|
||||
if spec.MasterKubelet != nil {
|
||||
allErrs = append(allErrs, validateKubelet(spec.MasterKubelet, c, fieldPath.Child("masterKubelet"))...)
|
||||
}
|
||||
|
||||
if spec.Networking != nil {
|
||||
|
|
@ -101,6 +138,10 @@ func validateClusterSpec(spec *kops.ClusterSpec, c *kops.Cluster, fieldPath *fie
|
|||
}
|
||||
}
|
||||
|
||||
if spec.NodeAuthorization != nil {
|
||||
allErrs = append(allErrs, validateNodeAuthorization(spec.NodeAuthorization, c, fieldPath.Child("nodeAuthorization"))...)
|
||||
}
|
||||
|
||||
// IAM additionalPolicies
|
||||
if spec.AdditionalPolicies != nil {
|
||||
for k, v := range *spec.AdditionalPolicies {
|
||||
|
|
@ -131,6 +172,12 @@ func validateClusterSpec(spec *kops.ClusterSpec, c *kops.Cluster, fieldPath *fie
|
|||
allErrs = append(allErrs, validateDockerConfig(spec.Docker, fieldPath.Child("docker"))...)
|
||||
}
|
||||
|
||||
if spec.Assets != nil {
|
||||
if spec.Assets.ContainerProxy != nil && spec.Assets.ContainerRegistry != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(fieldPath.Child("assets", "containerProxy"), "containerProxy cannot be used in conjunction with containerRegistry"))
|
||||
}
|
||||
}
|
||||
|
||||
if spec.RollingUpdate != nil {
|
||||
allErrs = append(allErrs, validateRollingUpdate(spec.RollingUpdate, fieldPath.Child("rollingUpdate"), false)...)
|
||||
}
|
||||
|
|
@ -159,6 +206,42 @@ func validateCIDR(cidr string, fieldPath *field.Path) field.ErrorList {
|
|||
return allErrs
|
||||
}
|
||||
|
||||
func validateTopology(topology *kops.TopologySpec, fieldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if topology.Masters == "" {
|
||||
allErrs = append(allErrs, field.Required(fieldPath.Child("masters"), ""))
|
||||
} else {
|
||||
allErrs = append(allErrs, IsValidValue(fieldPath.Child("masters"), &topology.Masters, kops.SupportedTopologies)...)
|
||||
}
|
||||
|
||||
if topology.Nodes == "" {
|
||||
allErrs = append(allErrs, field.Required(fieldPath.Child("nodes"), ""))
|
||||
} else {
|
||||
allErrs = append(allErrs, IsValidValue(fieldPath.Child("nodes"), &topology.Nodes, kops.SupportedTopologies)...)
|
||||
}
|
||||
|
||||
if topology.Bastion != nil {
|
||||
bastion := topology.Bastion
|
||||
if topology.Masters == kops.TopologyPublic || topology.Nodes == kops.TopologyPublic {
|
||||
allErrs = append(allErrs, field.Forbidden(fieldPath.Child("bastion"), "bastion requires masters and nodes to have private topology"))
|
||||
}
|
||||
if bastion.IdleTimeoutSeconds != nil && *bastion.IdleTimeoutSeconds <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("bastion", "idleTimeoutSeconds"), *bastion.IdleTimeoutSeconds, "bastion idleTimeoutSeconds should be greater than zero"))
|
||||
}
|
||||
if bastion.IdleTimeoutSeconds != nil && *bastion.IdleTimeoutSeconds > 3600 {
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("bastion", "idleTimeoutSeconds"), *bastion.IdleTimeoutSeconds, "bastion idleTimeoutSeconds cannot be greater than one hour"))
|
||||
}
|
||||
}
|
||||
|
||||
if topology.DNS != nil {
|
||||
value := string(topology.DNS.Type)
|
||||
allErrs = append(allErrs, IsValidValue(fieldPath.Child("dns", "type"), &value, kops.SupportedDnsTypes)...)
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateSubnets(subnets []kops.ClusterSubnetSpec, fieldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
|
|
@ -210,6 +293,14 @@ func validateSubnet(subnet *kops.ClusterSubnetSpec, fieldPath *field.Path) field
|
|||
allErrs = append(allErrs, validateCIDR(subnet.CIDR, fieldPath.Child("cidr"))...)
|
||||
}
|
||||
|
||||
if subnet.Egress != "" {
|
||||
if !strings.HasPrefix(subnet.Egress, "nat-") && !strings.HasPrefix(subnet.Egress, "i-") && subnet.Egress != kops.EgressExternal {
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("egress"), subnet.Egress, "egress must be of type NAT Gateway or NAT EC2 Instance or 'External'"))
|
||||
}
|
||||
if subnet.Egress != kops.EgressExternal && subnet.Type != "Private" {
|
||||
allErrs = append(allErrs, field.Forbidden(fieldPath.Child("egress"), "egress can only be specified for private subnets"))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
|
|
@ -272,9 +363,16 @@ func validateExecContainerAction(v *kops.ExecContainerAction, fldPath *field.Pat
|
|||
return allErrs
|
||||
}
|
||||
|
||||
func validateKubeAPIServer(v *kops.KubeAPIServerConfig, fldPath *field.Path) field.ErrorList {
|
||||
func validateKubeAPIServer(v *kops.KubeAPIServerConfig, c *kops.Cluster, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if len(v.AdmissionControl) > 0 {
|
||||
if len(v.DisableAdmissionPlugins) > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("disableAdmissionPlugins"),
|
||||
"disableAdmissionPlugins is mutually exclusive, you cannot use both admissionControl and disableAdmissionPlugins together"))
|
||||
}
|
||||
}
|
||||
|
||||
proxyClientCertIsNil := v.ProxyClientCertFile == nil
|
||||
proxyClientKeyIsNil := v.ProxyClientKeyFile == nil
|
||||
|
||||
|
|
@ -299,6 +397,99 @@ func validateKubeAPIServer(v *kops.KubeAPIServerConfig, fldPath *field.Path) fie
|
|||
return allErrs
|
||||
}
|
||||
|
||||
func validateKubeProxy(k *kops.KubeProxyConfig, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
master := k.Master
|
||||
|
||||
for i, x := range k.IPVSExcludeCIDRS {
|
||||
if _, _, err := net.ParseCIDR(x); err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("ipvsExcludeCidrs").Index(i), x, "Invalid network CIDR"))
|
||||
}
|
||||
}
|
||||
|
||||
if master != "" && !isValidAPIServersURL(master) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("master"), master, "Not a valid APIServer URL"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateKubelet(k *kops.KubeletConfigSpec, c *kops.Cluster, kubeletPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if k != nil {
|
||||
|
||||
{
|
||||
// Flag removed in 1.6
|
||||
if k.APIServers != "" {
|
||||
allErrs = append(allErrs, field.Forbidden(
|
||||
kubeletPath.Child("apiServers"),
|
||||
"api-servers flag was removed in 1.6"))
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Flag removed in 1.10
|
||||
if k.RequireKubeconfig != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(
|
||||
kubeletPath.Child("requireKubeconfig"),
|
||||
"require-kubeconfig flag was removed in 1.10. (Please be sure you are not using a cluster config from `kops get cluster --full`)"))
|
||||
}
|
||||
}
|
||||
|
||||
if k.BootstrapKubeconfig != "" {
|
||||
if c.Spec.KubeAPIServer == nil {
|
||||
allErrs = append(allErrs, field.Required(kubeletPath.Root().Child("spec").Child("kubeAPIServer"), "bootstrap token require the NodeRestriction admissions controller"))
|
||||
}
|
||||
}
|
||||
|
||||
if k.TopologyManagerPolicy != "" {
|
||||
allErrs = append(allErrs, IsValidValue(kubeletPath.Child("topologyManagerPolicy"), &k.TopologyManagerPolicy, []string{"none", "best-effort", "restricted", "single-numa-node"})...)
|
||||
if !c.IsKubernetesGTE("1.18") {
|
||||
allErrs = append(allErrs, field.Forbidden(kubeletPath.Child("topologyManagerPolicy"), "topologyManagerPolicy requires at least Kubernetes 1.18"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateNodeAuthorization(n *kops.NodeAuthorizationSpec, c *kops.Cluster, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
// @check the feature gate is enabled for this
|
||||
if !featureflag.EnableNodeAuthorization.Enabled() {
|
||||
return field.ErrorList{field.Forbidden(fldPath, "node authorization is experimental feature; set `export KOPS_FEATURE_FLAGS=EnableNodeAuthorization`")}
|
||||
}
|
||||
|
||||
authorizerPath := fldPath.Child("nodeAuthorizer")
|
||||
if c.Spec.NodeAuthorization.NodeAuthorizer == nil {
|
||||
allErrs = append(allErrs, field.Required(authorizerPath, "no node authorization policy has been set"))
|
||||
} else {
|
||||
if c.Spec.NodeAuthorization.NodeAuthorizer.Port < 0 || n.NodeAuthorizer.Port >= 65535 {
|
||||
allErrs = append(allErrs, field.Invalid(authorizerPath.Child("port"), n.NodeAuthorizer.Port, "invalid port"))
|
||||
}
|
||||
if c.Spec.NodeAuthorization.NodeAuthorizer.Timeout != nil && n.NodeAuthorizer.Timeout.Duration <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(authorizerPath.Child("timeout"), n.NodeAuthorizer.Timeout, "must be greater than zero"))
|
||||
}
|
||||
if c.Spec.NodeAuthorization.NodeAuthorizer.TokenTTL != nil && n.NodeAuthorizer.TokenTTL.Duration < 0 {
|
||||
allErrs = append(allErrs, field.Invalid(authorizerPath.Child("tokenTTL"), n.NodeAuthorizer.TokenTTL, "must be greater than or equal to zero"))
|
||||
}
|
||||
|
||||
// @question: we could probably just default these settings in the model when the node-authorizer is enabled??
|
||||
if c.Spec.KubeAPIServer == nil {
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("spec", "kubeAPIServer"), "bootstrap token authentication is not enabled in the kube-apiserver"))
|
||||
} else if c.Spec.KubeAPIServer.EnableBootstrapAuthToken == nil {
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("spec", "kubeAPIServer", "enableBootstrapAuthToken"), "kube-apiserver has not been configured to use bootstrap tokens"))
|
||||
} else if !fi.BoolValue(c.Spec.KubeAPIServer.EnableBootstrapAuthToken) {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "kubeAPIServer", "enableBootstrapAuthToken"), "bootstrap tokens in the kube-apiserver has been disabled"))
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateNetworking(c *kops.ClusterSpec, v *kops.NetworkingSpec, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
optionTaken := false
|
||||
|
|
|
|||
|
|
@ -208,7 +208,12 @@ func TestValidateKubeAPIServer(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, g := range grid {
|
||||
errs := validateKubeAPIServer(&g.Input, field.NewPath("KubeAPIServer"))
|
||||
cluster := &kops.Cluster{
|
||||
Spec: kops.ClusterSpec{
|
||||
KubernetesVersion: "1.16.0",
|
||||
},
|
||||
}
|
||||
errs := validateKubeAPIServer(&g.Input, cluster, field.NewPath("KubeAPIServer"))
|
||||
|
||||
testErrors(t, g.Input, errs, g.ExpectedErrors)
|
||||
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ func TestValidate_ContainerRegistry_and_ContainerProxy_exclusivity(t *testing.T)
|
|||
|
||||
proxy := "https://proxy.example.com/"
|
||||
c.Spec.Assets.ContainerProxy = &proxy
|
||||
expectErrorFromValidate(t, c, "ContainerProxy cannot be used in conjunction with ContainerRegistry")
|
||||
expectErrorFromValidate(t, c, "containerProxy cannot be used in conjunction with containerRegistry")
|
||||
|
||||
c.Spec.Assets.ContainerRegistry = nil
|
||||
expectNoErrorFromValidate(t, c)
|
||||
|
|
|
|||
Loading…
Reference in New Issue