agent

package
v8.56.1709838579-SHA-e... Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 8, 2024 License: Apache-2.0 Imports: 24 Imported by: 0

README

agent

A Go implementation of a CDPI agent.

Overview

The Control-to-Data-Plane Interface (CDPI) works by exchanging Protocol Buffers, a language-agnostic format and toolchain for serializing messages, transmitted using gRPC, a performant RPC framework with many sophisticated features.

This directory provides two important pieces for interacting with the Spacetime CDPI:

  • The agent (//cdpi_agent/cmd/agent), a Go binary that handle the CDPI protocol and authentication details while delegating the actual implementation of enactments to a user-configured external process. While the details of the CDPI protocol are still subject to change, the command line interface for these binaries is intended to be significantly more stable and provide an easy to develop against abstraction that insulates platform integrators from the majority of those changes.

  • The cdpi_agent library (//cdpi_agent), a Go package that provides a growing set of abstractions for writing a new CDPI agent. This library is subject to change alongside the CDPI protocol, so platform integrators are encouraged to use the agent binary until the underlying APIs reach a stable milestone.

Building

This repo uses the bazel build system. Once you have a copy of bazel in your $PATH, running bazel build //cdpi_agent/cmd/agent will build the Go binary. Similarly, running bazel build //cdpi_agent will build the Go library.

For a full list of available build targets, you can use bazel query:

bazel query //cdpi_agent/...:all

Getting started with the agent binary

Configuration

The agent binary accepts configuration in the form of a protobuf message, documented in config.proto. The message can be encoded in prototext format (human readable and writable), json, or the binary proto format. Most users will find the prototext format the easiest to use, and as such it's the default.

More details for each aspect of the configuration are provided below, but a simple configuration file might look like this:

connection_params: {
  # TODO: change to point to the domain of your spacetime instance
  cdpi_endpoint: "dns:///my_instance.spacetime.aalyria.com"

  transport_security: {
    system_cert_pool: {}
  }

  auth_strategy: {
    jwt: {
      # TODO: change to the domain of your spacetime instance
      audience: "my_instance.spacetime.aalyria.com"
      # TODO: use the email your Aalyria representative will share with you
      email: "my-cdpi-agent@example.com"
      # TODO: use the private key ID your Aalyria representative will share with you
      private_key_id: "BADDB0BACAFE"
      signing_strategy: {
	# TODO: change to the path of your PEM-encoded RSA private key
        private_key_file: "/path/to/agent/private/key.pem"
      }
    }
  }
}

# each network_node is configured with a stanza like so:
network_nodes: {
  id: "node-a"
  state_backend: {
    static_initial_state: {}
  }
  enactment_backend: {
    external_command: {
      # while each command invocation will receive a node ID as part of the
      # enactment request, you can also pass additional arguments here to help
      # integrate with your own systems
      args: "/usr/local/bin/do_enactments"
      args: "--node=a"
      args: "--format=json"
      # Encode enactment requests as JSON to the process's standard input and
      # expect any new state messages to be written as JSON to the process's
      # standard output (this is the default)
      proto_format: JSON
    }
  }
}

network_nodes: {
  id: "node-b"
  state_backend: {
    static_initial_state: {}
  }
  enactment_backend: {
    external_command: {
      args: "/usr/local/bin/some_other_enactment_cmd"
      # Use the protobuf binary format for encoding both stdin and stdout messages.
      proto_format: WIRE
    }
  }
}

See the documentation in config.proto for more details on the available options. You can use the --dry-run flag to check that your configuration is valid:

bazel run //cdpi_agent/cmd/agent -- --log-level trace --config "$PWD/config.textproto" --dry-run
INFO: Invocation ID: d8a4b02f-1e01-47cb-bd26-5366704165af
INFO: Analyzed target //cdpi_agent/cmd/agent:agent (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //cdpi_agent/cmd/agent:agent up-to-date:
  bazel-bin/cdpi_agent/cmd/agent/agent_/agent
INFO: Elapsed time: 1.768s, Critical Path: 1.56s
INFO: 3 processes: 1 internal, 2 linux-sandbox.
INFO: Build completed successfully, 3 total actions
INFO: Running command line: bazel-bin/cdpi_agent/cmd/agent/agent_/agent --log-level trace --config /path/to/config.textproto --format text --dry-run
2023-04-19 11:57:01AM INF config is valid
Authentication

The agent uses signed JSON Web Tokens (JWTs) to authenticate with the CDPI service. The JWT needs to be signed using an RSA private key with a corresponding public key that's been shared - inside of a self-signed x509 certificate - with the Aalyria team.

Creating a test keypair

For testing purposes, you can generate a valid key using the openssl tool:

# generate a private key of size 4096 and save it to agent_priv_key.pem
openssl genrsa -out agent_priv_key.pem 4096
# extract the public key and save it to an x509 certificate named
# agent_pub_key.cer (with an expiration far into the future)
openssl req -new -x509 -key agent_priv_key.pem -out agent_pub_key.cer -days 36500
Starting the agent

Assuming you've saved your configuration in a file called config.textproto, you can use the following command to start the agent:

bazel run //cdpi_agent/cmd/agent -- --config "$PWD/my_config.textproto" --log-level debug

NOTE: bazel run changes the working directory of the process, so you'll need to use absolute paths to point to the config file.

If the agent was able to authenticate correctly, you should see something like this appear as output (requires --log-level be "debug" or "trace"):

2023-04-18 08:44:48PM INF starting agent
2023-04-18 08:44:48PM DBG node controller starting nodeID=Atlantis-groundstation

Next steps

Writing a custom extproc enactment backend

Writing a custom enactment backend using the agent is relatively simple as the agent takes care of the CDPI protocol details, including timing and error reporting. When the agent receives a scheduled control update, it invokes the configured external process, writes the incoming ScheduledControlUpdate message in the encoding format of your choice to the process's stdin, and optionally reads a new ControlPlaneState message (using the same encoding format) from the process's stdout.

  • If nothing is written to stdout and the process terminates with an exit code of 0, the enactment is considered successful and the node state is assumed to have been updated to match the change.

  • If anything goes wrong during the enactment (indicated by a non-zero exit code), the process's stderr and exit code are combined to form a gRPC status which is conveyed back to the CDPI endpoint as the (failing) result of the enactment.

Since the external process only needs to be able to encode and decode JSON, it's trivial to write the platform-specific logic in whatever language best suits the task. Included in this repo are some sample programs that demonstrate basic error handling and message parsing in different languages:

  • examples/enact_flow_forward_updates.py: A python script that reads the input messages as ad-hoc JSON, implements some basic error handling, and demonstrates how one might go about enacting flow updates (the actual logic for forwarding packets is left as an exercise for the reader).

Documentation

Overview

Package agent provides a CDPI agent implementation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func OK

func OK() *status.Status

Types

type Agent

type Agent struct {
	// contains filtered or unexported fields
}

Agent is a CDPI agent that coordinates change requests across multiple nodes.

func NewAgent

func NewAgent(opts ...AgentOption) (*Agent, error)

NewAgent creates a new Agent configured with the provided options.

func (*Agent) Run

func (a *Agent) Run(ctx context.Context) error

Run starts the Agent and blocks until a fatal error is encountered or all node controllers terminate.

type AgentOption

type AgentOption interface {
	// contains filtered or unexported methods
}

AgentOption provides a well-typed and sound mechanism to configure an Agent.

func WithClock

func WithClock(clock clockwork.Clock) AgentOption

WithClock configures the Agent to use the provided clock.

func WithDialOpts

func WithDialOpts(dialOpts ...grpc.DialOption) AgentOption

WithDialOpts configures the Agent to use the provided DialOptions when connecting to the CDPI endpoint.

NOTE: The CDPI agent always uses the `grpc.WithBlock` option to ensure initial connection errors are caught immediately, whereas logical errors are often more tightly scoped to individual RPCs.

func WithNode

func WithNode(id string, opts ...NodeOption) AgentOption

WithNode configures a network node for the agent to represent.

func WithRealClock

func WithRealClock() AgentOption

WithRealClock configures the Agent to use a real clock.

func WithServerEndpoint

func WithServerEndpoint(endpoint string) AgentOption

WithServerEndpoint configures the Agent to connect to the provided endpoint.

type NodeOption

type NodeOption interface {
	// contains filtered or unexported methods
}

NodeOption provides a well-typed and sound mechanism to configure an individual node that an Agent will manage.

func WithChannelPriority

func WithChannelPriority(priority uint32) NodeOption

func WithEnactmentBackend

func WithEnactmentBackend(eb enactment.Backend) NodeOption

WithEnactmentBackend configures the EnactmentBackend for the given Node.

func WithInitialState

func WithInitialState(initState *apipb.ControlPlaneState) NodeOption

WithInitialState configures the initial state of the Node.

func WithTelemetryBackend

func WithTelemetryBackend(tb telemetry.Backend) NodeOption

WithTelemetryBackend configures the telemetry.Backend for the given Node.

Directories

Path Synopsis
cmd
agent
Package main provides a CDPI agent that is configured using a protobuf-based manifest.
Package main provides a CDPI agent that is configured using a protobuf-based manifest.
extproc
Package extproc provides an enactment.Backend implementation that relies on an external process to enact changes.
Package extproc provides an enactment.Backend implementation that relies on an external process to enact changes.
internal
agentcli
Package agentcli provides a CDPI agent that is configured using a protobuf-based manifest.
Package agentcli provides a CDPI agent that is configured using a protobuf-based manifest.
channels
Package channels provide some simple adapters to facilitate common read/write patterns.
Package channels provide some simple adapters to facilitate common read/write patterns.
extprocs
Package extprocs provides common utilities shared between the extproc backends.
Package extprocs provides common utilities shared between the extproc backends.
loggable
Package loggable provides some adaptors for logging Spacetime domain objects using the zerolog library.
Package loggable provides some adaptors for logging Spacetime domain objects using the zerolog library.
task
Package task provides some useful helpers to express common tasks like retries and adding contextual information to a context-scoped logger.
Package task provides some useful helpers to express common tasks like retries and adding contextual information to a context-scoped logger.
extproc
Package extproc provides a telemetry.Backend implementation that relies on an external process to generate telemetry reports in the specified form.
Package extproc provides a telemetry.Backend implementation that relies on an external process to generate telemetry reports in the specified form.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL