| ... | ... |
@@ -664,11 +664,13 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
| 664 | 664 |
if _, exists := container.Volumes[volPath]; exists {
|
| 665 | 665 |
continue |
| 666 | 666 |
} |
| 667 |
+ var srcPath string |
|
| 668 |
+ srcRW := false |
|
| 667 | 669 |
// If an external bind is defined for this volume, use that as a source |
| 668 | 670 |
if bindMap, exists := binds[volPath]; exists {
|
| 669 |
- container.Volumes[volPath] = bindMap.SrcPath |
|
| 671 |
+ srcPath = bindMap.SrcPath |
|
| 670 | 672 |
if strings.ToLower(bindMap.Mode) == "rw" {
|
| 671 |
- container.VolumesRW[volPath] = true |
|
| 673 |
+ srcRW = true |
|
| 672 | 674 |
} |
| 673 | 675 |
// Otherwise create an directory in $ROOT/volumes/ and use that |
| 674 | 676 |
} else {
|
| ... | ... |
@@ -676,17 +678,36 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
| 676 | 676 |
if err != nil {
|
| 677 | 677 |
return err |
| 678 | 678 |
} |
| 679 |
- srcPath, err := c.layer() |
|
| 679 |
+ srcPath, err = c.layer() |
|
| 680 | 680 |
if err != nil {
|
| 681 | 681 |
return err |
| 682 | 682 |
} |
| 683 |
- container.Volumes[volPath] = srcPath |
|
| 684 |
- container.VolumesRW[volPath] = true // RW by default |
|
| 683 |
+ srcRW = true // RW by default |
|
| 685 | 684 |
} |
| 685 |
+ container.Volumes[volPath] = srcPath |
|
| 686 |
+ container.VolumesRW[volPath] = srcRW |
|
| 686 | 687 |
// Create the mountpoint |
| 687 |
- if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
|
|
| 688 |
+ rootVolPath := path.Join(container.RootfsPath(), volPath) |
|
| 689 |
+ if err := os.MkdirAll(rootVolPath, 0755); err != nil {
|
|
| 688 | 690 |
return nil |
| 689 | 691 |
} |
| 692 |
+ if srcRW {
|
|
| 693 |
+ volList, err := ioutil.ReadDir(rootVolPath) |
|
| 694 |
+ if err != nil {
|
|
| 695 |
+ return err |
|
| 696 |
+ } |
|
| 697 |
+ if len(volList) > 0 {
|
|
| 698 |
+ srcList, err := ioutil.ReadDir(srcPath) |
|
| 699 |
+ if err != nil {
|
|
| 700 |
+ return err |
|
| 701 |
+ } |
|
| 702 |
+ if len(srcList) == 0 {
|
|
| 703 |
+ if err := CopyWithTar(rootVolPath, srcPath); err != nil {
|
|
| 704 |
+ return err |
|
| 705 |
+ } |
|
| 706 |
+ } |
|
| 707 |
+ } |
|
| 708 |
+ } |
|
| 690 | 709 |
} |
| 691 | 710 |
|
| 692 | 711 |
if err := container.generateLXCConfig(hostConfig); err != nil {
|
| ... | ... |
@@ -1196,6 +1196,60 @@ func tempDir(t *testing.T) string {
|
| 1196 | 1196 |
return tmpDir |
| 1197 | 1197 |
} |
| 1198 | 1198 |
|
| 1199 |
+// Test for #1582 |
|
| 1200 |
+func TestCopyVolumeContent(t *testing.T) {
|
|
| 1201 |
+ r := mkRuntime(t) |
|
| 1202 |
+ defer nuke(r) |
|
| 1203 |
+ |
|
| 1204 |
+ // Put some content in a directory of a container and commit it |
|
| 1205 |
+ container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello/local && echo hello > /hello/local/world"}, t)
|
|
| 1206 |
+ defer r.Destroy(container1) |
|
| 1207 |
+ |
|
| 1208 |
+ if container1.State.Running {
|
|
| 1209 |
+ t.Errorf("Container shouldn't be running")
|
|
| 1210 |
+ } |
|
| 1211 |
+ if err := container1.Run(); err != nil {
|
|
| 1212 |
+ t.Fatal(err) |
|
| 1213 |
+ } |
|
| 1214 |
+ if container1.State.Running {
|
|
| 1215 |
+ t.Errorf("Container shouldn't be running")
|
|
| 1216 |
+ } |
|
| 1217 |
+ |
|
| 1218 |
+ rwTar, err := container1.ExportRw() |
|
| 1219 |
+ if err != nil {
|
|
| 1220 |
+ t.Error(err) |
|
| 1221 |
+ } |
|
| 1222 |
+ img, err := r.graph.Create(rwTar, container1, "unit test commited image", "", nil) |
|
| 1223 |
+ if err != nil {
|
|
| 1224 |
+ t.Error(err) |
|
| 1225 |
+ } |
|
| 1226 |
+ |
|
| 1227 |
+ // Test that the content is copied from the image to the volume |
|
| 1228 |
+ tmpDir1 := tempDir(t) |
|
| 1229 |
+ defer os.RemoveAll(tmpDir1) |
|
| 1230 |
+ stdout1, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello", tmpDir1), img.ID, "find", "/hello"}, t)
|
|
| 1231 |
+ if !(strings.Contains(stdout1, "/hello/local/world") && strings.Contains(stdout1, "/hello/local")) {
|
|
| 1232 |
+ t.Fatal("Container failed to transfer content to volume")
|
|
| 1233 |
+ } |
|
| 1234 |
+ |
|
| 1235 |
+ // Test that the content is not copied when the volume is readonly |
|
| 1236 |
+ tmpDir2 := tempDir(t) |
|
| 1237 |
+ defer os.RemoveAll(tmpDir2) |
|
| 1238 |
+ stdout2, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:ro", tmpDir2), img.ID, "find", "/hello"}, t)
|
|
| 1239 |
+ if strings.Contains(stdout2, "/hello/local/world") || strings.Contains(stdout2, "/hello/local") {
|
|
| 1240 |
+ t.Fatal("Container transfered content to readonly volume")
|
|
| 1241 |
+ } |
|
| 1242 |
+ |
|
| 1243 |
+ // Test that the content is not copied when the volume is non-empty |
|
| 1244 |
+ tmpDir3 := tempDir(t) |
|
| 1245 |
+ defer os.RemoveAll(tmpDir3) |
|
| 1246 |
+ writeFile(path.Join(tmpDir3, "touch-me"), "", t) |
|
| 1247 |
+ stdout3, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:rw", tmpDir3), img.ID, "find", "/hello"}, t)
|
|
| 1248 |
+ if strings.Contains(stdout3, "/hello/local/world") || strings.Contains(stdout3, "/hello/local") || !strings.Contains(stdout3, "/hello/touch-me") {
|
|
| 1249 |
+ t.Fatal("Container transfered content to non-empty volume")
|
|
| 1250 |
+ } |
|
| 1251 |
+} |
|
| 1252 |
+ |
|
| 1199 | 1253 |
func TestBindMounts(t *testing.T) {
|
| 1200 | 1254 |
r := mkRuntime(t) |
| 1201 | 1255 |
defer nuke(r) |
| ... | ... |
@@ -642,7 +642,7 @@ func (manager *NetworkManager) Allocate() (*NetworkInterface, error) {
|
| 642 | 642 |
if err != nil {
|
| 643 | 643 |
return nil, err |
| 644 | 644 |
} |
| 645 |
- // avoid duplicate IP |
|
| 645 |
+ // avoid duplicate IP |
|
| 646 | 646 |
ipNum := ipToInt(ip) |
| 647 | 647 |
firstIP := manager.ipAllocator.network.IP.To4().Mask(manager.ipAllocator.network.Mask) |
| 648 | 648 |
firstIPNum := ipToInt(firstIP) + 1 |