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