@@ -962,6 +962,170 @@ func ContainerCreate(opt *option.Option) {
962
962
Expect (responseReadIopsDevices [0 ].String ()).Should (Equal (readIopsDevices [0 ].String ()))
963
963
Expect (responseWriteIopsDevices [0 ].String ()).Should (Equal (writeIopsDevices [0 ].String ()))
964
964
})
965
+
966
+ It ("should create container with volumes from another container" , func () {
967
+ tID := testContainerName
968
+
969
+ // Create temporary directories
970
+ rwDir , err := os .MkdirTemp ("" , "rw" )
971
+ Expect (err ).Should (BeNil ())
972
+ roDir , err := os .MkdirTemp ("" , "ro" )
973
+ Expect (err ).Should (BeNil ())
974
+ defer os .RemoveAll (rwDir )
975
+ defer os .RemoveAll (roDir )
976
+
977
+ // Create named volumes
978
+ rwVolName := tID + "-rw"
979
+ roVolName := tID + "-ro"
980
+ command .Run (opt , "volume" , "create" , rwVolName )
981
+ command .Run (opt , "volume" , "create" , roVolName )
982
+ defer command .Run (opt , "volume" , "rm" , "-f" , rwVolName )
983
+ defer command .Run (opt , "volume" , "rm" , "-f" , roVolName )
984
+
985
+ // Create source container with multiple volume types
986
+ fromContainerName := tID + "-from"
987
+ sourceOptions := types.ContainerCreateRequest {}
988
+ sourceOptions .Image = defaultImage
989
+ sourceOptions .Cmd = []string {"top" }
990
+ sourceOptions .HostConfig .Binds = []string {
991
+ fmt .Sprintf ("%s:%s" , rwDir , "/mnt1" ),
992
+ fmt .Sprintf ("%s:%s:ro" , roDir , "/mnt2" ),
993
+ fmt .Sprintf ("%s:%s" , rwVolName , "/mnt3" ),
994
+ fmt .Sprintf ("%s:%s:ro" , roVolName , "/mnt4" ),
995
+ }
996
+
997
+ // Create and start source container
998
+ statusCode , _ := createContainer (uClient , url , fromContainerName , sourceOptions )
999
+ Expect (statusCode ).Should (Equal (http .StatusCreated ))
1000
+ command .Run (opt , "start" , fromContainerName )
1001
+ defer command .Run (opt , "rm" , "-f" , fromContainerName )
1002
+
1003
+ // Create target container with volumes-from
1004
+ toContainerName := tID + "-to"
1005
+ targetOptions := types.ContainerCreateRequest {}
1006
+ targetOptions .Image = defaultImage
1007
+ targetOptions .Cmd = []string {"top" }
1008
+ targetOptions .HostConfig .VolumesFrom = []string {fromContainerName }
1009
+
1010
+ // Create and start target container
1011
+ statusCode , _ = createContainer (uClient , url , toContainerName , targetOptions )
1012
+ Expect (statusCode ).Should (Equal (http .StatusCreated ))
1013
+ command .Run (opt , "start" , toContainerName )
1014
+ defer command .Run (opt , "rm" , "-f" , toContainerName )
1015
+
1016
+ // Test write permissions
1017
+ command .Run (opt , "exec" , toContainerName , "sh" , "-exc" , "echo -n str1 > /mnt1/file1" )
1018
+ command .RunWithoutSuccessfulExit (opt , "exec" , toContainerName , "sh" , "-exc" , "echo -n str2 > /mnt2/file2" )
1019
+ command .Run (opt , "exec" , toContainerName , "sh" , "-exc" , "echo -n str3 > /mnt3/file3" )
1020
+ command .RunWithoutSuccessfulExit (opt , "exec" , toContainerName , "sh" , "-exc" , "echo -n str4 > /mnt4/file4" )
1021
+
1022
+ // Remove target container
1023
+ command .Run (opt , "rm" , "-f" , toContainerName )
1024
+
1025
+ // Create a new container to verify data persistence
1026
+ verifyOptions := types.ContainerCreateRequest {}
1027
+ verifyOptions .Image = defaultImage
1028
+ verifyOptions .Cmd = []string {"sh" , "-c" , "cat /mnt1/file1 /mnt3/file3" }
1029
+ verifyOptions .HostConfig .VolumesFrom = []string {fromContainerName }
1030
+
1031
+ statusCode , _ = createContainer (uClient , url , "verify-container" , verifyOptions )
1032
+ Expect (statusCode ).Should (Equal (http .StatusCreated ))
1033
+ out := command .StdoutStr (opt , "start" , "-a" , "verify-container" )
1034
+ Expect (out ).Should (Equal ("str1str3" ))
1035
+ defer command .Run (opt , "rm" , "-f" , "verify-container" )
1036
+ })
1037
+
1038
+ It ("should create a container with tmpfs mounts" , func () {
1039
+ // Define options
1040
+ options .Cmd = []string {"sleep" , "Infinity" }
1041
+ options .HostConfig .Tmpfs = map [string ]string {
1042
+ "/tmpfs1" : "rw,noexec,nosuid,size=65536k" ,
1043
+ "/tmpfs2" : "" , // no options
1044
+ }
1045
+
1046
+ // Create container
1047
+ statusCode , ctr := createContainer (uClient , url , testContainerName , options )
1048
+ Expect (statusCode ).Should (Equal (http .StatusCreated ))
1049
+ Expect (ctr .ID ).ShouldNot (BeEmpty ())
1050
+
1051
+ // Start container
1052
+ command .Run (opt , "start" , testContainerName )
1053
+
1054
+ // Verify tmpfs mounts using native inspect
1055
+ nativeResp := command .Stdout (opt , "inspect" , "--mode=native" , testContainerName )
1056
+ var nativeInspect []map [string ]interface {}
1057
+ err := json .Unmarshal (nativeResp , & nativeInspect )
1058
+ Expect (err ).Should (BeNil ())
1059
+ Expect (nativeInspect ).Should (HaveLen (1 ))
1060
+
1061
+ // Navigate to the mounts section
1062
+ spec , ok := nativeInspect [0 ]["Spec" ].(map [string ]interface {})
1063
+ Expect (ok ).Should (BeTrue ())
1064
+ mounts , ok := spec ["mounts" ].([]interface {})
1065
+ Expect (ok ).Should (BeTrue ())
1066
+
1067
+ // Verify tmpfs mounts
1068
+ foundMounts := make (map [string ]bool )
1069
+ for _ , mount := range mounts {
1070
+ m := mount .(map [string ]interface {})
1071
+ if m ["type" ] == "tmpfs" {
1072
+ foundMounts [m ["destination" ].(string )] = true
1073
+ if m ["destination" ] == "/tmpfs1" {
1074
+ options := m ["options" ].([]interface {})
1075
+ optionsStr := make ([]string , len (options ))
1076
+ for i , opt := range options {
1077
+ optionsStr [i ] = opt .(string )
1078
+ }
1079
+ Expect (optionsStr ).Should (ContainElements (
1080
+ "rw" ,
1081
+ "noexec" ,
1082
+ "nosuid" ,
1083
+ "size=65536k" ,
1084
+ ))
1085
+ }
1086
+ }
1087
+ }
1088
+ })
1089
+
1090
+ It ("should create a container with UTSMode set to host" , func () {
1091
+ // Define options
1092
+ options .Cmd = []string {"sleep" , "Infinity" }
1093
+ options .HostConfig .UTSMode = "host"
1094
+
1095
+ // Create container
1096
+ statusCode , ctr := createContainer (uClient , url , testContainerName , options )
1097
+ Expect (statusCode ).Should (Equal (http .StatusCreated ))
1098
+ Expect (ctr .ID ).ShouldNot (BeEmpty ())
1099
+
1100
+ // Start container
1101
+ command .Run (opt , "start" , testContainerName )
1102
+
1103
+ // Inspect using native format to verify UTS namespace configuration
1104
+ nativeResp := command .Stdout (opt , "inspect" , "--mode=native" , testContainerName )
1105
+ var nativeInspect []map [string ]interface {}
1106
+ err := json .Unmarshal (nativeResp , & nativeInspect )
1107
+ Expect (err ).Should (BeNil ())
1108
+ Expect (nativeInspect ).Should (HaveLen (1 ))
1109
+
1110
+ // Navigate to the namespaces section
1111
+ spec , ok := nativeInspect [0 ]["Spec" ].(map [string ]interface {})
1112
+ Expect (ok ).Should (BeTrue ())
1113
+ linux , ok := spec ["linux" ].(map [string ]interface {})
1114
+ Expect (ok ).Should (BeTrue ())
1115
+ namespaces , ok := linux ["namespaces" ].([]interface {})
1116
+ Expect (ok ).Should (BeTrue ())
1117
+
1118
+ // Verify UTS namespace is not present (indicating host namespace is used)
1119
+ foundUTSNamespace := false
1120
+ for _ , ns := range namespaces {
1121
+ namespace := ns .(map [string ]interface {})
1122
+ if namespace ["type" ] == "uts" {
1123
+ foundUTSNamespace = true
1124
+ break
1125
+ }
1126
+ }
1127
+ Expect (foundUTSNamespace ).Should (BeFalse ())
1128
+ })
965
1129
})
966
1130
}
967
1131
0 commit comments