Skip to content

Commit

Permalink
add log search for certificate issues
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeljguarino committed Feb 10, 2025
1 parent 7ead5c8 commit 58c40b4
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 30 deletions.
10 changes: 10 additions & 0 deletions lib/console/ai/evidence/base.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ defmodule Console.AI.Evidence.Base do
alias Kazan.Apis.Core.V1, as: CoreV1
alias Kazan.Models.Apimachinery.Meta.V1, as: MetaV1

@cluster_key {__MODULE__, :cluster}

defmacro __using__(_) do
quote do
import Console.AI.Evidence.Base
Expand All @@ -17,8 +19,15 @@ defmodule Console.AI.Evidence.Base do
end
end

def save_cluster(cluster), do: Process.put(@cluster_key, cluster)
def get_cluster(), do: Process.get(@cluster_key)

def history(msgs, claims \\ %{}), do: {:ok, msgs, claims}

def as_history({:ok, res}) when is_list(res), do: {:ok, res, %{}}
def as_history({:ok, res, %{} = claims}) when is_list(res), do: {:ok, res, claims}
def as_history({:error, err}), do: {:error, err}

def default_empty({:ok, res}, fun), do: {:ok, fun.(res)}
def default_empty(_, _), do: {:ok, []}

Expand Down Expand Up @@ -102,6 +111,7 @@ defmodule Console.AI.Evidence.Base do
def meaning(:pending), do: meaning(:stale)

