1
0
mirror of https://github.com/rancher/os.git synced 2025-06-29 16:26:50 +00:00

Merge pull request #316 from imikushin/hash

fix container hashing
This commit is contained in:
Darren Shepherd 2015-05-18 23:57:15 -07:00
commit 8024a7b51b
2 changed files with 68 additions and 43 deletions

View File

@ -6,6 +6,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"os" "os"
"reflect" "reflect"
"sort" "sort"
@ -41,18 +42,21 @@ func (c ByCreated) Len() int { return len(c) }
func (c ByCreated) Swap(i, j int) { c[i], c[j] = c[j], c[i] } func (c ByCreated) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c ByCreated) Less(i, j int) bool { return c[j].Created < c[i].Created } func (c ByCreated) Less(i, j int) bool { return c[j].Created < c[i].Created }
func getHash(containerCfg *config.ContainerConfig) (string, error) { func getHash(containerCfg *config.ContainerConfig) string {
hash := sha1.New() hash := sha1.New()
w := util.NewErrorWriter(hash)
w.Write([]byte(containerCfg.Id)) io.WriteString(hash, fmt.Sprintln(containerCfg.Id))
w.Write([]byte(containerCfg.Cmd)) io.WriteString(hash, fmt.Sprintln(containerCfg.Cmd))
io.WriteString(hash, fmt.Sprintln(containerCfg.MigrateVolumes))
io.WriteString(hash, fmt.Sprintln(containerCfg.ReloadConfig))
io.WriteString(hash, fmt.Sprintln(containerCfg.CreateOnly))
if containerCfg.Service != nil { if containerCfg.Service != nil {
//Get values of Service through reflection //Get values of Service through reflection
val := reflect.ValueOf(containerCfg.Service).Elem() val := reflect.ValueOf(containerCfg.Service).Elem()
//Create slice to sort the keys in Service Config, which allow constant hash ordering //Create slice to sort the keys in Service Config, which allow constant hash ordering
var serviceKeys []string serviceKeys := []string{}
//Create a data structure of map of values keyed by a string //Create a data structure of map of values keyed by a string
unsortedKeyValue := make(map[string]interface{}) unsortedKeyValue := make(map[string]interface{})
@ -70,14 +74,14 @@ func getHash(containerCfg *config.ContainerConfig) (string, error) {
sort.Strings(serviceKeys) sort.Strings(serviceKeys)
//Go through keys and write hash //Go through keys and write hash
for i := 0; i < len(serviceKeys); i++ { for _, serviceKey := range serviceKeys {
serviceValue := unsortedKeyValue[serviceKeys[i]] serviceValue := unsortedKeyValue[serviceKey]
sliceKeys := []string{}
io.WriteString(hash, fmt.Sprintf("\n %v: ", serviceKey))
switch s := serviceValue.(type) { switch s := serviceValue.(type) {
default: case project.SliceorMap:
w.Write([]byte(fmt.Sprintf("%v", serviceValue))) sliceKeys := []string{}
case *project.SliceorMap:
for lkey := range s.MapParts() { for lkey := range s.MapParts() {
if lkey != "io.rancher.os.hash" { if lkey != "io.rancher.os.hash" {
sliceKeys = append(sliceKeys, lkey) sliceKeys = append(sliceKeys, lkey)
@ -85,32 +89,37 @@ func getHash(containerCfg *config.ContainerConfig) (string, error) {
} }
sort.Strings(sliceKeys) sort.Strings(sliceKeys)
for j := 0; j < len(sliceKeys); j++ { for _, sliceKey := range sliceKeys {
w.Write([]byte(fmt.Sprintf("%s=%v", sliceKeys[j], s.MapParts()[sliceKeys[j]]))) io.WriteString(hash, fmt.Sprintf("%s=%v, ", sliceKey, s.MapParts()[sliceKey]))
} }
case *project.Stringorslice: case project.Maporslice:
sliceKeys = s.Slice() sliceKeys := s.Slice()
// do not sort environment keys as the order matters
for _, sliceKey := range sliceKeys {
io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
}
case project.Stringorslice:
sliceKeys := s.Slice()
sort.Strings(sliceKeys) sort.Strings(sliceKeys)
for j := 0; j < len(sliceKeys); j++ { for _, sliceKey := range sliceKeys {
w.Write([]byte(fmt.Sprintf("%s", sliceKeys[j]))) io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
} }
case []string: case []string:
sliceKeys = s sliceKeys := s
sort.Strings(sliceKeys) sort.Strings(sliceKeys)
for j := 0; j < len(sliceKeys); j++ { for _, sliceKey := range sliceKeys {
w.Write([]byte(fmt.Sprintf("%s", sliceKeys[j]))) io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
} }
default:
io.WriteString(hash, fmt.Sprintf("%v", serviceValue))
} }
} }
} }
if w.Err != nil { return hex.EncodeToString(hash.Sum(nil))
return "", w.Err
}
return hex.EncodeToString(hash.Sum([]byte{})), nil
} }
func StartAndWait(dockerHost string, containerCfg *config.ContainerConfig) error { func StartAndWait(dockerHost string, containerCfg *config.ContainerConfig) error {
@ -171,10 +180,7 @@ func (c *Container) Lookup() *Container {
return c return c
} }
hash, err := getHash(c.ContainerCfg) hash := getHash(c.ContainerCfg)
if err != nil {
return c.returnErr(err)
}
client, err := NewClient(c.dockerHost) client, err := NewClient(c.dockerHost)
if err != nil { if err != nil {
@ -486,10 +492,7 @@ func (c *Container) getCreateOpts(client *dockerClient.Client) (*dockerClient.Cr
opts.Config.Labels = make(map[string]string) opts.Config.Labels = make(map[string]string)
} }
hash, err := getHash(c.ContainerCfg) hash := getHash(c.ContainerCfg)
if err != nil {
return nil, err
}
opts.Config.Labels[config.HASH] = hash opts.Config.Labels[config.HASH] = hash
opts.Config.Labels[config.ID] = c.ContainerCfg.Id opts.Config.Labels[config.ID] = c.ContainerCfg.Id

View File

@ -1,10 +1,12 @@
package docker package docker
import ( import (
"fmt"
"strings" "strings"
"testing" "testing"
"github.com/rancherio/os/config" "github.com/rancherio/os/config"
"github.com/rancherio/rancher-compose/librcompose/project"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
dockerClient "github.com/fsouza/go-dockerclient" dockerClient "github.com/fsouza/go-dockerclient"
@ -13,30 +15,50 @@ import (
func TestHash(t *testing.T) { func TestHash(t *testing.T) {
assert := require.New(t) assert := require.New(t)
hash, err := getHash(&config.ContainerConfig{ hash := getHash(&config.ContainerConfig{
Id: "id", Id: "id",
Cmd: "1 2 3", Cmd: "1 2 3",
}) })
assert.NoError(err, "")
hash2, err := getHash(&config.ContainerConfig{ hash2 := getHash(&config.ContainerConfig{
Id: "id2", Id: "id2",
Cmd: "1 2 3", Cmd: "1 2 3",
}) })
assert.NoError(err, "")
hash3, err := getHash(&config.ContainerConfig{ hash3 := getHash(&config.ContainerConfig{
Id: "id3", Id: "id3",
Cmd: "1 2 3 4", Cmd: "1 2 3 4",
}) })
assert.NoError(err, "")
assert.Equal("510b68938cba936876588b0143093a5850d4a142", hash, "") assert.Equal("d601444333c7fb4cb955bcca36c5ed59b6fa8c3f", hash, "")
assert.NotEqual(hash, hash2, "") assert.NotEqual(hash, hash2, "")
assert.NotEqual(hash2, hash3, "") assert.NotEqual(hash2, hash3, "")
assert.NotEqual(hash, hash3, "") assert.NotEqual(hash, hash3, "")
} }
func TestHash2(t *testing.T) {
assert := require.New(t)
cfg := &config.ContainerConfig{
Id: "docker-volumes",
Cmd: "",
MigrateVolumes: false,
ReloadConfig: false,
CreateOnly: true,
Service: &project.ServiceConfig{CapAdd:nil, CapDrop:nil, CpuShares:0, Command:"", Detach:"", Dns:project.NewStringorslice(), DnsSearch:project.NewStringorslice(), DomainName:"", Entrypoint:"", EnvFile:"", Environment:project.NewMaporslice([]string{}), Hostname:"", Image:"state", Labels:project.NewSliceorMap(map[string]string{"io.rancher.os.createonly":"true", "io.rancher.os.scope":"system"}), Links:nil, LogDriver:"json-file", MemLimit:0, Name:"", Net:"none", Pid:"", Ipc:"", Ports:nil, Privileged:true, Restart:"", ReadOnly:true, StdinOpen:false, Tty:false, User:"", Volumes:[]string{"/var/lib/docker:/var/lib/docker", "/var/lib/rancher/conf:/var/lib/rancher/conf", "/var/lib/system-docker:/var/lib/system-docker"}, VolumesFrom:nil, WorkingDir:"", Expose:nil, ExternalLinks:nil},
}
for i := 0; i < 1000; i++ {
assert.Equal(getHash(cfg), getHash(cfg), fmt.Sprintf("Failed at iteration: %v", i))
}
}
func TestBool2String(t *testing.T) {
assert := require.New(t)
assert.Equal("true", fmt.Sprint(true), "")
}
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
assert := require.New(t) assert := require.New(t)
@ -57,9 +79,9 @@ func TestParse(t *testing.T) {
assert.Equal(c.Name, "c1", "Name doesn't match") assert.Equal(c.Name, "c1", "Name doesn't match")
assert.True(c.remove, "Remove doesn't match") assert.True(c.remove, "Remove doesn't match")
assert.True(c.detach, "Detach doesn't match") assert.True(c.detach, "Detach doesn't match")
assert.Equal(len(c.Config.Cmd), 2, "Args doesn't match") assert.Equal(c.Config.Cmd.Len(), 2, "Args doesn't match")
assert.Equal(c.Config.Cmd[0], "arg1", "Arg1 doesn't match") assert.Equal(c.Config.Cmd.Slice()[0], "arg1", "Arg1 doesn't match")
assert.Equal(c.Config.Cmd[1], "arg2", "Arg2 doesn't match") assert.Equal(c.Config.Cmd.Slice()[1], "arg2", "Arg2 doesn't match")
assert.True(c.HostConfig.Privileged, "Privileged doesn't match") assert.True(c.HostConfig.Privileged, "Privileged doesn't match")
} }