kube-scheduler: MVP configuration validation

We check that users haven't specified the kubeconfig file path, as
this file is created / managed by kOps.  We don't try to reuse the
upstream configuration validation, as this allows the user to specify
a partial configuration, and this means that we don't have to pull in
the upstream libraries.

We could in future accept the "correct" value or just treat providing
a value as a signal that kOps should not manage the file; for now we
are starting with the most restrictive configuration, as we can then
relax it in future if needed.
This commit is contained in:
justinsb 2022-05-13 12:34:28 -04:00
parent c82c30d8aa
commit e4d8dff835
2 changed files with 78 additions and 0 deletions

View File

@ -0,0 +1,58 @@
/*
Copyright 2022 The Kubernetes 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
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
)
func ValidateAdditionalObject(ctx context.Context, fieldPath *field.Path, u *unstructured.Unstructured) field.ErrorList {
var errors field.ErrorList
gvk := u.GroupVersionKind()
// Note: we use unstructured because:
// 1) it means we don't have to depend on types / validation code from multiple projects
// 2) we can more easily differentiate whether a field is set.
// 3) we can support partial configuration specification - just the fields we care about.
//
// It would be nice to be able to consume validation code e.g. via a container,
// so we could be more extensible.
errors = append(errors, validateAdditionalObjectKubescheduler(ctx, fieldPath, gvk, u)...)
return errors
}
func validateAdditionalObjectKubescheduler(ctx context.Context, fieldPath *field.Path, gvk schema.GroupVersionKind, u *unstructured.Unstructured) field.ErrorList {
var errors field.ErrorList
if gvk.Kind == "KubeSchedulerConfiguration" && gvk.Group == "kubescheduler.config.k8s.io" {
kubeconfig, found, err := unstructured.NestedString(u.Object, "clientConnection", "kubeconfig")
if err != nil {
errors = append(errors, field.Invalid(fieldPath.Child("clientConnection", "kubeconfig"), u, fmt.Sprintf("error reading field: %v", err)))
}
if found && kubeconfig != "" {
errors = append(errors, field.Invalid(fieldPath.Child("clientConnection", "kubeconfig"), kubeconfig, "value is controlled by kOps and should not be set"))
}
}
return errors
}

View File

@ -18,12 +18,15 @@ package vfsclientset
import (
"bytes"
"context"
"fmt"
"os"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/klog/v2"
"k8s.io/kops/pkg/acls"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/apis/kops/validation"
"k8s.io/kops/pkg/client/simple"
"k8s.io/kops/pkg/kubemanifest"
"k8s.io/kops/util/pkg/vfs"
@ -56,6 +59,23 @@ func newAddonsVFS(c *VFSClientset, cluster *kops.Cluster) *vfsAddonsClient {
// TODO: Offer partial replacement?
func (c *vfsAddonsClient) Replace(addons kubemanifest.ObjectList) error {
ctx := context.TODO()
for _, addon := range addons {
fieldPath := field.NewPath("addons")
if kind := addon.Kind(); kind != "" {
fieldPath = fieldPath.Child("kind=" + kind)
}
if name := addon.GetName(); name != "" {
fieldPath = fieldPath.Child("name=" + name)
}
errors := validation.ValidateAdditionalObject(ctx, fieldPath, addon.ToUnstructured())
if len(errors) != 0 {
return errors.ToAggregate()
}
}
b, err := addons.ToYAML()
if err != nil {
return err