mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-10 15:04:25 +08:00
Support http proxy configuration via env variables (#2850)
This commit is contained in:
parent
7de7378da6
commit
cb047455a6
8 changed files with 123 additions and 30 deletions
|
@ -2,6 +2,8 @@ defmodule Livebook.Application do
|
||||||
use Application
|
use Application
|
||||||
|
|
||||||
def start(_type, _args) do
|
def start(_type, _args) do
|
||||||
|
Livebook.Utils.HTTP.set_proxy_options()
|
||||||
|
|
||||||
Livebook.ZTA.init()
|
Livebook.ZTA.init()
|
||||||
create_teams_hub = parse_teams_hub()
|
create_teams_hub = parse_teams_hub()
|
||||||
setup_optional_dependencies()
|
setup_optional_dependencies()
|
||||||
|
|
|
@ -250,7 +250,9 @@ defmodule Livebook.FlyAPI do
|
||||||
|> Keyword.merge(opts)
|
|> Keyword.merge(opts)
|
||||||
|> Keyword.merge(test_options())
|
|> Keyword.merge(test_options())
|
||||||
|
|
||||||
case Req.request(opts) do
|
req = opts |> Req.new() |> Livebook.Utils.req_attach_defaults()
|
||||||
|
|
||||||
|
case Req.request(req) do
|
||||||
{:ok, %{status: status, body: body}} when status in 200..299 ->
|
{:ok, %{status: status, body: body}} when status in 200..299 ->
|
||||||
{:ok, body}
|
{:ok, body}
|
||||||
|
|
||||||
|
@ -281,7 +283,9 @@ defmodule Livebook.FlyAPI do
|
||||||
]
|
]
|
||||||
|> Keyword.merge(test_options())
|
|> Keyword.merge(test_options())
|
||||||
|
|
||||||
case Req.request(opts) do
|
req = opts |> Req.new() |> Livebook.Utils.req_attach_defaults()
|
||||||
|
|
||||||
|
case Req.request(req) do
|
||||||
{:ok, %{status: 200, body: body}} ->
|
{:ok, %{status: 200, body: body}} ->
|
||||||
case body do
|
case body do
|
||||||
%{"errors" => [%{"extensions" => %{"code" => "UNAUTHORIZED"}} | _]} ->
|
%{"errors" => [%{"extensions" => %{"code" => "UNAUTHORIZED"}} | _]} ->
|
||||||
|
|
|
@ -91,7 +91,7 @@ defmodule Livebook.K8sAPI do
|
||||||
"""
|
"""
|
||||||
@spec watch_pod_events(kubeconfig(), String.t(), String.t()) :: {:ok, Enumerable.t()} | error()
|
@spec watch_pod_events(kubeconfig(), String.t(), String.t()) :: {:ok, Enumerable.t()} | error()
|
||||||
def watch_pod_events(kubeconfig, namespace, name) do
|
def watch_pod_events(kubeconfig, namespace, name) do
|
||||||
req = Req.new() |> Kubereq.attach(kubeconfig: kubeconfig, api_version: "v1", kind: "Event")
|
req = build_req() |> Kubereq.attach(kubeconfig: kubeconfig, api_version: "v1", kind: "Event")
|
||||||
|
|
||||||
Kubereq.watch(req, namespace,
|
Kubereq.watch(req, namespace,
|
||||||
field_selectors: [
|
field_selectors: [
|
||||||
|
@ -120,7 +120,7 @@ defmodule Livebook.K8sAPI do
|
||||||
when data: list(%{name: String.t()})
|
when data: list(%{name: String.t()})
|
||||||
def list_namespaces(kubeconfig) do
|
def list_namespaces(kubeconfig) do
|
||||||
req =
|
req =
|
||||||
Req.new() |> Kubereq.attach(kubeconfig: kubeconfig, api_version: "v1", kind: "Namespace")
|
build_req() |> Kubereq.attach(kubeconfig: kubeconfig, api_version: "v1", kind: "Namespace")
|
||||||
|
|
||||||
case Kubereq.list(req, nil) do
|
case Kubereq.list(req, nil) do
|
||||||
{:ok, %{status: 200, body: %{"items" => items}}} ->
|
{:ok, %{status: 200, body: %{"items" => items}}} ->
|
||||||
|
@ -198,7 +198,7 @@ defmodule Livebook.K8sAPI do
|
||||||
when data: list(%{name: String.t()})
|
when data: list(%{name: String.t()})
|
||||||
def list_storage_classes(kubeconfig) do
|
def list_storage_classes(kubeconfig) do
|
||||||
req =
|
req =
|
||||||
Req.new()
|
build_req()
|
||||||
|> Kubereq.attach(
|
|> Kubereq.attach(
|
||||||
kubeconfig: kubeconfig,
|
kubeconfig: kubeconfig,
|
||||||
api_version: "storage.k8s.io/v1",
|
api_version: "storage.k8s.io/v1",
|
||||||
|
@ -260,7 +260,7 @@ defmodule Livebook.K8sAPI do
|
||||||
}
|
}
|
||||||
|
|
||||||
req =
|
req =
|
||||||
Req.new()
|
build_req()
|
||||||
|> Kubereq.attach(
|
|> Kubereq.attach(
|
||||||
kubeconfig: kubeconfig,
|
kubeconfig: kubeconfig,
|
||||||
api_version: "authorization.k8s.io/v1",
|
api_version: "authorization.k8s.io/v1",
|
||||||
|
@ -304,11 +304,15 @@ defmodule Livebook.K8sAPI do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp pod_req(kubeconfig) do
|
defp pod_req(kubeconfig) do
|
||||||
Req.new() |> Kubereq.attach(kubeconfig: kubeconfig, api_version: "v1", kind: "Pod")
|
build_req() |> Kubereq.attach(kubeconfig: kubeconfig, api_version: "v1", kind: "Pod")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp pvc_req(kubeconfig) do
|
defp pvc_req(kubeconfig) do
|
||||||
Req.new()
|
build_req()
|
||||||
|> Kubereq.attach(kubeconfig: kubeconfig, api_version: "v1", kind: "PersistentVolumeClaim")
|
|> Kubereq.attach(kubeconfig: kubeconfig, api_version: "v1", kind: "PersistentVolumeClaim")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp build_req() do
|
||||||
|
Req.new() |> Livebook.Utils.req_attach_defaults()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,18 +6,10 @@ defmodule Livebook.Runtime.Dependencies do
|
||||||
{:ok, String.t()} | {:error, String.t()}
|
{:ok, String.t()} | {:error, String.t()}
|
||||||
def add_dependencies(code, dependencies) do
|
def add_dependencies(code, dependencies) do
|
||||||
deps = Enum.map(dependencies, & &1.dep)
|
deps = Enum.map(dependencies, & &1.dep)
|
||||||
config = Enum.reduce(dependencies, [], &deep_merge(&2, &1.config))
|
config = Enum.reduce(dependencies, [], &Livebook.Utils.keyword_deep_merge(&2, &1.config))
|
||||||
add_mix_deps(code, deps, config)
|
add_mix_deps(code, deps, config)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp deep_merge(left, right) do
|
|
||||||
if Keyword.keyword?(left) and Keyword.keyword?(right) do
|
|
||||||
Keyword.merge(left, right, fn _key, left, right -> deep_merge(left, right) end)
|
|
||||||
else
|
|
||||||
right
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Finds or adds a `Mix.install/2` call to `code` and modifies it to
|
Finds or adds a `Mix.install/2` call to `code` and modifies it to
|
||||||
include the given Mix deps.
|
include the given Mix deps.
|
||||||
|
|
|
@ -294,9 +294,9 @@ defmodule Livebook.Teams.Requests do
|
||||||
defp build_req() do
|
defp build_req() do
|
||||||
Req.new(
|
Req.new(
|
||||||
base_url: Livebook.Config.teams_url(),
|
base_url: Livebook.Config.teams_url(),
|
||||||
inet6: String.ends_with?(Livebook.Config.teams_url(), ".flycast"),
|
|
||||||
headers: [{"x-lb-version", Livebook.Config.app_version()}]
|
headers: [{"x-lb-version", Livebook.Config.app_version()}]
|
||||||
)
|
)
|
||||||
|
|> Livebook.Utils.req_attach_defaults()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_team_auth(req, nil), do: req
|
defp add_team_auth(req, nil), do: req
|
||||||
|
|
|
@ -22,19 +22,9 @@ defmodule Livebook.Teams.WebSocket do
|
||||||
{http_scheme, ws_scheme} = parse_scheme(uri)
|
{http_scheme, ws_scheme} = parse_scheme(uri)
|
||||||
state = %{status: nil, headers: [], body: []}
|
state = %{status: nil, headers: [], body: []}
|
||||||
|
|
||||||
transport_opts =
|
opts = Livebook.Utils.mint_connect_options_for_uri(uri)
|
||||||
if http_scheme == :https do
|
|
||||||
[cacerts: :public_key.cacerts_get()]
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
|
|
||||||
transport_opts =
|
opts = Keyword.merge(opts, protocols: [:http1])
|
||||||
if String.ends_with?(Livebook.Config.teams_url(), ".flycast"),
|
|
||||||
do: Keyword.put(transport_opts, :inet6, true),
|
|
||||||
else: transport_opts
|
|
||||||
|
|
||||||
opts = [protocols: [:http1], transport_opts: transport_opts]
|
|
||||||
|
|
||||||
with {:ok, conn} <- Mint.HTTP.connect(http_scheme, uri.host, uri.port, opts),
|
with {:ok, conn} <- Mint.HTTP.connect(http_scheme, uri.host, uri.port, opts),
|
||||||
{:ok, conn, ref} <- Mint.WebSocket.upgrade(ws_scheme, conn, @ws_path, headers) do
|
{:ok, conn, ref} <- Mint.WebSocket.upgrade(ws_scheme, conn, @ws_path, headers) do
|
||||||
|
|
|
@ -204,6 +204,18 @@ defmodule Livebook.Utils do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Recursively merges keyword lists.
|
||||||
|
"""
|
||||||
|
@spec keyword_deep_merge(keyword(), keyword()) :: keyword()
|
||||||
|
def keyword_deep_merge(left, right) do
|
||||||
|
if Keyword.keyword?(left) and Keyword.keyword?(right) do
|
||||||
|
Keyword.merge(left, right, fn _key, left, right -> keyword_deep_merge(left, right) end)
|
||||||
|
else
|
||||||
|
right
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Validates if the given URL is syntactically valid.
|
Validates if the given URL is syntactically valid.
|
||||||
|
|
||||||
|
@ -745,4 +757,66 @@ defmodule Livebook.Utils do
|
||||||
def ip_to_host({0, 0, 0, 0}), do: "localhost"
|
def ip_to_host({0, 0, 0, 0}), do: "localhost"
|
||||||
def ip_to_host({127, 0, 0, 1}), do: "localhost"
|
def ip_to_host({127, 0, 0, 1}), do: "localhost"
|
||||||
def ip_to_host(ip), do: ip |> :inet.ntoa() |> List.to_string()
|
def ip_to_host(ip), do: ip |> :inet.ntoa() |> List.to_string()
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Req plugin adding global Livebook-specific plugs.
|
||||||
|
|
||||||
|
The plugin covers cacerts and HTTP proxy configuration.
|
||||||
|
"""
|
||||||
|
@spec req_attach_defaults(Req.Request.t()) :: Req.Request.t()
|
||||||
|
def req_attach_defaults(req) do
|
||||||
|
Req.Request.append_request_steps(req,
|
||||||
|
connect_options: fn request ->
|
||||||
|
uri = URI.parse(request.url)
|
||||||
|
connect_options = mint_connect_options_for_uri(uri)
|
||||||
|
Req.Request.merge_options(request, connect_options: connect_options)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns options for `Mint.HTTP.connect/4` that should be used for
|
||||||
|
the given target URI.
|
||||||
|
"""
|
||||||
|
@spec mint_connect_options_for_uri(URI.t()) :: keyword()
|
||||||
|
def mint_connect_options_for_uri(uri) do
|
||||||
|
http_proxy = System.get_env("HTTP_PROXY") || System.get_env("http_proxy")
|
||||||
|
https_proxy = System.get_env("HTTPS_PROXY") || System.get_env("https_proxy") || http_proxy
|
||||||
|
|
||||||
|
no_proxy =
|
||||||
|
if no_proxy = System.get_env("NO_PROXY") || System.get_env("no_proxy") do
|
||||||
|
String.split(no_proxy, ",")
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
proxy_opts =
|
||||||
|
cond do
|
||||||
|
uri.host in no_proxy -> []
|
||||||
|
uri.scheme == "http" && http_proxy -> proxy_options(http_proxy)
|
||||||
|
uri.scheme == "https" && https_proxy -> proxy_options(https_proxy)
|
||||||
|
true -> []
|
||||||
|
end
|
||||||
|
|
||||||
|
cacertfile = Livebook.Config.cacertfile()
|
||||||
|
|
||||||
|
cert_opts =
|
||||||
|
if uri.scheme == "https" && cacertfile do
|
||||||
|
[transport_opts: [cacertfile: cacertfile]]
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
proxy_opts ++ cert_opts
|
||||||
|
end
|
||||||
|
|
||||||
|
defp proxy_options(proxy) do
|
||||||
|
uri = URI.parse(proxy || "")
|
||||||
|
|
||||||
|
if uri.host && uri.port do
|
||||||
|
[proxy: {:http, uri.host, uri.port, []}]
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -212,4 +212,31 @@ defmodule Livebook.Utils.HTTP do
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def set_proxy_options() do
|
||||||
|
http_proxy = System.get_env("HTTP_PROXY") || System.get_env("http_proxy")
|
||||||
|
https_proxy = System.get_env("HTTPS_PROXY") || System.get_env("https_proxy")
|
||||||
|
|
||||||
|
no_proxy =
|
||||||
|
if no_proxy = System.get_env("NO_PROXY") || System.get_env("no_proxy") do
|
||||||
|
no_proxy
|
||||||
|
|> String.split(",")
|
||||||
|
|> Enum.map(&String.to_charlist/1)
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
set_proxy_option(:proxy, http_proxy, no_proxy)
|
||||||
|
set_proxy_option(:https_proxy, https_proxy, no_proxy)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp set_proxy_option(proxy_scheme, proxy, no_proxy) do
|
||||||
|
uri = URI.parse(proxy || "")
|
||||||
|
|
||||||
|
if uri.host && uri.port do
|
||||||
|
host = String.to_charlist(uri.host)
|
||||||
|
:httpc.set_options([{proxy_scheme, {{host, uri.port}, no_proxy}}])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue