Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

agent: add agent subscription for agent-local config

+121 -18
+1 -1
apps/sower_agent/lib/sower_agent/config.ex
··· 40 40 }, 41 41 subscriptions: %Schema{ 42 42 type: :array, 43 - items: SowerClient.Schemas.Orchestration.Subscription, 43 + items: SowerAgent.Subscription, 44 44 default: [] 45 45 } 46 46 },
+20 -15
apps/sower_agent/lib/sower_agent/socket_client.ex
··· 5 5 6 6 alias SowerAgent.Storage 7 7 8 - def handle_call( 9 - {:deployment_request, subscription = %SowerClient.Schemas.Orchestration.Subscription{}}, 10 - _from, 11 - socket 12 - ) do 8 + def handle_call({:deployment_request, %{sid: sid}}, _from, socket) do 13 9 {:ok, upgrade_request} = 14 10 SowerClient.Schemas.Orchestration.DeploymentRequest.new(%{ 15 - subscription_sids: [subscription.sid] 11 + subscription_sids: [sid] 16 12 }) 17 13 18 14 {:ok, ref} = push_message(socket, upgrade_request) ··· 24 20 def handle_cast(:register_subscriptions, socket) do 25 21 subscriptions = 26 22 SowerAgent.Config.get().subscriptions 27 - |> Enum.map(fn sub -> 28 - with {:ok, ref} <- push_message(socket, sub), 29 - {:ok, subscription} <- await_reply(ref), 30 - {:ok, subscription} <- 31 - SowerClient.Schemas.Orchestration.Subscription.cast(subscription) do 32 - Logger.debug(subscription) 33 - subscription 23 + |> Enum.map(fn agent_sub -> 24 + # Convert to client schema before sending to server 25 + client_sub = SowerAgent.Subscription.to_client_schema(agent_sub) 26 + 27 + with {:ok, ref} <- push_message(socket, client_sub), 28 + {:ok, response} <- await_reply(ref), 29 + {:ok, registered} <- 30 + SowerClient.Schemas.Orchestration.Subscription.cast(response) do 31 + Logger.debug(registered) 32 + # Merge server-assigned sid back into agent subscription 33 + %{agent_sub | sid: registered.sid} 34 34 else 35 35 {:error, error} -> 36 - Logger.error(msg: "Failed to register subscription", error: error, subscription: sub) 36 + Logger.error( 37 + msg: "Failed to register subscription", 38 + error: error, 39 + subscription: agent_sub 40 + ) 41 + 37 42 nil 38 43 39 44 :error -> 40 45 Logger.error( 41 46 msg: "Failed to register subscription with unknown error", 42 - subscription: sub 47 + subscription: agent_sub 43 48 ) 44 49 45 50 nil
+95
apps/sower_agent/lib/sower_agent/subscription.ex
··· 1 + defmodule SowerAgent.Subscription do 2 + @moduledoc """ 3 + Agent-side subscription schema that extends the client subscription 4 + with agent-only configuration fields like polling schedules. 5 + 6 + Use `to_client_schema/1` to convert to the server-compatible schema 7 + when communicating with the Sower server. 8 + """ 9 + alias OpenApiSpex.Schema 10 + require OpenApiSpex 11 + 12 + alias SowerClient.Schemas.Orchestration.Subscription.Rule 13 + 14 + OpenApiSpex.schema(%{ 15 + title: "AgentSubscription", 16 + type: :object, 17 + properties: %{ 18 + # Fields shared with server (mirrors SowerClient.Schemas.Orchestration.Subscription) 19 + sid: %Schema{ 20 + type: :string, 21 + description: "Subscription sid allocated by Sower", 22 + readOnly: true, 23 + nullable: true 24 + }, 25 + seed_name: %Schema{ 26 + type: :string, 27 + description: "Name of the seed", 28 + example: "myhost" 29 + }, 30 + seed_type: %Schema{ 31 + type: :string, 32 + description: "Type of the seed", 33 + enum: SowerClient.Schemas.Seed.seed_types(), 34 + example: "nixos" 35 + }, 36 + rules: %Schema{ 37 + type: :array, 38 + items: Rule, 39 + default: [], 40 + description: "Tag-based rules to filter seeds" 41 + }, 42 + 43 + # Agent-only fields 44 + schedule: %Schema{ 45 + type: :string, 46 + description: "Cron expression for polling schedule", 47 + example: "*/15 * * * *", 48 + nullable: true 49 + }, 50 + poll_on_connect: %Schema{ 51 + type: :boolean, 52 + description: "Whether to request deployment immediately on connect", 53 + default: false 54 + } 55 + }, 56 + required: [:seed_name, :seed_type] 57 + }) 58 + 59 + @doc """ 60 + Convert to the client schema for sending to the server. 61 + Strips agent-only fields. 62 + """ 63 + def to_client_schema(%__MODULE__{} = sub) do 64 + %SowerClient.Schemas.Orchestration.Subscription{ 65 + sid: sub.sid, 66 + seed_name: sub.seed_name, 67 + seed_type: sub.seed_type, 68 + rules: sub.rules 69 + } 70 + end 71 + 72 + @doc """ 73 + Cast a map to the AgentSubscription struct with validation. 74 + """ 75 + def cast(attrs) do 76 + spec = build_spec() 77 + resolved_schema = spec.components.schemas["AgentSubscription"] 78 + OpenApiSpex.cast_value(attrs, resolved_schema, spec) 79 + end 80 + 81 + def cast!(attrs) do 82 + {:ok, val} = cast(attrs) 83 + val 84 + end 85 + 86 + defp build_spec do 87 + %OpenApiSpex.OpenApi{ 88 + info: %OpenApiSpex.Info{title: "AgentSubscription", version: "1.0.0"}, 89 + paths: %{}, 90 + components: nil 91 + } 92 + |> OpenApiSpex.resolve_schema_modules() 93 + |> OpenApiSpex.add_schemas([__MODULE__]) 94 + end 95 + end
+5 -2
dev-agent.json
··· 6 6 { 7 7 "seed_name": "deck", 8 8 "seed_type": "nixos", 9 - "rules": ["source=dev"] 9 + "rules": ["source=dev"], 10 + "schedule": "*/15 * * * *", 11 + "poll_on_connect": true 10 12 }, 11 13 { 12 14 "seed_name": "deck", 13 - "seed_type": "home-manager" 15 + "seed_type": "home-manager", 16 + "poll_on_connect": false 14 17 } 15 18 ] 16 19 }