mirror of https://github.com/kubernetes/kops.git
				
				
				
			
		
			
				
	
	
		
			151 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
/*
 | 
						|
Copyright 2019 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 fi
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
 | 
						|
	"k8s.io/klog/v2"
 | 
						|
	"k8s.io/kops/util/pkg/reflectutils"
 | 
						|
)
 | 
						|
 | 
						|
type HasDependencies[T SubContext] interface {
 | 
						|
	GetDependencies(tasks map[string]Task[T]) []Task[T]
 | 
						|
}
 | 
						|
 | 
						|
type CloudupHasDependencies = HasDependencies[CloudupSubContext]
 | 
						|
type InstallHasDependencies = HasDependencies[InstallSubContext]
 | 
						|
type NodeupHasDependencies = HasDependencies[NodeupSubContext]
 | 
						|
 | 
						|
// NotADependency is a marker type to prevent FindTaskDependencies() from considering it a potential dependency.
 | 
						|
type NotADependency[T SubContext] struct{}
 | 
						|
 | 
						|
type NodeupNotADependency = NotADependency[NodeupSubContext]
 | 
						|
type CloudupNotADependency = NotADependency[CloudupSubContext]
 | 
						|
 | 
						|
var _ CloudupHasDependencies = &CloudupNotADependency{}
 | 
						|
var _ NodeupHasDependencies = &NodeupNotADependency{}
 | 
						|
 | 
						|
func (NotADependency[T]) GetDependencies(map[string]Task[T]) []Task[T] {
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// FindTaskDependencies returns a map from each task's key to the discovered list of dependencies
 | 
						|
func FindTaskDependencies[T SubContext](tasks map[string]Task[T]) map[string][]string {
 | 
						|
	taskToId := make(map[interface{}]string)
 | 
						|
	for k, t := range tasks {
 | 
						|
		taskToId[t] = k
 | 
						|
	}
 | 
						|
 | 
						|
	edges := make(map[string][]string)
 | 
						|
 | 
						|
	for k, t := range tasks {
 | 
						|
		task := t
 | 
						|
 | 
						|
		var dependencies []Task[T]
 | 
						|
		if hd, ok := task.(HasDependencies[T]); ok {
 | 
						|
			dependencies = hd.GetDependencies(tasks)
 | 
						|
		} else {
 | 
						|
			dependencies = reflectForDependencies(tasks, task)
 | 
						|
		}
 | 
						|
 | 
						|
		var dependencyKeys []string
 | 
						|
		for _, dep := range dependencies {
 | 
						|
			dependencyKey, found := taskToId[dep]
 | 
						|
			if !found {
 | 
						|
				klog.Fatalf("dependency not found: %v", dep)
 | 
						|
			}
 | 
						|
			dependencyKeys = append(dependencyKeys, dependencyKey)
 | 
						|
		}
 | 
						|
 | 
						|
		edges[k] = dependencyKeys
 | 
						|
	}
 | 
						|
 | 
						|
	klog.V(4).Infof("Dependencies:")
 | 
						|
	for k, v := range edges {
 | 
						|
		klog.V(4).Infof("\t%s:\t%v", k, v)
 | 
						|
	}
 | 
						|
 | 
						|
	return edges
 | 
						|
}
 | 
						|
 | 
						|
func reflectForDependencies[T SubContext](tasks map[string]Task[T], task Task[T]) []Task[T] {
 | 
						|
	v := reflect.ValueOf(task).Elem()
 | 
						|
	return getDependencies(tasks, v)
 | 
						|
}
 | 
						|
 | 
						|
// FindDependencies will try to infer dependencies for an arbitrary object
 | 
						|
func FindDependencies[T SubContext](tasks map[string]Task[T], o interface{}) []Task[T] {
 | 
						|
	if hd, ok := o.(HasDependencies[T]); ok {
 | 
						|
		return hd.GetDependencies(tasks)
 | 
						|
	}
 | 
						|
 | 
						|
	v := reflect.ValueOf(o).Elem()
 | 
						|
	return getDependencies(tasks, v)
 | 
						|
}
 | 
						|
 | 
						|
func getDependencies[T SubContext](tasks map[string]Task[T], v reflect.Value) []Task[T] {
 | 
						|
	var dependencies []Task[T]
 | 
						|
 | 
						|
	visitor := func(path *reflectutils.FieldPath, f *reflect.StructField, v reflect.Value) error {
 | 
						|
		if reflectutils.IsPrimitiveValue(v) {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		switch v.Kind() {
 | 
						|
		case reflect.String:
 | 
						|
			return nil
 | 
						|
 | 
						|
		case reflect.Interface, reflect.Ptr, reflect.Slice, reflect.Map:
 | 
						|
			// The recursive walk will descend into this; we can ignore here
 | 
						|
			return nil
 | 
						|
 | 
						|
		case reflect.Struct:
 | 
						|
			if path.IsEmpty() {
 | 
						|
				// Ignore self - we are a struct, but not our own dependency!
 | 
						|
				return nil
 | 
						|
			}
 | 
						|
 | 
						|
			// TODO: Can we / should we use a type-switch statement
 | 
						|
			intf := v.Addr().Interface()
 | 
						|
			if hd, ok := intf.(HasDependencies[T]); ok {
 | 
						|
				deps := hd.GetDependencies(tasks)
 | 
						|
				dependencies = append(dependencies, deps...)
 | 
						|
			} else if dep, ok := intf.(Task[T]); ok {
 | 
						|
				dependencies = append(dependencies, dep)
 | 
						|
			} else if _, ok := intf.(Resource); ok {
 | 
						|
				// Ignore: not a dependency, unless we explicitly implement HasDependencies (e.g. TaskDependentResource)
 | 
						|
			} else {
 | 
						|
				return fmt.Errorf("Unhandled type for %q: %T", path, v.Interface())
 | 
						|
			}
 | 
						|
			return reflectutils.SkipReflection
 | 
						|
 | 
						|
		default:
 | 
						|
			klog.Infof("Unhandled kind for %q: %T", path, v.Interface())
 | 
						|
			return fmt.Errorf("Unhandled kind for %q: %v", path, v.Kind())
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	err := reflectutils.ReflectRecursive(v, visitor, &reflectutils.ReflectOptions{DeprecatedDoubleVisit: true})
 | 
						|
	if err != nil {
 | 
						|
		klog.Fatalf("unexpected error finding dependencies %v", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return dependencies
 | 
						|
}
 |