def save_kubeconfig(cluster) do
save_cluster(cluster)
with %Kazan.Server{} = server <- Clusters.control_plane(cluster),
do: Kube.Utils.save_kubeconfig(server)
end
Expand Down
3 changes: 2 additions & 1 deletion lib/console/ai/evidence/cluster_insight_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defimpl Console.AI.Evidence, for: Console.Schema.ClusterInsightComponent do
save_kubeconfig(cluster)
with {:ok, resource} <- Resource.resource(to_svc_component(comp), cluster),
{:ok, events} <- Resource.events(resource),
{:ok, hydration} <- Resource.hydrate(resource) do
{:ok, hydration, claims} <- Resource.hydrate(resource) do
(
[{:user, """
The kubernetes resource #{component(comp)}. It is deployed on the #{distro(cluster.distro)} kubernetes cluster named #{cluster.name} with version #{cluster.version}
Expand All @@ -29,6 +29,7 @@ defimpl Console.AI.Evidence, for: Console.Schema.ClusterInsightComponent do
++ tpl_hydration(hydration)
)
|> Logs.with_logging(comp)
|> Context.evidence(claims)
|> Context.result()
end
end
Expand Down
20 changes: 20 additions & 0 deletions lib/console/ai/evidence/component/certificate.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
defmodule Console.AI.Evidence.Component.Certificate do
use Console.AI.Evidence.Base
alias Console.Repo
alias Console.AI.Evidence.Logs
alias Console.AI.Evidence.Context
alias Console.Schema.{Cluster, OperationalLayout}

def hydrate(%Kube.Certificate{metadata: %MetaV1.ObjectMeta{namespace: ns, name: n}}) when is_binary(ns) do
Kube.Client.list_certificate_requests(ns)
Expand All @@ -10,6 +14,22 @@ defmodule Console.AI.Evidence.Component.Certificate do
end)
|> Enum.map(& {:user, "the certificate manages a set of certificate requests #{component(&1)} with current state:\n#{encode(&1)}"})
end)
|> case do
{:ok, history} -> history
_ -> []
end
|> maybe_add_logs(Repo.preload(get_cluster(), [:operational_layout]))
end
def hydrate(_), do: {:ok, []}

defp maybe_add_logs(history, %Cluster{
operational_layout: %OperationalLayout{namespaces: %OperationalLayout.Namespaces{
cert_manager: cm,
external_dns: ed
}
}} = cluster) when is_binary(cm) do
Logs.with_logging(history, cluster, force: true, namespaces: [cm | (ed || [])])
|> Context.result()
end
defp maybe_add_logs(history, _), do: {:ok, history}
end
48 changes: 26 additions & 22 deletions lib/console/ai/evidence/component/resource.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,24 @@ defmodule Console.AI.Evidence.Component.Resource do
|> Kube.Client.raw()
end

def events(resource) do
{uid, ns} = details(resource)
case ns do
ns when is_binary(ns) ->
CoreV1.list_namespaced_event!(ns, field_selector: "involvedObject.uid=#{uid}")
|> Kube.Utils.run()
_ -> {:ok, []}
end
end

def hydrate(resource) do
do_hydrate(resource)
|> as_history()
end

def generate(resource) do
with {:ok, events} <- events(resource),
{:ok, hydration} <- hydrate(resource) do
{:ok, hydration, claims} <- hydrate(resource) do
{:ok, [{:user, """
The kubernetes component #{description(resource)} could also be related.
Expand All @@ -58,8 +73,7 @@ defmodule Console.AI.Evidence.Component.Resource do
"""
}]
++ tpl_events(events)
++ tpl_hydration(hydration)
}
++ tpl_hydration(hydration), claims}
end
end

Expand All @@ -76,25 +90,15 @@ defmodule Console.AI.Evidence.Component.Resource do
"#{g}/#{v} #{k}#{ns(namespace)} with name #{name}"
end

def hydrate(%AppsV1.Deployment{} = dep), do: Deployment.hydrate(dep)
def hydrate(%AppsV1.StatefulSet{} = ss), do: StatefulSet.hydrate(ss)
def hydrate(%AppsV1.DaemonSet{} = ds), do: DaemonSet.hydrate(ds)
def hydrate(%NetworkingV1.Ingress{} = ing), do: Ingress.hydrate(ing)
def hydrate(%BatchV1.CronJob{} = cj), do: CronJob.hydrate(cj)
def hydrate(%BatchV1.Job{} = cj), do: Job.hydrate(cj)
def hydrate(%Kube.Certificate{} = cert), do: Certificate.hydrate(cert)
def hydrate(%{"metadata" => _} = raw), do: Raw.hydrate(raw)
def hydrate(_), do: {:ok, []}

def events(resource) do
{uid, ns} = details(resource)
case ns do
ns when is_binary(ns) ->
CoreV1.list_namespaced_event!(ns, field_selector: "involvedObject.uid=#{uid}")
|> Kube.Utils.run()
_ -> {:ok, []}
end
end
defp do_hydrate(%AppsV1.Deployment{} = dep), do: Deployment.hydrate(dep)
defp do_hydrate(%AppsV1.StatefulSet{} = ss), do: StatefulSet.hydrate(ss)
defp do_hydrate(%AppsV1.DaemonSet{} = ds), do: DaemonSet.hydrate(ds)
defp do_hydrate(%NetworkingV1.Ingress{} = ing), do: Ingress.hydrate(ing)
defp do_hydrate(%BatchV1.CronJob{} = cj), do: CronJob.hydrate(cj)
defp do_hydrate(%BatchV1.Job{} = cj), do: Job.hydrate(cj)
defp do_hydrate(%Kube.Certificate{} = cert), do: Certificate.hydrate(cert)
defp do_hydrate(%{"metadata" => _} = raw), do: Raw.hydrate(raw)
defp do_hydrate(_), do: {:ok, []}

defp details(%{metadata: %{uid: uid} = meta}), do: {uid, Map.get(meta, :namespace)}
defp details(%{"metadata" => %{"uid" => uid} = meta}), do: {uid, meta["namespace"]}
Expand Down
4 changes: 3 additions & 1 deletion lib/console/ai/evidence/context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ defmodule Console.AI.Evidence.Context do

def new(history), do: %__MODULE__{history: history}

def evidence(%__MODULE__{} = ctx, evidence), do: %{ctx | evidence: [evidence | ctx.evidence]}
def evidence(%__MODULE__{} = ctx, %{} = e) when map_size(e) > 0, do: %{ctx | evidence: [e | ctx.evidence]}
def evidence(%__MODULE__{} = ctx, [_ | _] = es), do: %{ctx | evidence: ctx.evidence ++ es}
def evidence(%__MODULE__{} = ctx, _), do: ctx

def prompt(%__MODULE__{history: hist} = ctx, msg), do: %{ctx | history: append(hist, msg)}

Expand Down
9 changes: 6 additions & 3 deletions lib/console/ai/evidence/logs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ defmodule Console.AI.Evidence.Logs do
alias Console.Deployments.Settings
alias Console.Logs.Provider, as: LogEngine
alias Console.AI.{Provider, Tools.Logging, Evidence.Context}
alias Console.Schema.{Service, ClusterInsightComponent, DeploymentSettings}
alias Console.Schema.{Service, ClusterInsightComponent, Cluster, DeploymentSettings}

require Logger

@type parent :: Service.t | ClusterInsightComponent.t | Cluster.t

@base [query: "error fatal exception", limit: 10]
@format ~s({"timestamp": datetime, "log": string})

Expand All @@ -18,10 +20,10 @@ defmodule Console.AI.Evidence.Logs do
container.
"""

@spec with_logging(Provider.history, Service.t | ClusterInsightComponent.t) :: Context.t
@spec with_logging(Provider.history, parent) :: Context.t
def with_logging(history, parent, opts \\ []) do
force = Keyword.get(opts, :force, false)
args = Keyword.take(opts, ~w(lines q)a)
args = Keyword.take(opts, ~w(lines q namespaces)a)
with %DeploymentSettings{logging: %{enabled: true}} <- Settings.cached(),
true <- use_logs?(history, force),
{:ok, query} <- query(parent, args),
Expand All @@ -43,6 +45,7 @@ defmodule Console.AI.Evidence.Logs do
%{cluster: cluster} = Repo.preload(comp, [:cluster])
build_query(cluster, args ++ @base ++ [cluster_id: cluster.id, namespaces: [comp.namespace]])
end
defp query(%Cluster{} = cluster, args), do: build_query(cluster, args ++ @base ++ [cluster_id: cluster.id])
defp query(_, _), do: {:error, :invalid_parent}

defp build_query(resource, args), do: {:ok, %{Query.new(args) | resource: resource}}
Expand Down
5 changes: 3 additions & 2 deletions lib/console/ai/evidence/service_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ defimpl Console.AI.Evidence, for: Console.Schema.ServiceComponent do
save_kubeconfig(cluster)
with {:ok, resource} <- Resource.resource(comp, cluster),
{:ok, events} <- Resource.events(resource),
{:ok, hydration} <- Resource.hydrate(resource) do
{:ok, hydration, claims} <- Resource.hydrate(resource) do
history(
[{:user, """
The kubernetes component #{description(comp)} is in #{comp.state} state, meaning #{meaning(comp.state)}. It is deployed
Expand All @@ -26,7 +26,8 @@ defimpl Console.AI.Evidence, for: Console.Schema.ServiceComponent do
"""
}]
++ tpl_events(events)
++ tpl_hydration(hydration)
++ tpl_hydration(hydration),
claims
)
end
end
Expand Down
5 changes: 4 additions & 1 deletion lib/console/graphql/kubernetes/ingress.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ defmodule Console.GraphQl.Kubernetes.Ingress do
field :status, non_null(:service_status)
field :spec, non_null(:ingress_spec)

field :certificates, list_of(:certificate), resolve: fn model, _, _ -> Kubernetes.ingress_certificates(model) end
field :certificates, list_of(:certificate) do
resolve fn model, _, _ -> Kubernetes.ingress_certificates(model) end
middleware ErrorHandler
end

field :raw, non_null(:string), resolve: fn model, _, _ -> encode(model) end
field :events, list_of(:event), resolve: fn model, _, _ -> Kubernetes.list_events(model) end
Expand Down

0 comments on commit 58c40b4

Please sign in to comment.