@@ -45,7 +45,9 @@ func TestClientGatewayIntegration(t *testing.T) {
45
45
testClientGatewayContainerPID1Exit ,
46
46
testClientGatewayContainerMounts ,
47
47
testClientGatewayContainerPID1Tty ,
48
+ testClientGatewayContainerCancelPID1Tty ,
48
49
testClientGatewayContainerExecTty ,
50
+ testClientGatewayContainerCancelExecTty ,
49
51
testClientSlowCacheRootfsRef ,
50
52
testClientGatewayContainerPlatformPATH ,
51
53
testClientGatewayExecError ,
@@ -923,6 +925,77 @@ func testClientGatewayContainerPID1Tty(t *testing.T, sb integration.Sandbox) {
923
925
checkAllReleasable (t , c , sb , true )
924
926
}
925
927
928
+ // testClientGatewayContainerCancelPID1Tty is testing that the tty will cleanly
929
+ // shutdown on context cancel
930
+ func testClientGatewayContainerCancelPID1Tty (t * testing.T , sb integration.Sandbox ) {
931
+ requiresLinux (t )
932
+ ctx := sb .Context ()
933
+
934
+ c , err := New (ctx , sb .Address ())
935
+ require .NoError (t , err )
936
+ defer c .Close ()
937
+
938
+ product := "buildkit_test"
939
+
940
+ inputR , inputW := io .Pipe ()
941
+ output := bytes .NewBuffer (nil )
942
+
943
+ b := func (ctx context.Context , c client.Client ) (* client.Result , error ) {
944
+ ctx , cancel := context .WithTimeout (ctx , 10 * time .Second )
945
+ defer cancel ()
946
+
947
+ st := llb .Image ("busybox:latest" )
948
+
949
+ def , err := st .Marshal (ctx )
950
+ if err != nil {
951
+ return nil , errors .Wrap (err , "failed to marshal state" )
952
+ }
953
+
954
+ r , err := c .Solve (ctx , client.SolveRequest {
955
+ Definition : def .ToPB (),
956
+ })
957
+ if err != nil {
958
+ return nil , errors .Wrap (err , "failed to solve" )
959
+ }
960
+
961
+ ctr , err := c .NewContainer (ctx , client.NewContainerRequest {
962
+ Mounts : []client.Mount {{
963
+ Dest : "/" ,
964
+ MountType : pb .MountType_BIND ,
965
+ Ref : r .Ref ,
966
+ }},
967
+ })
968
+ require .NoError (t , err )
969
+ defer ctr .Release (ctx )
970
+
971
+ prompt := newTestPrompt (ctx , t , inputW , output )
972
+ pid1 , err := ctr .Start (ctx , client.StartRequest {
973
+ Args : []string {"sh" },
974
+ Tty : true ,
975
+ Stdin : inputR ,
976
+ Stdout : & nopCloser {output },
977
+ Stderr : & nopCloser {output },
978
+ Env : []string {fmt .Sprintf ("PS1=%s" , prompt .String ())},
979
+ })
980
+ require .NoError (t , err )
981
+ prompt .SendExpect ("echo hi" , "hi" )
982
+ cancel ()
983
+
984
+ err = pid1 .Wait ()
985
+ require .ErrorIs (t , err , context .Canceled )
986
+
987
+ return & client.Result {}, err
988
+ }
989
+
990
+ _ , err = c .Build (ctx , SolveOpt {}, product , b , nil )
991
+ require .Error (t , err )
992
+
993
+ inputW .Close ()
994
+ inputR .Close ()
995
+
996
+ checkAllReleasable (t , c , sb , true )
997
+ }
998
+
926
999
type testPrompt struct {
927
1000
ctx context.Context
928
1001
t * testing.T
@@ -1071,6 +1144,87 @@ func testClientGatewayContainerExecTty(t *testing.T, sb integration.Sandbox) {
1071
1144
checkAllReleasable (t , c , sb , true )
1072
1145
}
1073
1146
1147
+ // testClientGatewayContainerExecTty is testing the tty shuts down cleanly
1148
+ // on context.Cancel
1149
+ func testClientGatewayContainerCancelExecTty (t * testing.T , sb integration.Sandbox ) {
1150
+ requiresLinux (t )
1151
+ ctx := sb .Context ()
1152
+
1153
+ c , err := New (ctx , sb .Address ())
1154
+ require .NoError (t , err )
1155
+ defer c .Close ()
1156
+
1157
+ product := "buildkit_test"
1158
+
1159
+ inputR , inputW := io .Pipe ()
1160
+ output := bytes .NewBuffer (nil )
1161
+ b := func (ctx context.Context , c client.Client ) (* client.Result , error ) {
1162
+ ctx , timeout := context .WithTimeout (ctx , 10 * time .Second )
1163
+ defer timeout ()
1164
+ st := llb .Image ("busybox:latest" )
1165
+
1166
+ def , err := st .Marshal (ctx )
1167
+ if err != nil {
1168
+ return nil , errors .Wrap (err , "failed to marshal state" )
1169
+ }
1170
+
1171
+ r , err := c .Solve (ctx , client.SolveRequest {
1172
+ Definition : def .ToPB (),
1173
+ })
1174
+ if err != nil {
1175
+ return nil , errors .Wrap (err , "failed to solve" )
1176
+ }
1177
+
1178
+ ctr , err := c .NewContainer (ctx , client.NewContainerRequest {
1179
+ Mounts : []client.Mount {{
1180
+ Dest : "/" ,
1181
+ MountType : pb .MountType_BIND ,
1182
+ Ref : r .Ref ,
1183
+ }},
1184
+ })
1185
+ require .NoError (t , err )
1186
+
1187
+ pid1 , err := ctr .Start (ctx , client.StartRequest {
1188
+ Args : []string {"sleep" , "10" },
1189
+ })
1190
+ require .NoError (t , err )
1191
+
1192
+ defer pid1 .Wait ()
1193
+ defer ctr .Release (ctx )
1194
+
1195
+ execCtx , cancel := context .WithCancel (ctx )
1196
+ defer cancel ()
1197
+
1198
+ prompt := newTestPrompt (execCtx , t , inputW , output )
1199
+ pid2 , err := ctr .Start (execCtx , client.StartRequest {
1200
+ Args : []string {"sh" },
1201
+ Tty : true ,
1202
+ Stdin : inputR ,
1203
+ Stdout : & nopCloser {output },
1204
+ Stderr : & nopCloser {output },
1205
+ Env : []string {fmt .Sprintf ("PS1=%s" , prompt .String ())},
1206
+ })
1207
+ require .NoError (t , err )
1208
+
1209
+ prompt .SendExpect ("echo hi" , "hi" )
1210
+ cancel ()
1211
+
1212
+ err = pid2 .Wait ()
1213
+ require .ErrorIs (t , err , context .Canceled )
1214
+
1215
+ return & client.Result {}, err
1216
+ }
1217
+
1218
+ _ , err = c .Build (ctx , SolveOpt {}, product , b , nil )
1219
+ require .Error (t , err )
1220
+ require .Contains (t , err .Error (), context .Canceled .Error ())
1221
+
1222
+ inputW .Close ()
1223
+ inputR .Close ()
1224
+
1225
+ checkAllReleasable (t , c , sb , true )
1226
+ }
1227
+
1074
1228
func testClientSlowCacheRootfsRef (t * testing.T , sb integration.Sandbox ) {
1075
1229
requiresLinux (t )
1076
1230
0 commit comments