hash.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package config
  2. import (
  3. "crypto/sha1"
  4. "encoding/hex"
  5. "fmt"
  6. "io"
  7. "reflect"
  8. "sort"
  9. "github.com/docker/libcompose/yaml"
  10. )
  11. // GetServiceHash computes and returns a hash that will identify a service.
  12. // This hash will be then used to detect if the service definition/configuration
  13. // have changed and needs to be recreated.
  14. func GetServiceHash(name string, config *ServiceConfig) string {
  15. hash := sha1.New()
  16. io.WriteString(hash, name)
  17. //Get values of Service through reflection
  18. val := reflect.ValueOf(config).Elem()
  19. //Create slice to sort the keys in Service Config, which allow constant hash ordering
  20. serviceKeys := []string{}
  21. //Create a data structure of map of values keyed by a string
  22. unsortedKeyValue := make(map[string]interface{})
  23. //Get all keys and values in Service Configuration
  24. for i := 0; i < val.NumField(); i++ {
  25. valueField := val.Field(i)
  26. keyField := val.Type().Field(i)
  27. serviceKeys = append(serviceKeys, keyField.Name)
  28. unsortedKeyValue[keyField.Name] = valueField.Interface()
  29. }
  30. //Sort serviceKeys alphabetically
  31. sort.Strings(serviceKeys)
  32. //Go through keys and write hash
  33. for _, serviceKey := range serviceKeys {
  34. serviceValue := unsortedKeyValue[serviceKey]
  35. io.WriteString(hash, fmt.Sprintf("\n %v: ", serviceKey))
  36. switch s := serviceValue.(type) {
  37. case yaml.SliceorMap:
  38. sliceKeys := []string{}
  39. for lkey := range s {
  40. sliceKeys = append(sliceKeys, lkey)
  41. }
  42. sort.Strings(sliceKeys)
  43. for _, sliceKey := range sliceKeys {
  44. io.WriteString(hash, fmt.Sprintf("%s=%v, ", sliceKey, s[sliceKey]))
  45. }
  46. case yaml.MaporEqualSlice:
  47. for _, sliceKey := range s {
  48. io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
  49. }
  50. case yaml.MaporColonSlice:
  51. for _, sliceKey := range s {
  52. io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
  53. }
  54. case yaml.MaporSpaceSlice:
  55. for _, sliceKey := range s {
  56. io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
  57. }
  58. case yaml.Command:
  59. for _, sliceKey := range s {
  60. io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
  61. }
  62. case yaml.Stringorslice:
  63. sort.Strings(s)
  64. for _, sliceKey := range s {
  65. io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
  66. }
  67. case []string:
  68. sliceKeys := s
  69. sort.Strings(sliceKeys)
  70. for _, sliceKey := range sliceKeys {
  71. io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
  72. }
  73. default:
  74. io.WriteString(hash, fmt.Sprintf("%v", serviceValue))
  75. }
  76. }
  77. return hex.EncodeToString(hash.Sum(nil))
  78. }