func getDependencies(tasks map[string]Task, v reflect.Value) []Task { var dependencies []Task err := utils.ReflectRecursive(v, func(path string, f *reflect.StructField, v reflect.Value) error { if utils.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 == "" { // 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); ok { deps := hd.GetDependencies(tasks) dependencies = append(dependencies, deps...) } else if dep, ok := intf.(Task); ok { dependencies = append(dependencies, dep) } else if _, ok := intf.(Resource); ok { // Ignore: not a dependency (?) } else if _, ok := intf.(*ResourceHolder); ok { // Ignore: not a dependency (?) } else if _, ok := intf.(*pkix.Name); ok { // Ignore: not a dependency } else { return fmt.Errorf("Unhandled type for %q: %T", path, v.Interface()) } return utils.SkipReflection default: glog.Infof("Unhandled kind for %q: %T", path, v.Interface()) return fmt.Errorf("Unhandled kind for %q: %v", path, v.Kind()) } }) if err != nil { glog.Fatalf("unexpected error finding dependencies %v", err) } return dependencies }
// asString returns a human-readable string representation of the passed value func ValueAsString(value reflect.Value) string { b := &bytes.Buffer{} walker := func(path string, field *reflect.StructField, v reflect.Value) error { if utils.IsPrimitiveValue(v) || v.Kind() == reflect.String { fmt.Fprintf(b, "%v", v.Interface()) return utils.SkipReflection } switch v.Kind() { case reflect.Ptr, reflect.Interface, reflect.Slice, reflect.Map: if v.IsNil() { fmt.Fprintf(b, "<nil>") return utils.SkipReflection } } switch v.Kind() { case reflect.Ptr, reflect.Interface: return nil // descend into value case reflect.Slice: len := v.Len() fmt.Fprintf(b, "[") for i := 0; i < len; i++ { av := v.Index(i) if i != 0 { fmt.Fprintf(b, ", ") } fmt.Fprintf(b, "%s", ValueAsString(av)) } fmt.Fprintf(b, "]") return utils.SkipReflection case reflect.Map: keys := v.MapKeys() fmt.Fprintf(b, "{") for i, key := range keys { mv := v.MapIndex(key) if i != 0 { fmt.Fprintf(b, ", ") } fmt.Fprintf(b, "%s: %s", ValueAsString(key), ValueAsString(mv)) } fmt.Fprintf(b, "}") return utils.SkipReflection case reflect.Struct: intf := v.Addr().Interface() if _, ok := intf.(Resource); ok { fmt.Fprintf(b, "<resource>") } else if _, ok := intf.(*ResourceHolder); ok { fmt.Fprintf(b, "<resource>") } else if compareWithID, ok := intf.(CompareWithID); ok { id := compareWithID.CompareWithID() if id == nil { fmt.Fprintf(b, "id:<nil>") } else { fmt.Fprintf(b, "id:%s", *id) } } else { glog.V(4).Infof("Unhandled kind in asString for %q: %T", path, v.Interface()) fmt.Fprint(b, DebugAsJsonString(intf)) } return utils.SkipReflection default: glog.Infof("Unhandled kind in asString for %q: %T", path, v.Interface()) return fmt.Errorf("Unhandled kind for %q: %v", path, v.Kind()) } } err := utils.ReflectRecursive(value, walker) if err != nil { glog.Fatalf("unexpected error during reflective walk: %v", err) } return b.String() }
func (l *Loader) processDeferrals() error { for taskKey, task := range l.tasks { taskValue := reflect.ValueOf(task) err := utils.ReflectRecursive(taskValue, func(path string, f *reflect.StructField, v reflect.Value) error { if utils.IsPrimitiveValue(v) { return nil } if path == "" { // Don't process top-level value return nil } switch v.Kind() { case reflect.Interface, reflect.Ptr: if v.CanInterface() && !v.IsNil() { // TODO: Can we / should we use a type-switch statement intf := v.Interface() if hn, ok := intf.(fi.HasName); ok { name := hn.GetName() if name != nil { primary := l.tasks[*name] if primary == nil { glog.Infof("Known tasks:") for k := range l.tasks { glog.Infof(" %s", k) } return fmt.Errorf("Unable to find task %q, referenced from %s:%s", *name, taskKey, path) } glog.V(11).Infof("Replacing task %q at %s:%s", *name, taskKey, path) v.Set(reflect.ValueOf(primary)) } return utils.SkipReflection } else if rh, ok := intf.(*fi.ResourceHolder); ok { //Resources can contain template 'arguments', separated by spaces // <resourcename> <arg1> <arg2> tokens := strings.Split(rh.Name, " ") match := tokens[0] args := tokens[1:] match = strings.TrimPrefix(match, "resources/") resource := l.Resources[match] if resource == nil { glog.Infof("Known resources:") for k := range l.Resources { glog.Infof(" %s", k) } return fmt.Errorf("Unable to find resource %q, referenced from %s:%s", rh.Name, taskKey, path) } err := l.populateResource(rh, resource, args) if err != nil { return fmt.Errorf("error setting resource value: %v", err) } return utils.SkipReflection } } } return nil }) if err != nil { return fmt.Errorf("unexpected error resolving task %q: %v", taskKey, err) } } return nil }