fix ports
This commit is contained in:
parent
a85b38748f
commit
1fdd0bf5a5
@ -124,7 +124,8 @@ GET /event_stream?uuid=<host_uuid>&task_id=<task_id>
|
||||
| --- | --- | --- | --- |
|
||||
| `entrypoint` | string[] | `[]` | Docker create config 的 `entrypoint`。 |
|
||||
| `envs` | string[] | `[]` | 环境变量列表,例如 `["A=1"]`,对应 Docker create config 的 `env`。 |
|
||||
| `expose` | string[] | `[]` | 容器暴露端口,只表示容器端口,不支持宿主机端口绑定。 |
|
||||
| `expose` | string[] | `[]` | 容器暴露端口,只表示容器端口,不绑定宿主机端口。 |
|
||||
| `ports` | string[] | `[]` | 宿主机到容器的端口映射,格式为 `host_port:container_port`,例如 `["8080:80", "443:443"]`。 |
|
||||
| `volumes` | string[] | `[]` | volume bind 列表,格式见后文。 |
|
||||
| `networks` | string[] | `[]` | Docker network 名称列表,用于 networking config。 |
|
||||
| `network_mode` | string | `""` | Docker host config 的 `network_mode`。 |
|
||||
@ -150,10 +151,9 @@ GET /event_stream?uuid=<host_uuid>&task_id=<task_id>
|
||||
|
||||
| 字段 | 当前行为 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `ports` | 明确拒绝 | 如果传入会返回 `unsupported container config keys: ports`。当前只支持 `expose`,不支持宿主机端口绑定。 |
|
||||
| `container_dir` | 明确拒绝 | 容器目录由 efka 按系统默认规则管理,HTTP 调用方不能指定 efka 主机上的部署目录。 |
|
||||
| `env_file` | 忽略 | 当前校验不会识别该字段,后续构造 Docker create options 时也不会使用。 |
|
||||
| 其他未知字段 | 忽略 | 除 `ports` 和 `container_dir` 外,未知字段不会报错,也不会进入内部部署参数。 |
|
||||
| 其他未知字段 | 忽略 | 除 `container_dir` 外,未知字段不会报错,也不会进入内部部署参数。 |
|
||||
|
||||
## 5. 校验规则
|
||||
|
||||
@ -167,7 +167,7 @@ GET /event_stream?uuid=<host_uuid>&task_id=<task_id>
|
||||
|
||||
第二层在 `docker_container_builder:deploy_request/2`:
|
||||
|
||||
- 先拒绝不支持的 `ports` 和 `container_dir` 字段。
|
||||
- 先拒绝不支持的 `container_dir` 字段。
|
||||
- 检查必填字段是否存在。
|
||||
- 检查已知字段类型。
|
||||
- 检查 `healthcheck` 内部字段类型,避免无效嵌套字段进入构造阶段。
|
||||
@ -243,7 +243,7 @@ HTTP handler 最多等待 10 秒。超时返回 HTTP 504,其他参数或执行
|
||||
| `user` | `user` | 默认 `""`。 |
|
||||
| `working_dir` | `working_dir` | 默认 `""`。 |
|
||||
| `hostname` | `hostname` | 默认 `""`。 |
|
||||
| `exposed_ports` | `expose` | 转成 `#{container_port, protocol}` map 列表。 |
|
||||
| `exposed_ports` | `expose` + `ports` | 转成 `#{container_port, protocol}` map 列表;`ports` 中的容器端口会自动补进 `exposed_ports`。 |
|
||||
| `healthcheck` | `healthcheck` | 未传时为 `undefined`。 |
|
||||
|
||||
## 8. Docker host config 映射
|
||||
@ -263,6 +263,7 @@ HTTP handler 最多等待 10 秒。超时返回 HTTP 504,其他参数或执行
|
||||
| `memory_reservation` | `mem_reservation` | 解析成字节数,未传为 `0`。 |
|
||||
| `nano_cpus` | `cpus` | `cpus * 1000000000`,未传为 `0`。 |
|
||||
| `cpu_shares` | `cpu_shares` | 未传为 `0`。 |
|
||||
| `port_bindings` | `ports` | 转成 `#{host_ip, host_port, container_port, protocol}` map 列表。 |
|
||||
| `ulimits` | `ulimits` | 转成 ulimit map 列表。 |
|
||||
| `tmpfs` | `tmpfs` | 转成 map。 |
|
||||
| `sysctls` | `sysctls` | 默认 `{}`。 |
|
||||
@ -339,6 +340,32 @@ HTTP handler 最多等待 10 秒。超时返回 HTTP 504,其他参数或执行
|
||||
|
||||
端口必须是无符号整数,且不能超过 `4294967295`。
|
||||
|
||||
### ports
|
||||
|
||||
输入:
|
||||
|
||||
```json
|
||||
["8080:80", "443:443", "8053:53/udp"]
|
||||
```
|
||||
|
||||
转换为 `create.host_config.port_bindings`:
|
||||
|
||||
```erlang
|
||||
[
|
||||
#{<<"host_ip">> => <<>>, <<"host_port">> => 8080, <<"container_port">> => 80, <<"protocol">> => <<"tcp">>},
|
||||
#{<<"host_ip">> => <<>>, <<"host_port">> => 443, <<"container_port">> => 443, <<"protocol">> => <<"tcp">>},
|
||||
#{<<"host_ip">> => <<>>, <<"host_port">> => 8053, <<"container_port">> => 53, <<"protocol">> => <<"udp">>}
|
||||
]
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 端口映射之间使用冒号分隔:`host_port:container_port`。
|
||||
- 容器端口可以带协议:`host_port:container_port/protocol`。
|
||||
- 未指定协议时默认为 `tcp`。
|
||||
- host port 和 container port 都不能为空,且必须是 `0..65535` 范围内的无符号整数。
|
||||
- `ports` 中出现的容器端口会自动补进 `create.config.exposed_ports`,因此不需要在 `expose` 里重复声明。
|
||||
|
||||
### volumes
|
||||
|
||||
输入:
|
||||
@ -599,6 +626,7 @@ HTTP handler 最多等待 10 秒。超时返回 HTTP 504,其他参数或执行
|
||||
<<"memory_reservation">> => 0,
|
||||
<<"nano_cpus">> => 0,
|
||||
<<"cpu_shares">> => 0,
|
||||
<<"port_bindings">> => [],
|
||||
<<"ulimits">> => [],
|
||||
<<"tmpfs">> => #{},
|
||||
<<"sysctls">> => #{},
|
||||
@ -628,7 +656,7 @@ HTTP handler 最多等待 10 秒。超时返回 HTTP 504,其他参数或执行
|
||||
{
|
||||
"error": {
|
||||
"code": 400,
|
||||
"message": "unsupported container config keys: ports"
|
||||
"message": "invalid port binding"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -72,7 +72,7 @@ container_ref(ContainerName) when is_binary(ContainerName) ->
|
||||
|
||||
-spec ensure_supported_deploy_config(map()) -> ok.
|
||||
ensure_supported_deploy_config(Config) when is_map(Config) ->
|
||||
UnsupportedKeys = [Key || Key <- [<<"ports">>, <<"container_dir">>], maps:is_key(Key, Config)],
|
||||
UnsupportedKeys = [Key || Key <- [<<"container_dir">>], maps:is_key(Key, Config)],
|
||||
case UnsupportedKeys of
|
||||
[] ->
|
||||
ok;
|
||||
@ -267,7 +267,7 @@ build_docker_container_config(Config) when is_map(Config) ->
|
||||
user => maps:get(<<"user">>, Config, <<>>),
|
||||
working_dir => maps:get(<<"working_dir">>, Config, <<>>),
|
||||
hostname => maps:get(<<"hostname">>, Config, <<>>),
|
||||
exposed_ports => build_exposed_ports(maps:get(<<"expose">>, Config, [])),
|
||||
exposed_ports => build_exposed_ports(Config),
|
||||
healthcheck => build_healthcheck(maps:get(<<"healthcheck">>, Config, undefined))
|
||||
}.
|
||||
|
||||
@ -285,6 +285,7 @@ build_docker_host_config(Config) when is_map(Config) ->
|
||||
memory_reservation => default_uint64(parse_optional_size_bytes(maps:get(<<"mem_reservation">>, Config, undefined), <<"mem_reservation">>)),
|
||||
nano_cpus => default_uint64(parse_optional_nano_cpus(maps:get(<<"cpus">>, Config, undefined))),
|
||||
cpu_shares => default_uint64(maps:get(<<"cpu_shares">>, Config, undefined)),
|
||||
port_bindings => build_port_bindings(maps:get(<<"ports">>, Config, [])),
|
||||
ulimits => build_ulimits(maps:get(<<"ulimits">>, Config, #{})),
|
||||
tmpfs => maps:from_list(build_tmpfs_options(maps:get(<<"tmpfs">>, Config, []))),
|
||||
sysctls => maps:get(<<"sysctls">>, Config, #{}),
|
||||
@ -357,9 +358,14 @@ volume_bind(HostPath, ContainerPath, true) when is_binary(HostPath), is_binary(C
|
||||
volume_bind(HostPath, ContainerPath, false) when is_binary(HostPath), is_binary(ContainerPath) ->
|
||||
<<HostPath/binary, ":", ContainerPath/binary>>.
|
||||
|
||||
-spec build_exposed_ports([binary()]) -> [map()].
|
||||
build_exposed_ports(ExposeSpecs) when is_list(ExposeSpecs) ->
|
||||
[build_exposed_port(ExposeSpec) || ExposeSpec <- ExposeSpecs].
|
||||
-spec build_exposed_ports(map()) -> [map()].
|
||||
build_exposed_ports(Config) when is_map(Config) ->
|
||||
ExposePorts = [build_exposed_port(ExposeSpec) || ExposeSpec <- maps:get(<<"expose">>, Config, [])],
|
||||
BoundPorts = [
|
||||
#{container_port => ContainerPort, protocol => Protocol} ||
|
||||
#{container_port := ContainerPort, protocol := Protocol} <- build_port_bindings(maps:get(<<"ports">>, Config, []))
|
||||
],
|
||||
unique_ports(ExposePorts ++ BoundPorts).
|
||||
|
||||
-spec build_exposed_port(binary()) -> map().
|
||||
build_exposed_port(ExposeSpec) when is_binary(ExposeSpec) ->
|
||||
@ -370,6 +376,52 @@ build_exposed_port(ExposeSpec) when is_binary(ExposeSpec) ->
|
||||
#{container_port => parse_uint32(PortBin, <<"expose">>), protocol => Protocol}
|
||||
end.
|
||||
|
||||
-spec build_port_bindings([binary()]) -> [map()].
|
||||
build_port_bindings(PortSpecs) when is_list(PortSpecs) ->
|
||||
[build_port_binding(PortSpec) || PortSpec <- PortSpecs].
|
||||
|
||||
-spec build_port_binding(binary()) -> map().
|
||||
build_port_binding(PortSpec) when is_binary(PortSpec) ->
|
||||
case binary:split(PortSpec, <<":">>, [global]) of
|
||||
[HostPortBin, ContainerPortSpec] when HostPortBin =/= <<>>, ContainerPortSpec =/= <<>> ->
|
||||
{ContainerPort, Protocol} = parse_container_port_spec(ContainerPortSpec, <<"ports">>),
|
||||
#{
|
||||
host_ip => <<>>,
|
||||
host_port => parse_tcp_port(HostPortBin, <<"ports.host_port">>),
|
||||
container_port => ContainerPort,
|
||||
protocol => Protocol
|
||||
};
|
||||
_ ->
|
||||
throw({error, <<"invalid port binding">>})
|
||||
end.
|
||||
|
||||
-spec parse_container_port_spec(binary(), binary()) -> {non_neg_integer(), binary()}.
|
||||
parse_container_port_spec(PortSpec, Field) when is_binary(PortSpec), is_binary(Field) ->
|
||||
case binary:split(PortSpec, <<"/">>) of
|
||||
[PortBin] ->
|
||||
{parse_tcp_port(PortBin, Field), <<"tcp">>};
|
||||
[PortBin, Protocol] when Protocol =/= <<>> ->
|
||||
{parse_tcp_port(PortBin, Field), Protocol};
|
||||
_ ->
|
||||
throw({error, <<"invalid port binding">>})
|
||||
end.
|
||||
|
||||
-spec unique_ports([map()]) -> [map()].
|
||||
unique_ports(Ports) when is_list(Ports) ->
|
||||
{_Seen, Result} = lists:foldl(
|
||||
fun(Port = #{container_port := ContainerPort, protocol := Protocol}, {Seen, Acc}) ->
|
||||
Key = {ContainerPort, Protocol},
|
||||
case maps:is_key(Key, Seen) of
|
||||
true ->
|
||||
{Seen, Acc};
|
||||
false ->
|
||||
{Seen#{Key => true}, [Port | Acc]}
|
||||
end
|
||||
end,
|
||||
{#{}, []},
|
||||
Ports),
|
||||
lists:reverse(Result).
|
||||
|
||||
-spec build_device_mappings([binary()]) -> [map()].
|
||||
build_device_mappings(DeviceSpecs) when is_list(DeviceSpecs) ->
|
||||
[build_device_mapping(DeviceSpec) || DeviceSpec <- DeviceSpecs].
|
||||
@ -479,6 +531,16 @@ parse_uint32(Value, Field) when is_binary(Value), is_binary(Field) ->
|
||||
throw({error, <<"value overflow for ", Field/binary>>})
|
||||
end.
|
||||
|
||||
-spec parse_tcp_port(binary(), binary()) -> non_neg_integer().
|
||||
parse_tcp_port(Value, Field) when is_binary(Value), is_binary(Field) ->
|
||||
Parsed = parse_uint32(Value, Field),
|
||||
case Parsed =< 65535 of
|
||||
true ->
|
||||
Parsed;
|
||||
false ->
|
||||
throw({error, <<"port out of range for ", Field/binary>>})
|
||||
end.
|
||||
|
||||
-spec parse_uint64(binary(), binary()) -> non_neg_integer().
|
||||
parse_uint64(Value0, Field) when is_binary(Value0), is_binary(Field) ->
|
||||
Value = trim_binary(Value0),
|
||||
|
||||
@ -240,6 +240,7 @@ handle_message_frame(Body, State) ->
|
||||
|
||||
-spec handle_event_stream_frame(binary(), map()) -> ok.
|
||||
handle_event_stream_frame(UUID, #{<<"task_id">> := TaskId, <<"type">> := <<"close">>, <<"stream">> := Reason}) ->
|
||||
logger:debug("[ssl_channel] get uuid: ~p, task_id: ~p, close with reason: ~p", [UUID, TaskId, Reason]),
|
||||
iot_container_task_sup:close(UUID, TaskId, Reason);
|
||||
handle_event_stream_frame(UUID, #{<<"task_id">> := TaskId, <<"type">> := Type, <<"stream">> := Stream}) ->
|
||||
logger:debug("[ssl_channel] get uuid: ~p, task_id: ~p, type: ~ts, stream: ~ts", [UUID, TaskId, Type, Stream]),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user