github.com/smithx10/nomad@v0.9.1-rc1/nomad/structs/diff.go (about) 1 package structs 2 3 import ( 4 "fmt" 5 "reflect" 6 "sort" 7 "strings" 8 9 "github.com/hashicorp/nomad/helper/flatmap" 10 "github.com/mitchellh/hashstructure" 11 ) 12 13 // DiffType denotes the type of a diff object. 14 type DiffType string 15 16 var ( 17 DiffTypeNone DiffType = "None" 18 DiffTypeAdded DiffType = "Added" 19 DiffTypeDeleted DiffType = "Deleted" 20 DiffTypeEdited DiffType = "Edited" 21 ) 22 23 func (d DiffType) Less(other DiffType) bool { 24 // Edited > Added > Deleted > None 25 // But we do a reverse sort 26 if d == other { 27 return false 28 } 29 30 if d == DiffTypeEdited { 31 return true 32 } else if other == DiffTypeEdited { 33 return false 34 } else if d == DiffTypeAdded { 35 return true 36 } else if other == DiffTypeAdded { 37 return false 38 } else if d == DiffTypeDeleted { 39 return true 40 } else if other == DiffTypeDeleted { 41 return false 42 } 43 44 return true 45 } 46 47 // JobDiff contains the diff of two jobs. 48 type JobDiff struct { 49 Type DiffType 50 ID string 51 Fields []*FieldDiff 52 Objects []*ObjectDiff 53 TaskGroups []*TaskGroupDiff 54 } 55 56 // Diff returns a diff of two jobs and a potential error if the Jobs are not 57 // diffable. If contextual diff is enabled, objects within the job will contain 58 // field information even if unchanged. 59 func (j *Job) Diff(other *Job, contextual bool) (*JobDiff, error) { 60 // COMPAT: Remove "Update" in 0.7.0. Update pushed down to task groups 61 // in 0.6.0 62 diff := &JobDiff{Type: DiffTypeNone} 63 var oldPrimitiveFlat, newPrimitiveFlat map[string]string 64 filter := []string{"ID", "Status", "StatusDescription", "Version", "Stable", "CreateIndex", 65 "ModifyIndex", "JobModifyIndex", "Update", "SubmitTime"} 66 67 if j == nil && other == nil { 68 return diff, nil 69 } else if j == nil { 70 j = &Job{} 71 diff.Type = DiffTypeAdded 72 newPrimitiveFlat = flatmap.Flatten(other, filter, true) 73 diff.ID = other.ID 74 } else if other == nil { 75 other = &Job{} 76 diff.Type = DiffTypeDeleted 77 oldPrimitiveFlat = flatmap.Flatten(j, filter, true) 78 diff.ID = j.ID 79 } else { 80 if j.ID != other.ID { 81 return nil, fmt.Errorf("can not diff jobs with different IDs: %q and %q", j.ID, other.ID) 82 } 83 84 oldPrimitiveFlat = flatmap.Flatten(j, filter, true) 85 newPrimitiveFlat = flatmap.Flatten(other, filter, true) 86 diff.ID = other.ID 87 } 88 89 // Diff the primitive fields. 90 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, false) 91 92 // Datacenters diff 93 if setDiff := stringSetDiff(j.Datacenters, other.Datacenters, "Datacenters", contextual); setDiff != nil && setDiff.Type != DiffTypeNone { 94 diff.Objects = append(diff.Objects, setDiff) 95 } 96 97 // Constraints diff 98 conDiff := primitiveObjectSetDiff( 99 interfaceSlice(j.Constraints), 100 interfaceSlice(other.Constraints), 101 []string{"str"}, 102 "Constraint", 103 contextual) 104 if conDiff != nil { 105 diff.Objects = append(diff.Objects, conDiff...) 106 } 107 108 // Affinities diff 109 affinitiesDiff := primitiveObjectSetDiff( 110 interfaceSlice(j.Affinities), 111 interfaceSlice(other.Affinities), 112 []string{"str"}, 113 "Affinity", 114 contextual) 115 if affinitiesDiff != nil { 116 diff.Objects = append(diff.Objects, affinitiesDiff...) 117 } 118 119 // Task groups diff 120 tgs, err := taskGroupDiffs(j.TaskGroups, other.TaskGroups, contextual) 121 if err != nil { 122 return nil, err 123 } 124 diff.TaskGroups = tgs 125 126 // Periodic diff 127 if pDiff := primitiveObjectDiff(j.Periodic, other.Periodic, nil, "Periodic", contextual); pDiff != nil { 128 diff.Objects = append(diff.Objects, pDiff) 129 } 130 131 // ParameterizedJob diff 132 if cDiff := parameterizedJobDiff(j.ParameterizedJob, other.ParameterizedJob, contextual); cDiff != nil { 133 diff.Objects = append(diff.Objects, cDiff) 134 } 135 136 // Check to see if there is a diff. We don't use reflect because we are 137 // filtering quite a few fields that will change on each diff. 138 if diff.Type == DiffTypeNone { 139 for _, fd := range diff.Fields { 140 if fd.Type != DiffTypeNone { 141 diff.Type = DiffTypeEdited 142 break 143 } 144 } 145 } 146 147 if diff.Type == DiffTypeNone { 148 for _, od := range diff.Objects { 149 if od.Type != DiffTypeNone { 150 diff.Type = DiffTypeEdited 151 break 152 } 153 } 154 } 155 156 if diff.Type == DiffTypeNone { 157 for _, tg := range diff.TaskGroups { 158 if tg.Type != DiffTypeNone { 159 diff.Type = DiffTypeEdited 160 break 161 } 162 } 163 } 164 165 return diff, nil 166 } 167 168 func (j *JobDiff) GoString() string { 169 out := fmt.Sprintf("Job %q (%s):\n", j.ID, j.Type) 170 171 for _, f := range j.Fields { 172 out += fmt.Sprintf("%#v\n", f) 173 } 174 175 for _, o := range j.Objects { 176 out += fmt.Sprintf("%#v\n", o) 177 } 178 179 for _, tg := range j.TaskGroups { 180 out += fmt.Sprintf("%#v\n", tg) 181 } 182 183 return out 184 } 185 186 // TaskGroupDiff contains the diff of two task groups. 187 type TaskGroupDiff struct { 188 Type DiffType 189 Name string 190 Fields []*FieldDiff 191 Objects []*ObjectDiff 192 Tasks []*TaskDiff 193 Updates map[string]uint64 194 } 195 196 // Diff returns a diff of two task groups. If contextual diff is enabled, 197 // objects' fields will be stored even if no diff occurred as long as one field 198 // changed. 199 func (tg *TaskGroup) Diff(other *TaskGroup, contextual bool) (*TaskGroupDiff, error) { 200 diff := &TaskGroupDiff{Type: DiffTypeNone} 201 var oldPrimitiveFlat, newPrimitiveFlat map[string]string 202 filter := []string{"Name"} 203 204 if tg == nil && other == nil { 205 return diff, nil 206 } else if tg == nil { 207 tg = &TaskGroup{} 208 diff.Type = DiffTypeAdded 209 diff.Name = other.Name 210 newPrimitiveFlat = flatmap.Flatten(other, filter, true) 211 } else if other == nil { 212 other = &TaskGroup{} 213 diff.Type = DiffTypeDeleted 214 diff.Name = tg.Name 215 oldPrimitiveFlat = flatmap.Flatten(tg, filter, true) 216 } else { 217 if !reflect.DeepEqual(tg, other) { 218 diff.Type = DiffTypeEdited 219 } 220 if tg.Name != other.Name { 221 return nil, fmt.Errorf("can not diff task groups with different names: %q and %q", tg.Name, other.Name) 222 } 223 diff.Name = other.Name 224 oldPrimitiveFlat = flatmap.Flatten(tg, filter, true) 225 newPrimitiveFlat = flatmap.Flatten(other, filter, true) 226 } 227 228 // Diff the primitive fields. 229 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, false) 230 231 // Constraints diff 232 conDiff := primitiveObjectSetDiff( 233 interfaceSlice(tg.Constraints), 234 interfaceSlice(other.Constraints), 235 []string{"str"}, 236 "Constraint", 237 contextual) 238 if conDiff != nil { 239 diff.Objects = append(diff.Objects, conDiff...) 240 } 241 242 // Affinities diff 243 affinitiesDiff := primitiveObjectSetDiff( 244 interfaceSlice(tg.Affinities), 245 interfaceSlice(other.Affinities), 246 []string{"str"}, 247 "Affinity", 248 contextual) 249 if affinitiesDiff != nil { 250 diff.Objects = append(diff.Objects, affinitiesDiff...) 251 } 252 253 // Restart policy diff 254 rDiff := primitiveObjectDiff(tg.RestartPolicy, other.RestartPolicy, nil, "RestartPolicy", contextual) 255 if rDiff != nil { 256 diff.Objects = append(diff.Objects, rDiff) 257 } 258 259 // Reschedule policy diff 260 reschedDiff := primitiveObjectDiff(tg.ReschedulePolicy, other.ReschedulePolicy, nil, "ReschedulePolicy", contextual) 261 if reschedDiff != nil { 262 diff.Objects = append(diff.Objects, reschedDiff) 263 } 264 265 // EphemeralDisk diff 266 diskDiff := primitiveObjectDiff(tg.EphemeralDisk, other.EphemeralDisk, nil, "EphemeralDisk", contextual) 267 if diskDiff != nil { 268 diff.Objects = append(diff.Objects, diskDiff) 269 } 270 271 // Update diff 272 // COMPAT: Remove "Stagger" in 0.7.0. 273 if uDiff := primitiveObjectDiff(tg.Update, other.Update, []string{"Stagger"}, "Update", contextual); uDiff != nil { 274 diff.Objects = append(diff.Objects, uDiff) 275 } 276 277 // Tasks diff 278 tasks, err := taskDiffs(tg.Tasks, other.Tasks, contextual) 279 if err != nil { 280 return nil, err 281 } 282 diff.Tasks = tasks 283 284 return diff, nil 285 } 286 287 func (tg *TaskGroupDiff) GoString() string { 288 out := fmt.Sprintf("Group %q (%s):\n", tg.Name, tg.Type) 289 290 if len(tg.Updates) != 0 { 291 out += "Updates {\n" 292 for update, count := range tg.Updates { 293 out += fmt.Sprintf("%d %s\n", count, update) 294 } 295 out += "}\n" 296 } 297 298 for _, f := range tg.Fields { 299 out += fmt.Sprintf("%#v\n", f) 300 } 301 302 for _, o := range tg.Objects { 303 out += fmt.Sprintf("%#v\n", o) 304 } 305 306 for _, t := range tg.Tasks { 307 out += fmt.Sprintf("%#v\n", t) 308 } 309 310 return out 311 } 312 313 // TaskGroupDiffs diffs two sets of task groups. If contextual diff is enabled, 314 // objects' fields will be stored even if no diff occurred as long as one field 315 // changed. 316 func taskGroupDiffs(old, new []*TaskGroup, contextual bool) ([]*TaskGroupDiff, error) { 317 oldMap := make(map[string]*TaskGroup, len(old)) 318 newMap := make(map[string]*TaskGroup, len(new)) 319 for _, o := range old { 320 oldMap[o.Name] = o 321 } 322 for _, n := range new { 323 newMap[n.Name] = n 324 } 325 326 var diffs []*TaskGroupDiff 327 for name, oldGroup := range oldMap { 328 // Diff the same, deleted and edited 329 diff, err := oldGroup.Diff(newMap[name], contextual) 330 if err != nil { 331 return nil, err 332 } 333 diffs = append(diffs, diff) 334 } 335 336 for name, newGroup := range newMap { 337 // Diff the added 338 if old, ok := oldMap[name]; !ok { 339 diff, err := old.Diff(newGroup, contextual) 340 if err != nil { 341 return nil, err 342 } 343 diffs = append(diffs, diff) 344 } 345 } 346 347 sort.Sort(TaskGroupDiffs(diffs)) 348 return diffs, nil 349 } 350 351 // For sorting TaskGroupDiffs 352 type TaskGroupDiffs []*TaskGroupDiff 353 354 func (tg TaskGroupDiffs) Len() int { return len(tg) } 355 func (tg TaskGroupDiffs) Swap(i, j int) { tg[i], tg[j] = tg[j], tg[i] } 356 func (tg TaskGroupDiffs) Less(i, j int) bool { return tg[i].Name < tg[j].Name } 357 358 // TaskDiff contains the diff of two Tasks 359 type TaskDiff struct { 360 Type DiffType 361 Name string 362 Fields []*FieldDiff 363 Objects []*ObjectDiff 364 Annotations []string 365 } 366 367 // Diff returns a diff of two tasks. If contextual diff is enabled, objects 368 // within the task will contain field information even if unchanged. 369 func (t *Task) Diff(other *Task, contextual bool) (*TaskDiff, error) { 370 diff := &TaskDiff{Type: DiffTypeNone} 371 var oldPrimitiveFlat, newPrimitiveFlat map[string]string 372 filter := []string{"Name", "Config"} 373 374 if t == nil && other == nil { 375 return diff, nil 376 } else if t == nil { 377 t = &Task{} 378 diff.Type = DiffTypeAdded 379 diff.Name = other.Name 380 newPrimitiveFlat = flatmap.Flatten(other, filter, true) 381 } else if other == nil { 382 other = &Task{} 383 diff.Type = DiffTypeDeleted 384 diff.Name = t.Name 385 oldPrimitiveFlat = flatmap.Flatten(t, filter, true) 386 } else { 387 if !reflect.DeepEqual(t, other) { 388 diff.Type = DiffTypeEdited 389 } 390 if t.Name != other.Name { 391 return nil, fmt.Errorf("can not diff tasks with different names: %q and %q", t.Name, other.Name) 392 } 393 diff.Name = other.Name 394 oldPrimitiveFlat = flatmap.Flatten(t, filter, true) 395 newPrimitiveFlat = flatmap.Flatten(other, filter, true) 396 } 397 398 // Diff the primitive fields. 399 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, false) 400 401 // Constraints diff 402 conDiff := primitiveObjectSetDiff( 403 interfaceSlice(t.Constraints), 404 interfaceSlice(other.Constraints), 405 []string{"str"}, 406 "Constraint", 407 contextual) 408 if conDiff != nil { 409 diff.Objects = append(diff.Objects, conDiff...) 410 } 411 412 // Affinities diff 413 affinitiesDiff := primitiveObjectSetDiff( 414 interfaceSlice(t.Affinities), 415 interfaceSlice(other.Affinities), 416 []string{"str"}, 417 "Affinity", 418 contextual) 419 if affinitiesDiff != nil { 420 diff.Objects = append(diff.Objects, affinitiesDiff...) 421 } 422 423 // Config diff 424 if cDiff := configDiff(t.Config, other.Config, contextual); cDiff != nil { 425 diff.Objects = append(diff.Objects, cDiff) 426 } 427 428 // Resources diff 429 if rDiff := t.Resources.Diff(other.Resources, contextual); rDiff != nil { 430 diff.Objects = append(diff.Objects, rDiff) 431 } 432 433 // LogConfig diff 434 lDiff := primitiveObjectDiff(t.LogConfig, other.LogConfig, nil, "LogConfig", contextual) 435 if lDiff != nil { 436 diff.Objects = append(diff.Objects, lDiff) 437 } 438 439 // Dispatch payload diff 440 dDiff := primitiveObjectDiff(t.DispatchPayload, other.DispatchPayload, nil, "DispatchPayload", contextual) 441 if dDiff != nil { 442 diff.Objects = append(diff.Objects, dDiff) 443 } 444 445 // Artifacts diff 446 diffs := primitiveObjectSetDiff( 447 interfaceSlice(t.Artifacts), 448 interfaceSlice(other.Artifacts), 449 nil, 450 "Artifact", 451 contextual) 452 if diffs != nil { 453 diff.Objects = append(diff.Objects, diffs...) 454 } 455 456 // Services diff 457 if sDiffs := serviceDiffs(t.Services, other.Services, contextual); sDiffs != nil { 458 diff.Objects = append(diff.Objects, sDiffs...) 459 } 460 461 // Vault diff 462 vDiff := vaultDiff(t.Vault, other.Vault, contextual) 463 if vDiff != nil { 464 diff.Objects = append(diff.Objects, vDiff) 465 } 466 467 // Template diff 468 tmplDiffs := primitiveObjectSetDiff( 469 interfaceSlice(t.Templates), 470 interfaceSlice(other.Templates), 471 nil, 472 "Template", 473 contextual) 474 if tmplDiffs != nil { 475 diff.Objects = append(diff.Objects, tmplDiffs...) 476 } 477 478 return diff, nil 479 } 480 481 func (t *TaskDiff) GoString() string { 482 var out string 483 if len(t.Annotations) == 0 { 484 out = fmt.Sprintf("Task %q (%s):\n", t.Name, t.Type) 485 } else { 486 out = fmt.Sprintf("Task %q (%s) (%s):\n", t.Name, t.Type, strings.Join(t.Annotations, ",")) 487 } 488 489 for _, f := range t.Fields { 490 out += fmt.Sprintf("%#v\n", f) 491 } 492 493 for _, o := range t.Objects { 494 out += fmt.Sprintf("%#v\n", o) 495 } 496 497 return out 498 } 499 500 // taskDiffs diffs a set of tasks. If contextual diff is enabled, unchanged 501 // fields within objects nested in the tasks will be returned. 502 func taskDiffs(old, new []*Task, contextual bool) ([]*TaskDiff, error) { 503 oldMap := make(map[string]*Task, len(old)) 504 newMap := make(map[string]*Task, len(new)) 505 for _, o := range old { 506 oldMap[o.Name] = o 507 } 508 for _, n := range new { 509 newMap[n.Name] = n 510 } 511 512 var diffs []*TaskDiff 513 for name, oldGroup := range oldMap { 514 // Diff the same, deleted and edited 515 diff, err := oldGroup.Diff(newMap[name], contextual) 516 if err != nil { 517 return nil, err 518 } 519 diffs = append(diffs, diff) 520 } 521 522 for name, newGroup := range newMap { 523 // Diff the added 524 if old, ok := oldMap[name]; !ok { 525 diff, err := old.Diff(newGroup, contextual) 526 if err != nil { 527 return nil, err 528 } 529 diffs = append(diffs, diff) 530 } 531 } 532 533 sort.Sort(TaskDiffs(diffs)) 534 return diffs, nil 535 } 536 537 // For sorting TaskDiffs 538 type TaskDiffs []*TaskDiff 539 540 func (t TaskDiffs) Len() int { return len(t) } 541 func (t TaskDiffs) Swap(i, j int) { t[i], t[j] = t[j], t[i] } 542 func (t TaskDiffs) Less(i, j int) bool { return t[i].Name < t[j].Name } 543 544 // serviceDiff returns the diff of two service objects. If contextual diff is 545 // enabled, all fields will be returned, even if no diff occurred. 546 func serviceDiff(old, new *Service, contextual bool) *ObjectDiff { 547 diff := &ObjectDiff{Type: DiffTypeNone, Name: "Service"} 548 var oldPrimitiveFlat, newPrimitiveFlat map[string]string 549 550 if reflect.DeepEqual(old, new) { 551 return nil 552 } else if old == nil { 553 old = &Service{} 554 diff.Type = DiffTypeAdded 555 newPrimitiveFlat = flatmap.Flatten(new, nil, true) 556 } else if new == nil { 557 new = &Service{} 558 diff.Type = DiffTypeDeleted 559 oldPrimitiveFlat = flatmap.Flatten(old, nil, true) 560 } else { 561 diff.Type = DiffTypeEdited 562 oldPrimitiveFlat = flatmap.Flatten(old, nil, true) 563 newPrimitiveFlat = flatmap.Flatten(new, nil, true) 564 } 565 566 // Diff the primitive fields. 567 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) 568 569 if setDiff := stringSetDiff(old.CanaryTags, new.CanaryTags, "CanaryTags", contextual); setDiff != nil { 570 diff.Objects = append(diff.Objects, setDiff) 571 } 572 573 // Tag diffs 574 if setDiff := stringSetDiff(old.Tags, new.Tags, "Tags", contextual); setDiff != nil { 575 diff.Objects = append(diff.Objects, setDiff) 576 } 577 578 // Checks diffs 579 if cDiffs := serviceCheckDiffs(old.Checks, new.Checks, contextual); cDiffs != nil { 580 diff.Objects = append(diff.Objects, cDiffs...) 581 } 582 583 return diff 584 } 585 586 // serviceDiffs diffs a set of services. If contextual diff is enabled, unchanged 587 // fields within objects nested in the tasks will be returned. 588 func serviceDiffs(old, new []*Service, contextual bool) []*ObjectDiff { 589 oldMap := make(map[string]*Service, len(old)) 590 newMap := make(map[string]*Service, len(new)) 591 for _, o := range old { 592 oldMap[o.Name] = o 593 } 594 for _, n := range new { 595 newMap[n.Name] = n 596 } 597 598 var diffs []*ObjectDiff 599 for name, oldService := range oldMap { 600 // Diff the same, deleted and edited 601 if diff := serviceDiff(oldService, newMap[name], contextual); diff != nil { 602 diffs = append(diffs, diff) 603 } 604 } 605 606 for name, newService := range newMap { 607 // Diff the added 608 if old, ok := oldMap[name]; !ok { 609 if diff := serviceDiff(old, newService, contextual); diff != nil { 610 diffs = append(diffs, diff) 611 } 612 } 613 } 614 615 sort.Sort(ObjectDiffs(diffs)) 616 return diffs 617 } 618 619 // serviceCheckDiff returns the diff of two service check objects. If contextual 620 // diff is enabled, all fields will be returned, even if no diff occurred. 621 func serviceCheckDiff(old, new *ServiceCheck, contextual bool) *ObjectDiff { 622 diff := &ObjectDiff{Type: DiffTypeNone, Name: "Check"} 623 var oldPrimitiveFlat, newPrimitiveFlat map[string]string 624 625 if reflect.DeepEqual(old, new) { 626 return nil 627 } else if old == nil { 628 old = &ServiceCheck{} 629 diff.Type = DiffTypeAdded 630 newPrimitiveFlat = flatmap.Flatten(new, nil, true) 631 } else if new == nil { 632 new = &ServiceCheck{} 633 diff.Type = DiffTypeDeleted 634 oldPrimitiveFlat = flatmap.Flatten(old, nil, true) 635 } else { 636 diff.Type = DiffTypeEdited 637 oldPrimitiveFlat = flatmap.Flatten(old, nil, true) 638 newPrimitiveFlat = flatmap.Flatten(new, nil, true) 639 } 640 641 // Diff the primitive fields. 642 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) 643 644 // Diff Header 645 if headerDiff := checkHeaderDiff(old.Header, new.Header, contextual); headerDiff != nil { 646 diff.Objects = append(diff.Objects, headerDiff) 647 } 648 649 // Diff check_restart 650 if crDiff := checkRestartDiff(old.CheckRestart, new.CheckRestart, contextual); crDiff != nil { 651 diff.Objects = append(diff.Objects, crDiff) 652 } 653 654 return diff 655 } 656 657 // checkHeaderDiff returns the diff of two service check header objects. If 658 // contextual diff is enabled, all fields will be returned, even if no diff 659 // occurred. 660 func checkHeaderDiff(old, new map[string][]string, contextual bool) *ObjectDiff { 661 diff := &ObjectDiff{Type: DiffTypeNone, Name: "Header"} 662 var oldFlat, newFlat map[string]string 663 664 if reflect.DeepEqual(old, new) { 665 return nil 666 } else if len(old) == 0 { 667 diff.Type = DiffTypeAdded 668 newFlat = flatmap.Flatten(new, nil, false) 669 } else if len(new) == 0 { 670 diff.Type = DiffTypeDeleted 671 oldFlat = flatmap.Flatten(old, nil, false) 672 } else { 673 diff.Type = DiffTypeEdited 674 oldFlat = flatmap.Flatten(old, nil, false) 675 newFlat = flatmap.Flatten(new, nil, false) 676 } 677 678 diff.Fields = fieldDiffs(oldFlat, newFlat, contextual) 679 return diff 680 } 681 682 // checkRestartDiff returns the diff of two service check check_restart 683 // objects. If contextual diff is enabled, all fields will be returned, even if 684 // no diff occurred. 685 func checkRestartDiff(old, new *CheckRestart, contextual bool) *ObjectDiff { 686 diff := &ObjectDiff{Type: DiffTypeNone, Name: "CheckRestart"} 687 var oldFlat, newFlat map[string]string 688 689 if reflect.DeepEqual(old, new) { 690 return nil 691 } else if old == nil { 692 diff.Type = DiffTypeAdded 693 newFlat = flatmap.Flatten(new, nil, true) 694 diff.Type = DiffTypeAdded 695 } else if new == nil { 696 diff.Type = DiffTypeDeleted 697 oldFlat = flatmap.Flatten(old, nil, true) 698 } else { 699 diff.Type = DiffTypeEdited 700 oldFlat = flatmap.Flatten(old, nil, true) 701 newFlat = flatmap.Flatten(new, nil, true) 702 } 703 704 diff.Fields = fieldDiffs(oldFlat, newFlat, contextual) 705 return diff 706 } 707 708 // serviceCheckDiffs diffs a set of service checks. If contextual diff is 709 // enabled, unchanged fields within objects nested in the tasks will be 710 // returned. 711 func serviceCheckDiffs(old, new []*ServiceCheck, contextual bool) []*ObjectDiff { 712 oldMap := make(map[string]*ServiceCheck, len(old)) 713 newMap := make(map[string]*ServiceCheck, len(new)) 714 for _, o := range old { 715 oldMap[o.Name] = o 716 } 717 for _, n := range new { 718 newMap[n.Name] = n 719 } 720 721 var diffs []*ObjectDiff 722 for name, oldCheck := range oldMap { 723 // Diff the same, deleted and edited 724 if diff := serviceCheckDiff(oldCheck, newMap[name], contextual); diff != nil { 725 diffs = append(diffs, diff) 726 } 727 } 728 729 for name, newCheck := range newMap { 730 // Diff the added 731 if old, ok := oldMap[name]; !ok { 732 if diff := serviceCheckDiff(old, newCheck, contextual); diff != nil { 733 diffs = append(diffs, diff) 734 } 735 } 736 } 737 738 sort.Sort(ObjectDiffs(diffs)) 739 return diffs 740 } 741 742 // vaultDiff returns the diff of two vault objects. If contextual diff is 743 // enabled, all fields will be returned, even if no diff occurred. 744 func vaultDiff(old, new *Vault, contextual bool) *ObjectDiff { 745 diff := &ObjectDiff{Type: DiffTypeNone, Name: "Vault"} 746 var oldPrimitiveFlat, newPrimitiveFlat map[string]string 747 748 if reflect.DeepEqual(old, new) { 749 return nil 750 } else if old == nil { 751 old = &Vault{} 752 diff.Type = DiffTypeAdded 753 newPrimitiveFlat = flatmap.Flatten(new, nil, true) 754 } else if new == nil { 755 new = &Vault{} 756 diff.Type = DiffTypeDeleted 757 oldPrimitiveFlat = flatmap.Flatten(old, nil, true) 758 } else { 759 diff.Type = DiffTypeEdited 760 oldPrimitiveFlat = flatmap.Flatten(old, nil, true) 761 newPrimitiveFlat = flatmap.Flatten(new, nil, true) 762 } 763 764 // Diff the primitive fields. 765 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) 766 767 // Policies diffs 768 if setDiff := stringSetDiff(old.Policies, new.Policies, "Policies", contextual); setDiff != nil { 769 diff.Objects = append(diff.Objects, setDiff) 770 } 771 772 return diff 773 } 774 775 // parameterizedJobDiff returns the diff of two parameterized job objects. If 776 // contextual diff is enabled, all fields will be returned, even if no diff 777 // occurred. 778 func parameterizedJobDiff(old, new *ParameterizedJobConfig, contextual bool) *ObjectDiff { 779 diff := &ObjectDiff{Type: DiffTypeNone, Name: "ParameterizedJob"} 780 var oldPrimitiveFlat, newPrimitiveFlat map[string]string 781 782 if reflect.DeepEqual(old, new) { 783 return nil 784 } else if old == nil { 785 old = &ParameterizedJobConfig{} 786 diff.Type = DiffTypeAdded 787 newPrimitiveFlat = flatmap.Flatten(new, nil, true) 788 } else if new == nil { 789 new = &ParameterizedJobConfig{} 790 diff.Type = DiffTypeDeleted 791 oldPrimitiveFlat = flatmap.Flatten(old, nil, true) 792 } else { 793 diff.Type = DiffTypeEdited 794 oldPrimitiveFlat = flatmap.Flatten(old, nil, true) 795 newPrimitiveFlat = flatmap.Flatten(new, nil, true) 796 } 797 798 // Diff the primitive fields. 799 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) 800 801 // Meta diffs 802 if optionalDiff := stringSetDiff(old.MetaOptional, new.MetaOptional, "MetaOptional", contextual); optionalDiff != nil { 803 diff.Objects = append(diff.Objects, optionalDiff) 804 } 805 806 if requiredDiff := stringSetDiff(old.MetaRequired, new.MetaRequired, "MetaRequired", contextual); requiredDiff != nil { 807 diff.Objects = append(diff.Objects, requiredDiff) 808 } 809 810 return diff 811 } 812 813 // Diff returns a diff of two resource objects. If contextual diff is enabled, 814 // non-changed fields will still be returned. 815 func (r *Resources) Diff(other *Resources, contextual bool) *ObjectDiff { 816 diff := &ObjectDiff{Type: DiffTypeNone, Name: "Resources"} 817 var oldPrimitiveFlat, newPrimitiveFlat map[string]string 818 819 if reflect.DeepEqual(r, other) { 820 return nil 821 } else if r == nil { 822 r = &Resources{} 823 diff.Type = DiffTypeAdded 824 newPrimitiveFlat = flatmap.Flatten(other, nil, true) 825 } else if other == nil { 826 other = &Resources{} 827 diff.Type = DiffTypeDeleted 828 oldPrimitiveFlat = flatmap.Flatten(r, nil, true) 829 } else { 830 diff.Type = DiffTypeEdited 831 oldPrimitiveFlat = flatmap.Flatten(r, nil, true) 832 newPrimitiveFlat = flatmap.Flatten(other, nil, true) 833 } 834 835 // Diff the primitive fields. 836 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) 837 838 // Network Resources diff 839 if nDiffs := networkResourceDiffs(r.Networks, other.Networks, contextual); nDiffs != nil { 840 diff.Objects = append(diff.Objects, nDiffs...) 841 } 842 843 // Requested Devices diff 844 if nDiffs := requestedDevicesDiffs(r.Devices, other.Devices, contextual); nDiffs != nil { 845 diff.Objects = append(diff.Objects, nDiffs...) 846 } 847 848 return diff 849 } 850 851 // Diff returns a diff of two network resources. If contextual diff is enabled, 852 // non-changed fields will still be returned. 853 func (r *NetworkResource) Diff(other *NetworkResource, contextual bool) *ObjectDiff { 854 diff := &ObjectDiff{Type: DiffTypeNone, Name: "Network"} 855 var oldPrimitiveFlat, newPrimitiveFlat map[string]string 856 filter := []string{"Device", "CIDR", "IP"} 857 858 if reflect.DeepEqual(r, other) { 859 return nil 860 } else if r == nil { 861 r = &NetworkResource{} 862 diff.Type = DiffTypeAdded 863 newPrimitiveFlat = flatmap.Flatten(other, filter, true) 864 } else if other == nil { 865 other = &NetworkResource{} 866 diff.Type = DiffTypeDeleted 867 oldPrimitiveFlat = flatmap.Flatten(r, filter, true) 868 } else { 869 diff.Type = DiffTypeEdited 870 oldPrimitiveFlat = flatmap.Flatten(r, filter, true) 871 newPrimitiveFlat = flatmap.Flatten(other, filter, true) 872 } 873 874 // Diff the primitive fields. 875 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) 876 877 // Port diffs 878 resPorts := portDiffs(r.ReservedPorts, other.ReservedPorts, false, contextual) 879 dynPorts := portDiffs(r.DynamicPorts, other.DynamicPorts, true, contextual) 880 if resPorts != nil { 881 diff.Objects = append(diff.Objects, resPorts...) 882 } 883 if dynPorts != nil { 884 diff.Objects = append(diff.Objects, dynPorts...) 885 } 886 887 return diff 888 } 889 890 // networkResourceDiffs diffs a set of NetworkResources. If contextual diff is enabled, 891 // non-changed fields will still be returned. 892 func networkResourceDiffs(old, new []*NetworkResource, contextual bool) []*ObjectDiff { 893 makeSet := func(objects []*NetworkResource) map[string]*NetworkResource { 894 objMap := make(map[string]*NetworkResource, len(objects)) 895 for _, obj := range objects { 896 hash, err := hashstructure.Hash(obj, nil) 897 if err != nil { 898 panic(err) 899 } 900 objMap[fmt.Sprintf("%d", hash)] = obj 901 } 902 903 return objMap 904 } 905 906 oldSet := makeSet(old) 907 newSet := makeSet(new) 908 909 var diffs []*ObjectDiff 910 for k, oldV := range oldSet { 911 if newV, ok := newSet[k]; !ok { 912 if diff := oldV.Diff(newV, contextual); diff != nil { 913 diffs = append(diffs, diff) 914 } 915 } 916 } 917 for k, newV := range newSet { 918 if oldV, ok := oldSet[k]; !ok { 919 if diff := oldV.Diff(newV, contextual); diff != nil { 920 diffs = append(diffs, diff) 921 } 922 } 923 } 924 925 sort.Sort(ObjectDiffs(diffs)) 926 return diffs 927 928 } 929 930 // portDiffs returns the diff of two sets of ports. The dynamic flag marks the 931 // set of ports as being Dynamic ports versus Static ports. If contextual diff is enabled, 932 // non-changed fields will still be returned. 933 func portDiffs(old, new []Port, dynamic bool, contextual bool) []*ObjectDiff { 934 makeSet := func(ports []Port) map[string]Port { 935 portMap := make(map[string]Port, len(ports)) 936 for _, port := range ports { 937 portMap[port.Label] = port 938 } 939 940 return portMap 941 } 942 943 oldPorts := makeSet(old) 944 newPorts := makeSet(new) 945 946 var filter []string 947 name := "Static Port" 948 if dynamic { 949 filter = []string{"Value"} 950 name = "Dynamic Port" 951 } 952 953 var diffs []*ObjectDiff 954 for portLabel, oldPort := range oldPorts { 955 // Diff the same, deleted and edited 956 if newPort, ok := newPorts[portLabel]; ok { 957 diff := primitiveObjectDiff(oldPort, newPort, filter, name, contextual) 958 if diff != nil { 959 diffs = append(diffs, diff) 960 } 961 } else { 962 diff := primitiveObjectDiff(oldPort, nil, filter, name, contextual) 963 if diff != nil { 964 diffs = append(diffs, diff) 965 } 966 } 967 } 968 for label, newPort := range newPorts { 969 // Diff the added 970 if _, ok := oldPorts[label]; !ok { 971 diff := primitiveObjectDiff(nil, newPort, filter, name, contextual) 972 if diff != nil { 973 diffs = append(diffs, diff) 974 } 975 } 976 } 977 978 sort.Sort(ObjectDiffs(diffs)) 979 return diffs 980 981 } 982 983 // Diff returns a diff of two requested devices. If contextual diff is enabled, 984 // non-changed fields will still be returned. 985 func (r *RequestedDevice) Diff(other *RequestedDevice, contextual bool) *ObjectDiff { 986 diff := &ObjectDiff{Type: DiffTypeNone, Name: "Device"} 987 var oldPrimitiveFlat, newPrimitiveFlat map[string]string 988 989 if reflect.DeepEqual(r, other) { 990 return nil 991 } else if r == nil { 992 diff.Type = DiffTypeAdded 993 newPrimitiveFlat = flatmap.Flatten(other, nil, true) 994 } else if other == nil { 995 diff.Type = DiffTypeDeleted 996 oldPrimitiveFlat = flatmap.Flatten(r, nil, true) 997 } else { 998 diff.Type = DiffTypeEdited 999 oldPrimitiveFlat = flatmap.Flatten(r, nil, true) 1000 newPrimitiveFlat = flatmap.Flatten(other, nil, true) 1001 } 1002 1003 // Diff the primitive fields. 1004 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) 1005 1006 return diff 1007 } 1008 1009 // requestedDevicesDiffs diffs a set of RequestedDevices. If contextual diff is enabled, 1010 // non-changed fields will still be returned. 1011 func requestedDevicesDiffs(old, new []*RequestedDevice, contextual bool) []*ObjectDiff { 1012 makeSet := func(devices []*RequestedDevice) map[string]*RequestedDevice { 1013 deviceMap := make(map[string]*RequestedDevice, len(devices)) 1014 for _, d := range devices { 1015 deviceMap[d.Name] = d 1016 } 1017 1018 return deviceMap 1019 } 1020 1021 oldSet := makeSet(old) 1022 newSet := makeSet(new) 1023 1024 var diffs []*ObjectDiff 1025 for k, oldV := range oldSet { 1026 newV := newSet[k] 1027 if diff := oldV.Diff(newV, contextual); diff != nil { 1028 diffs = append(diffs, diff) 1029 } 1030 } 1031 for k, newV := range newSet { 1032 if oldV, ok := oldSet[k]; !ok { 1033 if diff := oldV.Diff(newV, contextual); diff != nil { 1034 diffs = append(diffs, diff) 1035 } 1036 } 1037 } 1038 1039 sort.Sort(ObjectDiffs(diffs)) 1040 return diffs 1041 1042 } 1043 1044 // configDiff returns the diff of two Task Config objects. If contextual diff is 1045 // enabled, all fields will be returned, even if no diff occurred. 1046 func configDiff(old, new map[string]interface{}, contextual bool) *ObjectDiff { 1047 diff := &ObjectDiff{Type: DiffTypeNone, Name: "Config"} 1048 if reflect.DeepEqual(old, new) { 1049 return nil 1050 } else if len(old) == 0 { 1051 diff.Type = DiffTypeAdded 1052 } else if len(new) == 0 { 1053 diff.Type = DiffTypeDeleted 1054 } else { 1055 diff.Type = DiffTypeEdited 1056 } 1057 1058 // Diff the primitive fields. 1059 oldPrimitiveFlat := flatmap.Flatten(old, nil, false) 1060 newPrimitiveFlat := flatmap.Flatten(new, nil, false) 1061 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) 1062 return diff 1063 } 1064 1065 // ObjectDiff contains the diff of two generic objects. 1066 type ObjectDiff struct { 1067 Type DiffType 1068 Name string 1069 Fields []*FieldDiff 1070 Objects []*ObjectDiff 1071 } 1072 1073 func (o *ObjectDiff) GoString() string { 1074 out := fmt.Sprintf("\n%q (%s) {\n", o.Name, o.Type) 1075 for _, f := range o.Fields { 1076 out += fmt.Sprintf("%#v\n", f) 1077 } 1078 for _, o := range o.Objects { 1079 out += fmt.Sprintf("%#v\n", o) 1080 } 1081 out += "}" 1082 return out 1083 } 1084 1085 func (o *ObjectDiff) Less(other *ObjectDiff) bool { 1086 if reflect.DeepEqual(o, other) { 1087 return false 1088 } else if other == nil { 1089 return false 1090 } else if o == nil { 1091 return true 1092 } 1093 1094 if o.Name != other.Name { 1095 return o.Name < other.Name 1096 } 1097 1098 if o.Type != other.Type { 1099 return o.Type.Less(other.Type) 1100 } 1101 1102 if lO, lOther := len(o.Fields), len(other.Fields); lO != lOther { 1103 return lO < lOther 1104 } 1105 1106 if lO, lOther := len(o.Objects), len(other.Objects); lO != lOther { 1107 return lO < lOther 1108 } 1109 1110 // Check each field 1111 sort.Sort(FieldDiffs(o.Fields)) 1112 sort.Sort(FieldDiffs(other.Fields)) 1113 1114 for i, oV := range o.Fields { 1115 if oV.Less(other.Fields[i]) { 1116 return true 1117 } 1118 } 1119 1120 // Check each object 1121 sort.Sort(ObjectDiffs(o.Objects)) 1122 sort.Sort(ObjectDiffs(other.Objects)) 1123 for i, oV := range o.Objects { 1124 if oV.Less(other.Objects[i]) { 1125 return true 1126 } 1127 } 1128 1129 return false 1130 } 1131 1132 // For sorting ObjectDiffs 1133 type ObjectDiffs []*ObjectDiff 1134 1135 func (o ObjectDiffs) Len() int { return len(o) } 1136 func (o ObjectDiffs) Swap(i, j int) { o[i], o[j] = o[j], o[i] } 1137 func (o ObjectDiffs) Less(i, j int) bool { return o[i].Less(o[j]) } 1138 1139 type FieldDiff struct { 1140 Type DiffType 1141 Name string 1142 Old, New string 1143 Annotations []string 1144 } 1145 1146 // fieldDiff returns a FieldDiff if old and new are different otherwise, it 1147 // returns nil. If contextual diff is enabled, even non-changed fields will be 1148 // returned. 1149 func fieldDiff(old, new, name string, contextual bool) *FieldDiff { 1150 diff := &FieldDiff{Name: name, Type: DiffTypeNone} 1151 if old == new { 1152 if !contextual { 1153 return nil 1154 } 1155 diff.Old, diff.New = old, new 1156 return diff 1157 } 1158 1159 if old == "" { 1160 diff.Type = DiffTypeAdded 1161 diff.New = new 1162 } else if new == "" { 1163 diff.Type = DiffTypeDeleted 1164 diff.Old = old 1165 } else { 1166 diff.Type = DiffTypeEdited 1167 diff.Old = old 1168 diff.New = new 1169 } 1170 return diff 1171 } 1172 1173 func (f *FieldDiff) GoString() string { 1174 out := fmt.Sprintf("%q (%s): %q => %q", f.Name, f.Type, f.Old, f.New) 1175 if len(f.Annotations) != 0 { 1176 out += fmt.Sprintf(" (%s)", strings.Join(f.Annotations, ", ")) 1177 } 1178 1179 return out 1180 } 1181 1182 func (f *FieldDiff) Less(other *FieldDiff) bool { 1183 if reflect.DeepEqual(f, other) { 1184 return false 1185 } else if other == nil { 1186 return false 1187 } else if f == nil { 1188 return true 1189 } 1190 1191 if f.Name != other.Name { 1192 return f.Name < other.Name 1193 } else if f.Old != other.Old { 1194 return f.Old < other.Old 1195 } 1196 1197 return f.New < other.New 1198 } 1199 1200 // For sorting FieldDiffs 1201 type FieldDiffs []*FieldDiff 1202 1203 func (f FieldDiffs) Len() int { return len(f) } 1204 func (f FieldDiffs) Swap(i, j int) { f[i], f[j] = f[j], f[i] } 1205 func (f FieldDiffs) Less(i, j int) bool { return f[i].Less(f[j]) } 1206 1207 // fieldDiffs takes a map of field names to their values and returns a set of 1208 // field diffs. If contextual diff is enabled, even non-changed fields will be 1209 // returned. 1210 func fieldDiffs(old, new map[string]string, contextual bool) []*FieldDiff { 1211 var diffs []*FieldDiff 1212 visited := make(map[string]struct{}) 1213 for k, oldV := range old { 1214 visited[k] = struct{}{} 1215 newV := new[k] 1216 if diff := fieldDiff(oldV, newV, k, contextual); diff != nil { 1217 diffs = append(diffs, diff) 1218 } 1219 } 1220 1221 for k, newV := range new { 1222 if _, ok := visited[k]; !ok { 1223 if diff := fieldDiff("", newV, k, contextual); diff != nil { 1224 diffs = append(diffs, diff) 1225 } 1226 } 1227 } 1228 1229 sort.Sort(FieldDiffs(diffs)) 1230 return diffs 1231 } 1232 1233 // stringSetDiff diffs two sets of strings with the given name. 1234 func stringSetDiff(old, new []string, name string, contextual bool) *ObjectDiff { 1235 oldMap := make(map[string]struct{}, len(old)) 1236 newMap := make(map[string]struct{}, len(new)) 1237 for _, o := range old { 1238 oldMap[o] = struct{}{} 1239 } 1240 for _, n := range new { 1241 newMap[n] = struct{}{} 1242 } 1243 if reflect.DeepEqual(oldMap, newMap) && !contextual { 1244 return nil 1245 } 1246 1247 diff := &ObjectDiff{Name: name} 1248 var added, removed bool 1249 for k := range oldMap { 1250 if _, ok := newMap[k]; !ok { 1251 diff.Fields = append(diff.Fields, fieldDiff(k, "", name, contextual)) 1252 removed = true 1253 } else if contextual { 1254 diff.Fields = append(diff.Fields, fieldDiff(k, k, name, contextual)) 1255 } 1256 } 1257 1258 for k := range newMap { 1259 if _, ok := oldMap[k]; !ok { 1260 diff.Fields = append(diff.Fields, fieldDiff("", k, name, contextual)) 1261 added = true 1262 } 1263 } 1264 1265 sort.Sort(FieldDiffs(diff.Fields)) 1266 1267 // Determine the type 1268 if added && removed { 1269 diff.Type = DiffTypeEdited 1270 } else if added { 1271 diff.Type = DiffTypeAdded 1272 } else if removed { 1273 diff.Type = DiffTypeDeleted 1274 } else { 1275 // Diff of an empty set 1276 if len(diff.Fields) == 0 { 1277 return nil 1278 } 1279 1280 diff.Type = DiffTypeNone 1281 } 1282 1283 return diff 1284 } 1285 1286 // primitiveObjectDiff returns a diff of the passed objects' primitive fields. 1287 // The filter field can be used to exclude fields from the diff. The name is the 1288 // name of the objects. If contextual is set, non-changed fields will also be 1289 // stored in the object diff. 1290 func primitiveObjectDiff(old, new interface{}, filter []string, name string, contextual bool) *ObjectDiff { 1291 oldPrimitiveFlat := flatmap.Flatten(old, filter, true) 1292 newPrimitiveFlat := flatmap.Flatten(new, filter, true) 1293 delete(oldPrimitiveFlat, "") 1294 delete(newPrimitiveFlat, "") 1295 1296 diff := &ObjectDiff{Name: name} 1297 diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) 1298 1299 var added, deleted, edited bool 1300 for _, f := range diff.Fields { 1301 switch f.Type { 1302 case DiffTypeEdited: 1303 edited = true 1304 break 1305 case DiffTypeDeleted: 1306 deleted = true 1307 case DiffTypeAdded: 1308 added = true 1309 } 1310 } 1311 1312 if edited || added && deleted { 1313 diff.Type = DiffTypeEdited 1314 } else if added { 1315 diff.Type = DiffTypeAdded 1316 } else if deleted { 1317 diff.Type = DiffTypeDeleted 1318 } else { 1319 return nil 1320 } 1321 1322 return diff 1323 } 1324 1325 // primitiveObjectSetDiff does a set difference of the old and new sets. The 1326 // filter parameter can be used to filter a set of primitive fields in the 1327 // passed structs. The name corresponds to the name of the passed objects. If 1328 // contextual diff is enabled, objects' primitive fields will be returned even if 1329 // no diff exists. 1330 func primitiveObjectSetDiff(old, new []interface{}, filter []string, name string, contextual bool) []*ObjectDiff { 1331 makeSet := func(objects []interface{}) map[string]interface{} { 1332 objMap := make(map[string]interface{}, len(objects)) 1333 for _, obj := range objects { 1334 hash, err := hashstructure.Hash(obj, nil) 1335 if err != nil { 1336 panic(err) 1337 } 1338 objMap[fmt.Sprintf("%d", hash)] = obj 1339 } 1340 1341 return objMap 1342 } 1343 1344 oldSet := makeSet(old) 1345 newSet := makeSet(new) 1346 1347 var diffs []*ObjectDiff 1348 for k, v := range oldSet { 1349 // Deleted 1350 if _, ok := newSet[k]; !ok { 1351 diffs = append(diffs, primitiveObjectDiff(v, nil, filter, name, contextual)) 1352 } 1353 } 1354 for k, v := range newSet { 1355 // Added 1356 if _, ok := oldSet[k]; !ok { 1357 diffs = append(diffs, primitiveObjectDiff(nil, v, filter, name, contextual)) 1358 } 1359 } 1360 1361 sort.Sort(ObjectDiffs(diffs)) 1362 return diffs 1363 } 1364 1365 // interfaceSlice is a helper method that takes a slice of typed elements and 1366 // returns a slice of interface. This method will panic if given a non-slice 1367 // input. 1368 func interfaceSlice(slice interface{}) []interface{} { 1369 s := reflect.ValueOf(slice) 1370 if s.Kind() != reflect.Slice { 1371 panic("InterfaceSlice() given a non-slice type") 1372 } 1373 1374 ret := make([]interface{}, s.Len()) 1375 1376 for i := 0; i < s.Len(); i++ { 1377 ret[i] = s.Index(i).Interface() 1378 } 1379 1380 return ret 1381 }