-module(testcontainer@internal@docker). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/testcontainer/internal/docker.gleam"). -export([parse_response/1, ping/0, image_exists/1, pull_image/2, create_container/1, start_container/1, stop_container/2, remove_container/1, inspect_container/1, container_logs/2, exec_container/2, create_network/1, remove_network/1, copy_file_to/3]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(false). -file("src/testcontainer/internal/docker.gleam", 50). ?DOC(false). -spec parse_response(binary()) -> {ok, {integer(), binary()}} | {error, binary()}. parse_response(Data) -> docker_transport:parse_response(Data). -file("src/testcontainer/internal/docker.gleam", 63). ?DOC(false). -spec encode_grapheme(binary()) -> binary(). encode_grapheme(G) -> case G of <<"%"/utf8>> -> <<"%25"/utf8>>; <<"\r"/utf8>> -> <<"%0D"/utf8>>; <<"\n"/utf8>> -> <<"%0A"/utf8>>; <<" "/utf8>> -> <<"%20"/utf8>>; <<"/"/utf8>> -> <<"%2F"/utf8>>; <<":"/utf8>> -> <<"%3A"/utf8>>; <<"="/utf8>> -> <<"%3D"/utf8>>; <<"?"/utf8>> -> <<"%3F"/utf8>>; <<"&"/utf8>> -> <<"%26"/utf8>>; <<"#"/utf8>> -> <<"%23"/utf8>>; <<"+"/utf8>> -> <<"%2B"/utf8>>; Other -> Other end. -file("src/testcontainer/internal/docker.gleam", 54). ?DOC(false). -spec url_encode(binary()) -> binary(). url_encode(Input) -> _pipe = Input, _pipe@1 = gleam@string:to_graphemes(_pipe), _pipe@2 = gleam@list:map(_pipe@1, fun encode_grapheme/1), erlang:list_to_binary(_pipe@2). -file("src/testcontainer/internal/docker.gleam", 80). ?DOC(false). -spec contains_crlf(binary()) -> boolean(). contains_crlf(Input) -> gleam_stdlib:contains_string(Input, <<"\r"/utf8>>) orelse gleam_stdlib:contains_string( Input, <<"\n"/utf8>> ). -file("src/testcontainer/internal/docker.gleam", 97). ?DOC(false). -spec request_with_headers( binary(), binary(), list({binary(), binary()}), binary() ) -> {ok, {integer(), binary()}} | {error, testcontainer@error:error()}. request_with_headers(Method, Path, Headers, Body) -> case docker_transport:request(Method, Path, Headers, Body) of {ok, {Status, Response}} -> {ok, {Status, Response}}; {error, Reason} -> {error, {docker_unavailable, docker_transport:socket_path(), Reason}} end. -file("src/testcontainer/internal/docker.gleam", 84). ?DOC(false). -spec request(binary(), binary(), binary()) -> {ok, {integer(), binary()}} | {error, testcontainer@error:error()}. request(Method, Path, Body) -> request_with_headers( Method, Path, [{<<"Content-Type"/utf8>>, <<"application/json"/utf8>>}], Body ). -file("src/testcontainer/internal/docker.gleam", 110). ?DOC(false). -spec check_ok(binary(), binary(), integer(), binary()) -> {ok, nil} | {error, testcontainer@error:error()}. check_ok(Method, Path, Status, Body) -> case Status of 200 -> {ok, nil}; 201 -> {ok, nil}; 204 -> {ok, nil}; _ -> {error, {docker_api_error, Method, Path, Status, Body}} end. -file("src/testcontainer/internal/docker.gleam", 124). ?DOC(false). -spec request_ok(binary(), binary(), binary()) -> {ok, nil} | {error, testcontainer@error:error()}. request_ok(Method, Path, Body) -> case request(Method, Path, Body) of {ok, {Status, Response}} -> check_ok(Method, Path, Status, Response); {error, E} -> {error, E} end. -file("src/testcontainer/internal/docker.gleam", 135). ?DOC(false). -spec request_json(binary(), binary(), gleam@json:json()) -> {ok, binary()} | {error, testcontainer@error:error()}. request_json(Method, Path, Body) -> Body_string = gleam@json:to_string(Body), case request(Method, Path, Body_string) of {ok, {200, Response}} -> {ok, Response}; {ok, {201, Response@1}} -> {ok, Response@1}; {ok, {Status, Response@2}} -> {error, {docker_api_error, Method, Path, Status, Response@2}}; {error, E} -> {error, E} end. -file("src/testcontainer/internal/docker.gleam", 150). ?DOC(false). -spec ping() -> {ok, nil} | {error, testcontainer@error:error()}. ping() -> request_ok(<<"GET"/utf8>>, <<"/_ping"/utf8>>, <<""/utf8>>). -file("src/testcontainer/internal/docker.gleam", 155). ?DOC(false). -spec image_exists(binary()) -> boolean(). image_exists(Image) -> Encoded = url_encode(Image), case request( <<"GET"/utf8>>, <<<<"/images/"/utf8, Encoded/binary>>/binary, "/json"/utf8>>, <<""/utf8>> ) of {ok, {200, _}} -> true; _ -> false end. -file("src/testcontainer/internal/docker.gleam", 231). ?DOC(false). -spec extract_after(binary(), binary()) -> gleam@option:option(binary()). extract_after(Stream, Marker) -> case gleam@string:split_once(Stream, Marker) of {ok, {_, Rest}} -> case gleam@string:split_once(Rest, <<"\""/utf8>>) of {ok, {Msg, _}} -> {some, Msg}; {error, _} -> {some, Rest} end; {error, _} -> none end. -file("src/testcontainer/internal/docker.gleam", 213). ?DOC(false). -spec scan_pull_stream_for_error(binary()) -> gleam@option:option(binary()). scan_pull_stream_for_error(Stream) -> case extract_after(Stream, <<"\"error\":\""/utf8>>) of {some, Msg} -> {some, Msg}; none -> case extract_after(Stream, <<"\"errorDetail\":"/utf8>>) of {some, Remainder} -> case extract_after(Remainder, <<"\"message\":\""/utf8>>) of {some, Msg@1} -> {some, Msg@1}; none -> {some, <<"image pull failed (errorDetail without message)"/utf8>>} end; none -> none end end. -file("src/testcontainer/internal/docker.gleam", 202). ?DOC(false). -spec encode_registry_auth(testcontainer@internal@config:registry_auth()) -> binary(). encode_registry_auth(A) -> Payload = gleam@json:object( [{<<"username"/utf8>>, gleam@json:string(erlang:element(2, A))}, {<<"password"/utf8>>, gleam@json:string(cowl:reveal(erlang:element(3, A)))}] ), gleam_stdlib:base64_encode( <<(gleam@json:to_string(Payload))/binary>>, false ). -file("src/testcontainer/internal/docker.gleam", 163). ?DOC(false). -spec pull_image( binary(), gleam@option:option(testcontainer@internal@config:registry_auth()) ) -> {ok, nil} | {error, testcontainer@error:error()}. pull_image(Image, Auth) -> Ref = testcontainer@internal@image_ref:parse(Image), Path = <<<<<<"/images/create?fromImage="/utf8, (url_encode(erlang:element(2, Ref)))/binary>>/binary, "&tag="/utf8>>/binary, (url_encode(erlang:element(3, Ref)))/binary>>, Headers = case Auth of {some, A} -> [{<<"Content-Type"/utf8>>, <<"application/json"/utf8>>}, {<<"X-Registry-Auth"/utf8>>, encode_registry_auth(A)}]; none -> [{<<"Content-Type"/utf8>>, <<"application/json"/utf8>>}] end, case request_with_headers(<<"POST"/utf8>>, Path, Headers, <<""/utf8>>) of {ok, {Status, Body}} when Status =:= 200 -> case scan_pull_stream_for_error(Body) of {some, Reason} -> {error, {image_pull_failed, Image, Reason}}; none -> {ok, nil} end; {ok, {Status@1, Body@1}} -> {error, {image_pull_failed, Image, <<<<<<"HTTP "/utf8, (erlang:integer_to_binary(Status@1))/binary>>/binary, ": "/utf8>>/binary, Body@1/binary>>}}; {error, E} -> {error, E} end. -file("src/testcontainer/internal/docker.gleam", 470). ?DOC(false). -spec validate_volumes(testcontainer@container:container_spec()) -> {ok, nil} | {error, testcontainer@error:error()}. validate_volumes(Spec) -> Bad = gleam@list:find( testcontainer@container:volumes(Spec), fun(V) -> case testcontainer@container:volume_kind(V) of {ok, {Host, Cpath, _}} -> (contains_crlf(Host) orelse contains_crlf(Cpath)) orelse gleam_stdlib:contains_string( Host, <<":"/utf8>> ); {error, P} -> contains_crlf(P) end end ), case Bad of {ok, _} -> {error, {container_create_failed, testcontainer@container:image(Spec), <<"volume path invalid (CR/LF or ':' in host path)"/utf8>>}}; {error, nil} -> {ok, nil} end. -file("src/testcontainer/internal/docker.gleam", 458). ?DOC(false). -spec validate_ports(testcontainer@container:container_spec()) -> {ok, nil} | {error, testcontainer@error:error()}. validate_ports(Spec) -> Bad = gleam@list:find( testcontainer@container:ports(Spec), fun(P) -> N = testcontainer@port:number(P), (N < 1) orelse (N > 65535) end ), case Bad of {ok, P@1} -> {error, {invalid_port, testcontainer@port:number(P@1)}}; {error, nil} -> {ok, nil} end. -file("src/testcontainer/internal/docker.gleam", 440). ?DOC(false). -spec validate_labels(testcontainer@container:container_spec(), binary()) -> {ok, nil} | {error, testcontainer@error:error()}. validate_labels(Spec, Image) -> Bad = gleam@list:find( testcontainer@container:labels(Spec), fun(Pair) -> contains_crlf(erlang:element(1, Pair)) orelse contains_crlf( erlang:element(2, Pair) ) end ), case Bad of {ok, _} -> {error, {container_create_failed, Image, <<"label key or value contains CR/LF"/utf8>>}}; {error, nil} -> {ok, nil} end. -file("src/testcontainer/internal/docker.gleam", 418). ?DOC(false). -spec validate_cmd_or_entrypoint( testcontainer@container:container_spec(), binary() ) -> {ok, nil} | {error, testcontainer@error:error()}. validate_cmd_or_entrypoint(Spec, Image) -> Cmd_bad = case testcontainer@container:command(Spec) of {some, Parts} -> gleam@list:any(Parts, fun contains_crlf/1); none -> false end, Ep_bad = case testcontainer@container:entrypoint(Spec) of {some, Parts@1} -> gleam@list:any(Parts@1, fun contains_crlf/1); none -> false end, case Cmd_bad orelse Ep_bad of true -> {error, {container_create_failed, Image, <<"command or entrypoint argument contains CR/LF"/utf8>>}}; false -> {ok, nil} end. -file("src/testcontainer/internal/docker.gleam", 400). ?DOC(false). -spec validate_envs(testcontainer@container:container_spec(), binary()) -> {ok, nil} | {error, testcontainer@error:error()}. validate_envs(Spec, Image) -> Bad = gleam@list:find( testcontainer@container:env(Spec), fun(Pair) -> contains_crlf(erlang:element(1, Pair)) orelse contains_crlf( cowl:reveal(erlang:element(2, Pair)) ) end ), case Bad of {ok, _} -> {error, {container_create_failed, Image, <<"env key or value contains CR/LF"/utf8>>}}; {error, nil} -> {ok, nil} end. -file("src/testcontainer/internal/docker.gleam", 375). ?DOC(false). -spec validate_spec(testcontainer@container:container_spec()) -> {ok, nil} | {error, testcontainer@error:error()}. validate_spec(Spec) -> Image = testcontainer@container:image(Spec), gleam@result:'try'(case contains_crlf(Image) of true -> {error, {invalid_image_ref, Image}}; false -> {ok, nil} end, fun(_) -> gleam@result:'try'(case testcontainer@container:name(Spec) of {some, N} -> case contains_crlf(N) of true -> {error, {container_create_failed, Image, <<"container name contains CR/LF"/utf8>>}}; false -> {ok, nil} end; none -> {ok, nil} end, fun(_) -> gleam@result:'try'( validate_envs(Spec, Image), fun(_) -> gleam@result:'try'( validate_cmd_or_entrypoint(Spec, Image), fun(_) -> gleam@result:'try'( validate_labels(Spec, Image), fun(_) -> gleam@result:'try'( validate_ports(Spec), fun(_) -> validate_volumes(Spec) end ) end ) end ) end ) end) end). -file("src/testcontainer/internal/docker.gleam", 242). ?DOC(false). -spec create_container(testcontainer@container:container_spec()) -> {ok, binary()} | {error, testcontainer@error:error()}. create_container(Spec) -> gleam@result:'try'( validate_spec(Spec), fun(_) -> Cmd = case testcontainer@container:command(Spec) of {some, C} -> gleam@json:array(C, fun gleam@json:string/1); none -> gleam@json:null() end, Entrypoint = case testcontainer@container:entrypoint(Spec) of {some, E} -> gleam@json:array(E, fun gleam@json:string/1); none -> gleam@json:null() end, Env_list = gleam@list:map( testcontainer@container:env(Spec), fun(Pair) -> gleam@json:string( <<<<(erlang:element(1, Pair))/binary, "="/utf8>>/binary, (cowl:reveal(erlang:element(2, Pair)))/binary>> ) end ), Port_keys = gleam@list:map( testcontainer@container:ports(Spec), fun(P) -> <<<<(erlang:integer_to_binary(testcontainer@port:number(P)))/binary, "/"/utf8>>/binary, (testcontainer@port:protocol(P))/binary>> end ), Exposed_ports = gleam@json:object( gleam@list:map( Port_keys, fun(K) -> {K, gleam@json:object([])} end ) ), Port_bindings = gleam@json:object( gleam@list:map( Port_keys, fun(K@1) -> {K@1, gleam@json:array( [gleam@json:object( [{<<"HostIp"/utf8>>, gleam@json:string(<<""/utf8>>)}, {<<"HostPort"/utf8>>, gleam@json:string(<<""/utf8>>)}] )], fun(X) -> X end )} end ) ), Binds = gleam@list:filter_map( testcontainer@container:volumes(Spec), fun(V) -> case testcontainer@container:volume_kind(V) of {ok, {Host, Cpath, Ro}} -> Mode = case Ro of true -> <<":ro"/utf8>>; false -> <<":rw"/utf8>> end, {ok, gleam@json:string( <<<<<>/binary, Cpath/binary>>/binary, Mode/binary>> )}; {error, _} -> {error, nil} end end ), Tmpfs_entries = gleam@list:filter_map( testcontainer@container:volumes(Spec), fun(V@1) -> case testcontainer@container:volume_kind(V@1) of {error, Cpath@1} -> {ok, {Cpath@1, gleam@json:string(<<""/utf8>>)}}; {ok, _} -> {error, nil} end end ), Labels_obj = gleam@json:object( gleam@list:map( testcontainer@container:labels(Spec), fun(Pair@1) -> {erlang:element(1, Pair@1), gleam@json:string(erlang:element(2, Pair@1))} end ) ), Network_mode = case testcontainer@container:network(Spec) of {some, N} -> gleam@json:string(N); none -> gleam@json:string(<<"bridge"/utf8>>) end, Networking_config = case testcontainer@container:network(Spec) of {some, N@1} -> gleam@json:object( [{<<"EndpointsConfig"/utf8>>, gleam@json:object( [{N@1, gleam@json:object([])}] )}] ); none -> gleam@json:object([]) end, Host_config = gleam@json:object( [{<<"PortBindings"/utf8>>, Port_bindings}, {<<"Binds"/utf8>>, gleam@json:array(Binds, fun(X@1) -> X@1 end)}, {<<"Tmpfs"/utf8>>, gleam@json:object(Tmpfs_entries)}, {<<"Privileged"/utf8>>, gleam@json:bool( testcontainer@container:is_privileged(Spec) )}, {<<"NetworkMode"/utf8>>, Network_mode}] ), Body = gleam@json:object( [{<<"Image"/utf8>>, gleam@json:string(testcontainer@container:image(Spec))}, {<<"Cmd"/utf8>>, Cmd}, {<<"Entrypoint"/utf8>>, Entrypoint}, {<<"Env"/utf8>>, gleam@json:array(Env_list, fun(X@2) -> X@2 end)}, {<<"ExposedPorts"/utf8>>, Exposed_ports}, {<<"Labels"/utf8>>, Labels_obj}, {<<"HostConfig"/utf8>>, Host_config}, {<<"NetworkingConfig"/utf8>>, Networking_config}] ), Path = case testcontainer@container:name(Spec) of {some, N@2} -> <<"/containers/create?name="/utf8, (url_encode(N@2))/binary>>; none -> <<"/containers/create"/utf8>> end, case request_json(<<"POST"/utf8>>, Path, Body) of {ok, Response} -> case gleam@json:parse( Response, gleam@dynamic@decode:at( [<<"Id"/utf8>>], {decoder, fun gleam@dynamic@decode:decode_string/1} ) ) of {ok, Id} -> {ok, Id}; {error, _} -> {error, {container_create_failed, testcontainer@container:image(Spec), <<"unable to parse create response"/utf8>>}} end; {error, {docker_api_error, _, _, Status, Body_str}} -> {error, {container_create_failed, testcontainer@container:image(Spec), <<<<<<"HTTP "/utf8, (erlang:integer_to_binary(Status))/binary>>/binary, ": "/utf8>>/binary, Body_str/binary>>}}; {error, E@1} -> {error, E@1} end end ). -file("src/testcontainer/internal/docker.gleam", 491). ?DOC(false). -spec start_container(binary()) -> {ok, nil} | {error, testcontainer@error:error()}. start_container(Id) -> case request( <<"POST"/utf8>>, <<<<"/containers/"/utf8, Id/binary>>/binary, "/start"/utf8>>, <<""/utf8>> ) of {ok, {Status, _}} when (Status =:= 204) orelse (Status =:= 304) -> {ok, nil}; {ok, {Status@1, Body}} -> {error, {container_start_failed, Id, <<<<<<"HTTP "/utf8, (erlang:integer_to_binary(Status@1))/binary>>/binary, ": "/utf8>>/binary, Body/binary>>}}; {error, {docker_unavailable, _, Reason}} -> {error, {container_start_failed, Id, Reason}}; {error, E} -> {error, E} end. -file("src/testcontainer/internal/docker.gleam", 505). ?DOC(false). -spec stop_container(binary(), integer()) -> {ok, nil} | {error, testcontainer@error:error()}. stop_container(Id, Timeout_sec) -> Path = <<<<<<"/containers/"/utf8, Id/binary>>/binary, "/stop?t="/utf8>>/binary, (erlang:integer_to_binary(Timeout_sec))/binary>>, case request(<<"POST"/utf8>>, Path, <<""/utf8>>) of {ok, {Status, _}} when (Status =:= 204) orelse (Status =:= 304) -> {ok, nil}; {ok, {Status@1, Body}} -> {error, {container_stop_failed, Id, <<<<<<"HTTP "/utf8, (erlang:integer_to_binary(Status@1))/binary>>/binary, ": "/utf8>>/binary, Body/binary>>}}; {error, {docker_unavailable, _, Reason}} -> {error, {container_stop_failed, Id, Reason}}; {error, E} -> {error, E} end. -file("src/testcontainer/internal/docker.gleam", 524). ?DOC(false). -spec remove_container(binary()) -> {ok, nil} | {error, testcontainer@error:error()}. remove_container(Id) -> request_ok( <<"DELETE"/utf8>>, <<<<"/containers/"/utf8, Id/binary>>/binary, "?force=true"/utf8>>, <<""/utf8>> ). -file("src/testcontainer/internal/docker.gleam", 528). ?DOC(false). -spec inspect_container(binary()) -> {ok, binary()} | {error, testcontainer@error:error()}. inspect_container(Id) -> case request( <<"GET"/utf8>>, <<<<"/containers/"/utf8, Id/binary>>/binary, "/json"/utf8>>, <<""/utf8>> ) of {ok, {200, Response}} -> {ok, Response}; {ok, {Status, Response@1}} -> {error, {docker_api_error, <<"GET"/utf8>>, <<<<"/containers/"/utf8, Id/binary>>/binary, "/json"/utf8>>, Status, Response@1}}; {error, E} -> {error, E} end. -file("src/testcontainer/internal/docker.gleam", 542). ?DOC(false). -spec container_logs(binary(), gleam@option:option(integer())) -> {ok, binary()} | {error, testcontainer@error:error()}. container_logs(Id, Tail) -> Tail_q = case Tail of {some, N} -> <<"&tail="/utf8, (erlang:integer_to_binary(N))/binary>>; none -> <<"&tail=all"/utf8>> end, case request( <<"GET"/utf8>>, <<<<<<"/containers/"/utf8, Id/binary>>/binary, "/logs?stdout=1&stderr=1"/utf8>>/binary, Tail_q/binary>>, <<""/utf8>> ) of {ok, {200, Response}} -> {ok, docker_transport:strip_log_frames(Response)}; {ok, {Status, Response@1}} -> {error, {docker_api_error, <<"GET"/utf8>>, <<<<"/containers/"/utf8, Id/binary>>/binary, "/logs"/utf8>>, Status, Response@1}}; {error, E} -> {error, E} end. -file("src/testcontainer/internal/docker.gleam", 573). ?DOC(false). -spec exec_container(binary(), list(binary())) -> {ok, testcontainer@exec:exec_result()} | {error, testcontainer@error:error()}. exec_container(Id, Cmd) -> Create_body = gleam@json:object( [{<<"Cmd"/utf8>>, gleam@json:array(Cmd, fun gleam@json:string/1)}, {<<"AttachStdout"/utf8>>, gleam@json:bool(true)}, {<<"AttachStderr"/utf8>>, gleam@json:bool(true)}] ), gleam@result:'try'( begin _pipe = request_json( <<"POST"/utf8>>, <<<<"/containers/"/utf8, Id/binary>>/binary, "/exec"/utf8>>, Create_body ), gleam@result:map_error(_pipe, fun(E) -> case E of {docker_api_error, _, _, Status, Body} -> {exec_failed, Id, Cmd, -1, <<<<<<"create exec HTTP "/utf8, (erlang:integer_to_binary(Status))/binary>>/binary, ": "/utf8>>/binary, Body/binary>>}; _ -> E end end) end, fun(Exec_resp) -> gleam@result:'try'( case gleam@json:parse( Exec_resp, gleam@dynamic@decode:at( [<<"Id"/utf8>>], {decoder, fun gleam@dynamic@decode:decode_string/1} ) ) of {ok, Eid} -> {ok, Eid}; {error, _} -> {error, {exec_failed, Id, Cmd, -1, <<"unable to parse exec create response"/utf8>>}} end, fun(Exec_id) -> Start_body = gleam@json:object( [{<<"Detach"/utf8>>, gleam@json:bool(false)}, {<<"Tty"/utf8>>, gleam@json:bool(false)}] ), gleam@result:'try'( case request( <<"POST"/utf8>>, <<<<"/exec/"/utf8, Exec_id/binary>>/binary, "/start"/utf8>>, gleam@json:to_string(Start_body) ) of {ok, {200, Body@1}} -> {ok, Body@1}; {ok, {Status@1, Body@2}} -> {error, {exec_failed, Id, Cmd, -1, <<<<<<"start exec HTTP "/utf8, (erlang:integer_to_binary( Status@1 ))/binary>>/binary, ": "/utf8>>/binary, Body@2/binary>>}}; {error, E@1} -> {error, E@1} end, fun(Raw) -> {Stdout, Stderr} = docker_transport:split_log_streams( Raw ), gleam@result:'try'( case request( <<"GET"/utf8>>, <<<<"/exec/"/utf8, Exec_id/binary>>/binary, "/json"/utf8>>, <<""/utf8>> ) of {ok, {200, Body@3}} -> case gleam@json:parse( Body@3, gleam@dynamic@decode:at( [<<"ExitCode"/utf8>>], {decoder, fun gleam@dynamic@decode:decode_int/1} ) ) of {ok, Code} -> {ok, Code}; {error, _} -> {error, {exec_failed, Id, Cmd, -1, <<"unable to parse exec inspect response"/utf8>>}} end; {ok, {Status@2, Body@4}} -> {error, {exec_failed, Id, Cmd, -1, <<<<<<"inspect exec HTTP "/utf8, (erlang:integer_to_binary( Status@2 ))/binary>>/binary, ": "/utf8>>/binary, Body@4/binary>>}}; {error, E@2} -> {error, E@2} end, fun(Exit_code) -> {ok, {exec_result, Exit_code, Stdout, Stderr}} end ) end ) end ) end ). -file("src/testcontainer/internal/docker.gleam", 670). ?DOC(false). -spec create_network(binary()) -> {ok, binary()} | {error, testcontainer@error:error()}. create_network(Name) -> case contains_crlf(Name) of true -> {error, {docker_api_error, <<"POST"/utf8>>, <<"/networks/create"/utf8>>, 0, <<"network name contains CR/LF"/utf8>>}}; false -> Body = gleam@json:object( [{<<"Name"/utf8>>, gleam@json:string(Name)}, {<<"Driver"/utf8>>, gleam@json:string(<<"bridge"/utf8>>)}] ), case request_json( <<"POST"/utf8>>, <<"/networks/create"/utf8>>, Body ) of {ok, Response} -> case gleam@json:parse( Response, gleam@dynamic@decode:at( [<<"Id"/utf8>>], {decoder, fun gleam@dynamic@decode:decode_string/1} ) ) of {ok, Nid} -> {ok, Nid}; {error, _} -> {error, {docker_api_error, <<"POST"/utf8>>, <<"/networks/create"/utf8>>, 0, <<"parse error"/utf8>>}} end; {error, E} -> {error, E} end end. -file("src/testcontainer/internal/docker.gleam", 703). ?DOC(false). -spec remove_network(binary()) -> {ok, nil} | {error, testcontainer@error:error()}. remove_network(Id) -> request_ok(<<"DELETE"/utf8>>, <<"/networks/"/utf8, Id/binary>>, <<""/utf8>>). -file("src/testcontainer/internal/docker.gleam", 714). ?DOC(false). -spec copy_file_to(binary(), binary(), binary()) -> {ok, nil} | {error, testcontainer@error:error()}. copy_file_to(Container_id, Host_path, Container_path) -> case (contains_crlf(Container_id) orelse contains_crlf(Host_path)) orelse contains_crlf( Container_path ) of true -> {error, {file_copy_failed, Container_path, <<"path contains CR/LF"/utf8>>}}; false -> case docker_transport:copy_file_to_container( Container_id, Host_path, Container_path ) of {ok, nil} -> {ok, nil}; {error, Reason} -> {error, {file_copy_failed, Container_path, Reason}} end end.