Sitelet https://github.com/kubernetes/kops/pull/18690/files
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 36 additions & 14 deletions upup/pkg/fi/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,19 +196,41 @@ func (c *Context[T]) Render(a, e, changes Task[T]) error {

targetType := reflect.ValueOf(c.Target).Type()

var renderer *reflect.Method
// Probe renderers with literal method names only: enumerating the method set or passing a
// variable name to MethodByName would disable linker pruning of every unused exported method.
candidates := []struct {
name string
method reflect.Value
}{
{"Render", v.MethodByName("Render")},
{"RenderAWS", v.MethodByName("RenderAWS")},
{"RenderAzure", v.MethodByName("RenderAzure")},
{"RenderDO", v.MethodByName("RenderDO")},
{"RenderGCE", v.MethodByName("RenderGCE")},
{"RenderHetzner", v.MethodByName("RenderHetzner")},
{"RenderInstall", v.MethodByName("RenderInstall")},
{"RenderLinode", v.MethodByName("RenderLinode")},
{"RenderLocal", v.MethodByName("RenderLocal")},
{"RenderOpenstack", v.MethodByName("RenderOpenstack")},
{"RenderScw", v.MethodByName("RenderScw")},
{"RenderSubnet", v.MethodByName("RenderSubnet")},
{"RenderTerraform", v.MethodByName("RenderTerraform")},
}

var rendererName string
var renderer reflect.Value
var rendererArgs []reflect.Value

for i := 0; i < vType.NumMethod(); i++ {
method := vType.Method(i)
if !strings.HasPrefix(method.Name, "Render") {
for _, candidate := range candidates {
if !candidate.method.IsValid() {
continue
}
mType := candidate.method.Type()
match := true

var args []reflect.Value
for j := 0; j < method.Type.NumIn(); j++ {
arg := method.Type.In(j)
for j := 0; j < mType.NumIn(); j++ {
arg := mType.In(j)
if vType.ConvertibleTo(arg) {
continue
}
Expand All @@ -224,28 +246,28 @@ func (c *Context[T]) Render(a, e, changes Task[T]) error {
break
}
if match {
if renderer != nil {
if method.Name == "Render" {
if renderer.IsValid() {
if candidate.name == "Render" {
continue
}
if renderer.Name != "Render" {
if rendererName != "Render" {
return fmt.Errorf("found multiple Render methods that could be involved on %T", e)
}
}
renderer = &method
rendererName = candidate.name
renderer = candidate.method
rendererArgs = args
}

}
if renderer == nil {
if !renderer.IsValid() {
return fmt.Errorf("could not find Render method on type %T (target %T)", e, c.Target)
}
rendererArgs = append(rendererArgs, reflect.ValueOf(a))
rendererArgs = append(rendererArgs, reflect.ValueOf(e))
rendererArgs = append(rendererArgs, reflect.ValueOf(changes))
klog.V(11).Infof("Calling method %s on %T", renderer.Name, e)
m := v.MethodByName(renderer.Name)
rv := m.Call(rendererArgs)
klog.V(11).Infof("Calling method %s on %T", rendererName, e)
rv := renderer.Call(rendererArgs)
var rvErr error
if !rv[0].IsNil() {
rvErr = rv[0].Interface().(error)
Expand Down
35 changes: 20 additions & 15 deletions upup/pkg/fi/default_methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,25 +137,30 @@ func defaultDeltaRunMethod[T SubContext](e Task[T], c *Context[T]) error {
return nil
}

// invokeCheckChanges calls the checkChanges method by reflection
// invokeCheckChanges calls the CheckChanges method by reflection. Here and below, MethodByName
// must be called with literal names to keep linker method pruning working (see Context.Render).
func invokeCheckChanges[T SubContext](a, e, changes Task[T]) error {
rv, err := reflectutils.InvokeMethod(e, "CheckChanges", a, e, changes)
if err != nil {
return err
m := reflect.ValueOf(e).MethodByName("CheckChanges")
if !m.IsValid() {
return &reflectutils.MethodNotFoundError{Name: "CheckChanges", Target: e}
}
rv := m.Call([]reflect.Value{reflect.ValueOf(a), reflect.ValueOf(e), reflect.ValueOf(changes)})
var err error
if !rv[0].IsNil() {
err = rv[0].Interface().(error)
}
return err
}

// invokeFind calls the find method by reflection
// invokeFind calls the Find method by reflection.
func invokeFind[T SubContext](e Task[T], c *Context[T]) (Task[T], error) {
rv, err := reflectutils.InvokeMethod(e, "Find", c)
if err != nil {
return nil, err
m := reflect.ValueOf(e).MethodByName("Find")
if !m.IsValid() {
return nil, &reflectutils.MethodNotFoundError{Name: "Find", Target: e}
}
rv := m.Call([]reflect.Value{reflect.ValueOf(c)})
var task Task[T]
var err error
if !rv[0].IsNil() {
task = rv[0].Interface().(Task[T])
}
Expand All @@ -165,16 +170,16 @@ func invokeFind[T SubContext](e Task[T], c *Context[T]) (Task[T], error) {
return task, err
}

// invokeShouldCreate calls the ShouldCreate method by reflection, if it exists
// invokeShouldCreate calls the ShouldCreate method by reflection; tasks without it are created
// by default.
func invokeShouldCreate[T SubContext](a, e, changes Task[T]) (bool, error) {
rv, err := reflectutils.InvokeMethod(e, "ShouldCreate", a, e, changes)
if err != nil {
if reflectutils.IsMethodNotFound(err) {
return true, nil
}
return false, err
m := reflect.ValueOf(e).MethodByName("ShouldCreate")
if !m.IsValid() {
return true, nil
}
rv := m.Call([]reflect.Value{reflect.ValueOf(a), reflect.ValueOf(e), reflect.ValueOf(changes)})
shouldCreate := rv[0].Interface().(bool)
var err error
if !rv[1].IsNil() {
err = rv[1].Interface().(error)
}
Expand Down
22 changes: 0 additions & 22 deletions util/pkg/reflectutils/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,28 +57,6 @@ func JSONMergeStruct(dest, src interface{}) {
}
}

// InvokeMethod calls the specified method by reflection
func InvokeMethod(target interface{}, name string, args ...interface{}) ([]reflect.Value, error) {
v := reflect.ValueOf(target)

method, found := v.Type().MethodByName(name)
if !found {
return nil, &MethodNotFoundError{
Name: name,
Target: target,
}
}

var argValues []reflect.Value
for _, a := range args {
argValues = append(argValues, reflect.ValueOf(a))
}
klog.V(12).Infof("Calling method %s on %T", method.Name, target)
m := v.MethodByName(method.Name)
rv := m.Call(argValues)
return rv, nil
}

func BuildTypeName(t reflect.Type) string {
switch t.Kind() {
case reflect.Ptr:
Expand Down
Loading