Class: CemAcpt::Scan::DaemonClient

Inherits:
Object
  • Object
show all
Defined in:
lib/cem_acpt/scan/daemon_client.rb

Overview

HTTP client for the on-node scan daemon. Mirrors the role of Goss::Api: build URIs, drive the async scan endpoints, parse JSON responses, turn the result into a typed Result.

The daemon is installed by Provision::Linux#scan_provision_commands and serves four endpoints on the configurable scan port:

GET  /health  -> 200 OK once the daemon has started and the scanner
                 binaries it needs are present on disk.
POST /scan    -> 202 Accepted; kicks off the scan in a background thread.
                 409 Conflict if a scan is already running.
GET  /status  -> { "state": "idle" | "running" | "done" | "error" }.
GET  /result  -> 200 with the JSON result when state is terminal,
                 404 otherwise.

#scan drives the async flow: POST /scan, then poll /status, then GET /result. Each request is short-lived to avoid the connection-drop failure mode that affected the previous synchronous /scan design.

Constant Summary collapse

DEFAULT_PORT =
8084
DEFAULT_READY_TIMEOUT =
60
DEFAULT_HTTP_TIMEOUT =

30 minutes — overall deadline for a scan

1800
DEFAULT_POLL_INTERVAL =
10
DEFAULT_REQUEST_TIMEOUT =
60

Instance Method Summary collapse

Constructor Details

#initialize(host:, port: DEFAULT_PORT, ready_timeout: DEFAULT_READY_TIMEOUT, http_timeout: DEFAULT_HTTP_TIMEOUT, poll_interval: DEFAULT_POLL_INTERVAL) ⇒ DaemonClient

Returns a new instance of DaemonClient.

Parameters:

  • host (String)

    The IP or DNS name of the test node.

  • port (Integer) (defaults to: DEFAULT_PORT)

    The port the daemon listens on.

  • ready_timeout (Integer) (defaults to: DEFAULT_READY_TIMEOUT)

    How long to wait for /health.

  • http_timeout (Integer) (defaults to: DEFAULT_HTTP_TIMEOUT)

    Overall deadline for a scan (POST + polls + result fetch).

  • poll_interval (Integer) (defaults to: DEFAULT_POLL_INTERVAL)

    Seconds between /status polls.



41
42
43
44
45
46
47
48
# File 'lib/cem_acpt/scan/daemon_client.rb', line 41

def initialize(host:, port: DEFAULT_PORT, ready_timeout: DEFAULT_READY_TIMEOUT,
               http_timeout: DEFAULT_HTTP_TIMEOUT, poll_interval: DEFAULT_POLL_INTERVAL)
  @host = host
  @port = port
  @ready_timeout = ready_timeout
  @http_timeout = http_timeout
  @poll_interval = poll_interval
end

Instance Method Details

#scan(test_case:, scanner:, profile:, threshold:) ⇒ Result

Kicks off the scan, polls until it finishes, and fetches the result.

Parameters:

  • test_case (String)

    Acceptance-test directory name.

  • scanner (Symbol)

    :openscap or :ciscat.

  • profile (String)

    Scanner-native profile id.

  • threshold (Float)

    Pass threshold (0-100).

Returns:

Raises:



75
76
77
78
79
80
# File 'lib/cem_acpt/scan/daemon_client.rb', line 75

def scan(test_case:, scanner:, profile:, threshold:)
  start_scan
  wait_for_completion
  body = fetch_result
  Result.new(test_case: test_case, scanner: scanner, profile: profile, threshold: threshold, body: body)
end

#wait_until_readyObject

Polls /health until it returns 200 or the timeout elapses.

Raises:



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/cem_acpt/scan/daemon_client.rb', line 52

def wait_until_ready
  deadline = Time.now + @ready_timeout
  last_error = nil
  while Time.now < deadline
    begin
      return true if get(URI("http://#{@host}:#{@port}/health"), timeout: 5).first.to_i == 200
    rescue StandardError => e
      last_error = e
    end
    sleep 2
  end
  msg = "Scan daemon at #{@host}:#{@port} did not become ready within #{@ready_timeout}s"
  msg += " (last error: #{last_error.class}: #{last_error.message})" if last_error
  raise DaemonNotReadyError, msg
end