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  }