Skip to content

Commit

Permalink
[RTC-454] Healthcheck endpoint (#143)
Browse files Browse the repository at this point in the history
* Initial healthcheck endpoint implementation
* Add distribution info
* Add test
  • Loading branch information
sgfn authored Feb 6, 2024
1 parent b88fbd5 commit 8f6e1ca
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ RUN chmod +x docker-entrypoint.sh

ENV HOME=/app

HEALTHCHECK CMD curl --fail -H "authorization: Bearer ${JF_SERVER_API_TOKEN}" http://localhost:${JF_PORT:-8080}/room || exit 1
HEALTHCHECK CMD curl --fail -H "authorization: Bearer ${JF_SERVER_API_TOKEN}" http://localhost:${JF_PORT:-8080}/health || exit 1

ENTRYPOINT ["./docker-entrypoint.sh"]

Expand Down
2 changes: 2 additions & 0 deletions lib/jellyfish/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ defmodule Jellyfish.Application do
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Jellyfish.Supervisor]

Application.put_env(:jellyfish, :start_time, System.monotonic_time(:second))

Logger.info("Starting Jellyfish v#{@version}")
Logger.info("Distribution config: #{inspect(Keyword.delete(dist_config, :cookie))}")
Logger.info("WebRTC config: #{inspect(webrtc_config)}")
Expand Down
57 changes: 57 additions & 0 deletions lib/jellyfish_web/api_spec/health_report.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule JellyfishWeb.ApiSpec.HealthReport do
@moduledoc false

require OpenApiSpex
alias OpenApiSpex.Schema

defmodule Status do
@moduledoc false

require OpenApiSpex

OpenApiSpex.schema(%{
title: "HealthReportStatus",
description: "Informs about the status of Jellyfish or a specific service",
type: :string,
enum: ["UP", "DOWN"],
example: "UP"
})
end

defmodule Distribution do
@moduledoc false

require OpenApiSpex

OpenApiSpex.schema(%{
title: "HealthReportDistribution",
description: "Informs about the status of Jellyfish distribution",
type: :object,
properties: %{
enabled: %Schema{
type: :boolean,
description: "Whether distribution is enabled on this Jellyfish"
},
nodeStatus: Status,
nodesInCluster: %Schema{
type: :integer,
description:
"Amount of nodes (including this Jellyfish's node) in the distribution cluster"
}
},
required: [:nodeStatus, :nodesInCluster]
})
end

