summaryrefslogtreecommitdiff
path: root/src/marlin.agents.shell/MarlinApiClient.cs
blob: 900101cb5d32cdfbd1fa3c19820fe507e66a0d81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
namespace marlin.agents.shell;

using System.Net.Http.Json;
using core.contracts;
using core.models;
using Grpc.Core;
using Grpc.Net.Client;
using ProtoBuf.Grpc.Client;

public class MarlinApiClient
{
    private readonly string _baseUrl;
    private readonly Guid _agentId;
    private readonly string _agentToken;
    
    private readonly HttpClient _http;

    public MarlinApiClient(string baseUrl, Guid agentId, string agentToken)
    {
        this._baseUrl = baseUrl;
        this._agentId = agentId;
        this._agentToken = agentToken;

        _http = new HttpClient { BaseAddress = new Uri(this._baseUrl) };
        _http.DefaultRequestHeaders.Add("Agent", $"{this._agentId} {this._agentToken}");
    }
    
    public static MarlinApiClient FromEnvironment()
    {
        var baseUrl = RequireEnv("MARLIN_API_BASE_URL");
        var agentId = RequireEnv("MARLIN_AGENT_ID");
        var agentToken = RequireEnv("MARLIN_AGENT_TOKEN");

        return new MarlinApiClient(baseUrl, Guid.Parse(agentId), agentToken);
    }

    private static string RequireEnv(string name) =>
        Environment.GetEnvironmentVariable(name)
        ?? throw new InvalidOperationException($"Missing required environment variable: {name}");

    public async Task<AgentConfiguration> Handshake(AgentStatus status, CancellationToken ct = default)
    {
        var response = await _http.PostAsJsonAsync("api/dispatch/handshake", status, ct);
        response.EnsureSuccessStatusCode();
        return (await response.Content.ReadFromJsonAsync<AgentConfiguration>(ct))!;
    }

    public async Task<DispatchJob?> QueueNextJob(CancellationToken ct = default) 
        => await _http.GetFromJsonAsync<DispatchJob?>("api/dispatch/queue", ct);

    public GrpcChannel CreateJobScopedRpc(DispatchJob job)
    {
        var credentials = CallCredentials.FromInterceptor(
            async (_, metadata) =>
            {
                metadata.Add("Agent", $"{this._agentId} {this._agentToken}");
                metadata.Add("Job", $"{job.Id} {job.Token}");
            });

        var channel = GrpcChannel.ForAddress(this._baseUrl, new GrpcChannelOptions
        {
            Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
        });

        return channel;
    }
}