serving/pkg/webhook/validate_unstructured.go

112 lines
3.4 KiB
Go

/*
Copyright 2020 The Knative 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 webhook
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"knative.dev/pkg/apis"
"knative.dev/pkg/logging"
"knative.dev/serving/pkg/apis/config"
"knative.dev/serving/pkg/apis/serving"
v1 "knative.dev/serving/pkg/apis/serving/v1"
)
// DryRunMode represents possible values of the config.DryRunFeatureKey annotation
type DryRunMode string
const (
// DryRunEnabled will run the dryrun logic. Will succeed if dryrun is unsupported.
DryRunEnabled DryRunMode = "enabled"
// DryRunStrict will run the dryrun logic and fail if dryrun is not supported.
DryRunStrict DryRunMode = "strict"
)
// ValidateService runs extra validation on Service resources
func ValidateService(ctx context.Context, uns *unstructured.Unstructured) error {
return validateRevisionTemplate(ctx, uns)
}
// ValidateConfiguration runs extra validation on Configuration resources
func ValidateConfiguration(ctx context.Context, uns *unstructured.Unstructured) error {
// If owned by a service, skip validation for Configuration.
if uns.GetLabels()[serving.ServiceLabelKey] != "" {
return nil
}
return validateRevisionTemplate(ctx, uns)
}
func validateRevisionTemplate(ctx context.Context, uns *unstructured.Unstructured) error {
content := uns.UnstructuredContent()
mode := DryRunMode(uns.GetAnnotations()[config.DryRunFeatureKey])
features := config.FromContextOrDefaults(ctx).Features
switch features.PodSpecDryRun {
case config.Enabled:
if mode != DryRunStrict {
mode = DryRunEnabled
}
case config.Disabled:
return nil
}
// TODO(https://github.com/knative/serving/issues/3425): remove this guard once variations
// of this are well-tested. Only run extra validation for the dry-run test.
// This will be in place to while the feature is tested for compatibility and later removed.
if mode != DryRunStrict && mode != DryRunEnabled {
return nil
}
namespace := uns.GetNamespace()
// Decode and validate the RevisionTemplateSpec
val, found, err := unstructured.NestedFieldNoCopy(content, "spec", "template")
if err != nil {
return fmt.Errorf("could not traverse nested spec.template field: %w", err)
}
if !found {
logger := logging.FromContext(ctx)
logger.Warn("no spec.template found for unstructured")
return nil
}
templ, err := decodeTemplate(val)
if err != nil {
return err
}
if templ == nil || templ == (&v1.RevisionTemplateSpec{}) {
return nil // Don't need to validate empty templates
}
if apis.IsInUpdate(ctx) {
if uns, err := runtime.DefaultUnstructuredConverter.ToUnstructured(apis.GetBaseline(ctx)); err == nil {
if oldVal, found, _ := unstructured.NestedFieldNoCopy(uns, "spec", "template"); found &&
equality.Semantic.DeepEqual(val, oldVal) {
return nil // Don't validate no-change updates.
}
}
}
return validatePodSpec(ctx, templ.Spec, namespace, mode)
}