Skip to content

Commit

Permalink
UPSTREAM: <carry>: Apply shared defaulters to CRD-based routes.
Browse files Browse the repository at this point in the history
OpenShift-Rebase-Source: bf7e23e
  • Loading branch information
benluddy authored and sanchezl committed Dec 20, 2022
1 parent eb99b32 commit 2d939ea
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var AllCustomResourceValidators = []string{
network.PluginName,
apirequestcount.PluginName,
node.PluginName,
route.DefaultingPluginName,
route.PluginName,

// the kubecontrollermanager operator resource has to exist in order to run deployments to deploy admission webhooks.
Expand Down Expand Up @@ -84,4 +85,5 @@ func RegisterCustomResourceValidation(plugins *admission.Plugins) {
// served via CRD. Most OpenShift flavors (including vanilla) will continue to do validation
// and defaulting inside openshift-apiserver.
route.Register(plugins)
route.RegisterDefaulting(plugins)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package route

import (
"context"
"fmt"
"io"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"

v1 "github.com/openshift/api/route/v1"
)

const (
DefaultingPluginName = "route.openshift.io/DefaultRoute"
)

func RegisterDefaulting(plugins *admission.Plugins) {
plugins.Register(DefaultingPluginName, func(_ io.Reader) (admission.Interface, error) {
return &defaultRoute{
Handler: admission.NewHandler(admission.Create, admission.Update),
}, nil
})
}

type defaultRoute struct {
*admission.Handler
}

var _ admission.MutationInterface = &defaultRoute{}

func (a *defaultRoute) Admit(ctx context.Context, attributes admission.Attributes, _ admission.ObjectInterfaces) error {
if attributes.GetResource().GroupResource() != (schema.GroupResource{Group: "route.openshift.io", Resource: "routes"}) {
return nil
}

if len(attributes.GetSubresource()) > 0 {
return nil
}

u, ok := attributes.GetObject().(runtime.Unstructured)
if !ok {
// If a request to the resource routes.route.openshift.io is subject to
// kube-apiserver admission, that should imply that the route API is being served as
// CRs and the request body should have been unmarshaled into an unstructured
// object.
return fmt.Errorf("object being admitted is of type %T and does not implement runtime.Unstructured", attributes.GetObject())
}

var external v1.Route
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &external); err != nil {
return err
}

SetObjectDefaults_Route(&external)

content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&external)
if err != nil {
return err
}
u.SetUnstructuredContent(content)

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package route

import (
routev1 "github.com/openshift/api/route/v1"
"github.com/openshift/library-go/pkg/route/defaulting"
)

// Defaulters defined in github.com/openshift/library-go/pkg/route/defaulting are not recongized by
// codegen (make update). This file MUST contain duplicates of each defaulter function defined in
// library-go, with the body of each function defined here delegating to its library-go
// counterpart. Missing or extra defaulters here will introduce differences between Route as a CRD
// (MicroShift) and Route as an aggregated API of openshift-apiserver.

func SetDefaults_RouteSpec(obj *routev1.RouteSpec) {
defaulting.SetDefaults_RouteSpec(obj)
}

func SetDefaults_RouteTargetReference(obj *routev1.RouteTargetReference) {
defaulting.SetDefaults_RouteTargetReference(obj)
}

func SetDefaults_TLSConfig(obj *routev1.TLSConfig) {
defaulting.SetDefaults_TLSConfig(obj)
}

func SetDefaults_RouteIngress(obj *routev1.RouteIngress) {
defaulting.SetDefaults_RouteIngress(obj)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package route

import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/fs"
"strings"
"testing"

"k8s.io/apimachinery/pkg/util/sets"
)

func TestDuplicatedDefaulters(t *testing.T) {
expected, err := findDefaultersInPackage("../../../../vendor/github.com/openshift/library-go/pkg/route/defaulting")
if err != nil {
t.Fatalf("error finding expected manual defaulters: %v", err)
}

actual, err := findDefaultersInPackage(".")
if err != nil {
t.Fatalf("error finding actual manual defaulters: %v", err)
}

for _, missing := range expected.Difference(actual).List() {
t.Errorf("missing local duplicate of library-go defaulter %q", missing)
}

for _, extra := range actual.Difference(expected).List() {
t.Errorf("found local defaulter %q without library-go counterpart", extra)
}
}

// findDefaultersInPackage parses the source of the Go package at the given path and returns the
// names of all manual defaulter functions it declares. Package function declarations can't be
// enumerated using reflection.
func findDefaultersInPackage(path string) (sets.String, error) {
pkgs, err := parser.ParseDir(token.NewFileSet(), path, func(fi fs.FileInfo) bool {
return !strings.HasSuffix(fi.Name(), "_test.go")
}, 0)
if err != nil {
return nil, fmt.Errorf("failed to parse source of package at %q: %v", path, err)
}
if len(pkgs) != 1 {
return nil, fmt.Errorf("expected exactly 1 package for all sources in %q, got %d", path, len(pkgs))
}

defaulters := sets.NewString()
for _, pkg := range pkgs {
ast.Inspect(pkg, func(node ast.Node) bool {
switch typed := node.(type) {
case *ast.Package, *ast.File:
return true
case *ast.FuncDecl:
if typed.Recv == nil && strings.HasPrefix(typed.Name.Name, "SetDefaults_") {
defaulters.Insert(typed.Name.Name)
}
return false
default:
return false
}
})
}
return defaulters, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// +k8s:defaulter-gen=TypeMeta
// +k8s:defaulter-gen-input=github.com/openshift/api/route/v1

package route

0 comments on commit 2d939ea

Please sign in to comment.