OpenApiSpex.schema(%{
title: "HealthReport",
description: "Describes overall Jellyfish health",
type: :object,
properties: %{
status: Status,
uptime: %Schema{type: :integer, description: "Uptime of Jellyfish (in seconds)"},
distribution: Distribution
},
required: [:status, :uptime, :distribution]
})
end
4 changes: 3 additions & 1 deletion lib/jellyfish_web/api_spec/responses.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
{RoomsListingResponse, "Response containing list of all rooms",
%OpenApiSpex.Schema{type: :array, items: JellyfishWeb.ApiSpec.Room}},
{RecordingListResponse, "Response containing list of all recording",
%OpenApiSpex.Schema{type: :array, items: %OpenApiSpex.Schema{type: :string}}}
%OpenApiSpex.Schema{type: :array, items: %OpenApiSpex.Schema{type: :string}}},
{HealthcheckResponse, "Response containing health report of Jellyfish",
JellyfishWeb.ApiSpec.HealthReport}
]
|> Enum.map(fn {title, description, schema} ->
module = Module.concat(JellyfishWeb.ApiSpec, title)
Expand Down
47 changes: 47 additions & 0 deletions lib/jellyfish_web/controllers/healthcheck_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
defmodule JellyfishWeb.HealthcheckController do
use JellyfishWeb, :controller
use OpenApiSpex.ControllerSpecs

alias JellyfishWeb.ApiSpec

action_fallback JellyfishWeb.FallbackController

operation :show,
operation_id: "healthcheck",
summary: "Describes the health of Jellyfish",
responses: [
ok: ApiSpec.data("Healthy", ApiSpec.HealthcheckResponse),
internal_server_error: ApiSpec.data("Unhealthy", ApiSpec.HealthcheckResponse)
]

def show(conn, _params) do
report = get_health_report()

conn
|> put_resp_content_type("application/json")
|> render("show.json", report: report)
end

defp get_health_report() do
%{
status: :up,
uptime: get_uptime(),
distribution: get_distribution_report()
}
end

defp get_uptime() do
System.monotonic_time(:second) - Application.fetch_env!(:jellyfish, :start_time)
end

defp get_distribution_report() do
alive? = Node.alive?()
visible_nodes = Node.list() |> length()

%{
enabled: Application.fetch_env!(:jellyfish, :dist_config)[:enabled],
node_status: if(alive?, do: :up, else: :down),
nodes_in_cluster: visible_nodes + if(alive?, do: 1, else: 0)
}
end
end
22 changes: 22 additions & 0 deletions lib/jellyfish_web/controllers/healthcheck_json.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule JellyfishWeb.HealthcheckJSON do
@moduledoc false

def show(%{report: report}) do
%{data: data(report)}
end

def data(%{status: status, uptime: uptime, distribution: distribution}) do
%{
status: status_str(status),
uptime: uptime,
distribution: %{
enabled: distribution.enabled,
nodeStatus: status_str(distribution.node_status),
nodesInCluster: distribution.nodes_in_cluster
}
}
end

defp status_str(:up), do: "UP"
defp status_str(:down), do: "DOWN"
end
6 changes: 6 additions & 0 deletions lib/jellyfish_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ defmodule JellyfishWeb.Router do
plug :bearer_auth
end

scope "/health", JellyfishWeb do
pipe_through :api

get "/", HealthcheckController, :show
end

scope "/room", JellyfishWeb do
pipe_through :api

Expand Down
73 changes: 73 additions & 0 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ components:
title: PeerStatus
type: string
x-struct: Elixir.JellyfishWeb.ApiSpec.Peer.Status
HealthReportStatus:
description: Informs about the status of Jellyfish or a specific service
enum:
- UP
- DOWN
example: UP
title: HealthReportStatus
type: string
x-struct: Elixir.JellyfishWeb.ApiSpec.HealthReport.Status
RoomDetailsResponse:
description: Response containing room details
properties:
Expand Down Expand Up @@ -139,6 +148,16 @@ components:
title: PeerOptionsWebRTC
type: object
x-struct: Elixir.JellyfishWeb.ApiSpec.Peer.WebRTC
HealthcheckResponse:
description: Response containing health report of Jellyfish
properties:
data:
$ref: '#/components/schemas/HealthReport'
required:
- data
title: HealthcheckResponse
type: object
x-struct: Elixir.JellyfishWeb.ApiSpec.HealthcheckResponse
ComponentHLS:
description: Describes the HLS component
properties:
Expand Down Expand Up @@ -334,6 +353,23 @@ components:
title: ComponentOptions
type: object
x-struct: Elixir.JellyfishWeb.ApiSpec.Component.Options
HealthReportDistribution:
description: Informs about the status of Jellyfish distribution
properties:
enabled:
description: Whether distribution is enabled on this Jellyfish
type: boolean
nodeStatus:
$ref: '#/components/schemas/HealthReportStatus'
nodesInCluster:
description: Amount of nodes (including this Jellyfish's node) in the distribution cluster
type: integer
required:
- nodeStatus
- nodesInCluster
title: HealthReportDistribution
type: object
x-struct: Elixir.JellyfishWeb.ApiSpec.HealthReport.Distribution
ComponentPropertiesRTSP:
description: Properties specific to the RTSP component
properties:
Expand Down Expand Up @@ -368,6 +404,23 @@ components:
title: PeerOptions
type: object
x-struct: Elixir.JellyfishWeb.ApiSpec.Peer.Options
HealthReport:
description: Describes overall Jellyfish health
properties:
distribution:
$ref: '#/components/schemas/HealthReportDistribution'
status:
$ref: '#/components/schemas/HealthReportStatus'
uptime:
description: Uptime of Jellyfish (in seconds)
type: integer
required:
- status
- uptime
- distribution
title: HealthReport
type: object
x-struct: Elixir.JellyfishWeb.ApiSpec.HealthReport
Peer:
description: Describes peer status
properties:
Expand Down Expand Up @@ -528,6 +581,26 @@ info:
version: 0.2.0
openapi: 3.0.0
paths:
/health:
get:
callbacks: {}
operationId: healthcheck
parameters: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/HealthcheckResponse'
description: Healthy
'500':
content:
application/json:
schema:
$ref: '#/components/schemas/HealthcheckResponse'
description: Unhealthy
summary: Describes the health of Jellyfish
tags: []
/hls/{room_id}/subscribe:
post:
callbacks: {}
Expand Down
30 changes: 30 additions & 0 deletions test/jellyfish_web/controllers/healthcheck_controller_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
defmodule JellyfishWeb.HealthcheckControllerTest do
use JellyfishWeb.ConnCase, async: true

import OpenApiSpex.TestAssertions

@schema JellyfishWeb.ApiSpec.spec()

setup %{conn: conn} do
server_api_token = Application.fetch_env!(:jellyfish, :server_api_token)
conn = put_req_header(conn, "authorization", "Bearer " <> server_api_token)

[conn: conn]
end

test "healthcheck without distribution", %{conn: conn} do
conn = get(conn, ~p"/health")
response = json_response(conn, :ok)
assert_response_schema(response, "HealthcheckResponse", @schema)

assert %{
"status" => "UP",
"uptime" => _uptime,
"distribution" => %{
"enabled" => false,
"nodeStatus" => "DOWN",
"nodesInCluster" => 0
}
} = response["data"]
end
end

0 comments on commit 8f6e1ca

Please sign in to comment.