Go-based network automation with YANG models, gRPC, Ansible, Terraform, and Kubernetes integration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
102 lines
2.9 KiB
Go
102 lines
2.9 KiB
Go
package quartermaster
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
|
|
"github.com/guildhouse-co/kedge/internal/config"
|
|
)
|
|
|
|
// Client is a gRPC client for the Quartermaster governance and notary services.
|
|
type Client struct {
|
|
cfg config.QuartermasterConfig
|
|
conn *grpc.ClientConn
|
|
log *zap.SugaredLogger
|
|
|
|
// TODO: Add typed gRPC stub clients when proto generation is wired up.
|
|
// governance quartermasterv1.GovernanceServiceClient
|
|
// notary quartermasterv1.QuartermasterNotaryClient
|
|
}
|
|
|
|
// NewClient creates a new Quartermaster gRPC client.
|
|
func NewClient(cfg config.QuartermasterConfig) (*Client, error) {
|
|
log := zap.L().Sugar().Named("quartermaster")
|
|
|
|
// TODO: Use mTLS with SPIFFE SVID for production.
|
|
opts := []grpc.DialOption{
|
|
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
|
}
|
|
|
|
conn, err := grpc.NewClient(cfg.Endpoint, opts...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to connect to quartermaster at %s: %w", cfg.Endpoint, err)
|
|
}
|
|
|
|
log.Infof("connected to quartermaster at %s", cfg.Endpoint)
|
|
|
|
return &Client{
|
|
cfg: cfg,
|
|
conn: conn,
|
|
log: log,
|
|
}, nil
|
|
}
|
|
|
|
// Close closes the gRPC connection.
|
|
func (c *Client) Close() error {
|
|
if c.conn != nil {
|
|
return c.conn.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SubmitSessionTransit submits a SessionTransitArtifact to Quartermaster for notarization.
|
|
// This records overlay session crossings between clusters.
|
|
func (c *Client) SubmitSessionTransit(ctx context.Context, artifact *SessionTransitArtifact) error {
|
|
artifact.Timestamp = time.Now().UTC()
|
|
|
|
// Serialize to canonical JSON (RFC 8785) for hashing.
|
|
canonical, err := artifact.CanonicalBytes()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to serialize session transit artifact: %w", err)
|
|
}
|
|
|
|
c.log.Infow("submitting session transit",
|
|
"session_id", artifact.SessionID,
|
|
"source", artifact.SourceCluster,
|
|
"dest", artifact.DestCluster,
|
|
"mode", artifact.GrantedMode,
|
|
"bytes", len(canonical),
|
|
)
|
|
|
|
// TODO: Call governance.CreateIntent + notary.CreateAnchor via gRPC stubs.
|
|
// For Phase 1, the anchor buffer flush is async — operations are not blocked.
|
|
_ = canonical
|
|
return nil
|
|
}
|
|
|
|
// SubmitNetworkMutation submits a NetworkMutationArtifact to Quartermaster for notarization.
|
|
// This records underlay infrastructure config changes.
|
|
func (c *Client) SubmitNetworkMutation(ctx context.Context, artifact *NetworkMutationArtifact) error {
|
|
artifact.Timestamp = time.Now().UTC()
|
|
|
|
canonical, err := artifact.CanonicalBytes()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to serialize network mutation artifact: %w", err)
|
|
}
|
|
|
|
c.log.Infow("submitting network mutation",
|
|
"mutation_id", artifact.MutationID,
|
|
"device", artifact.Device,
|
|
"operation", artifact.Operation,
|
|
"bytes", len(canonical),
|
|
)
|
|
|
|
// TODO: Call governance.CreateIntent + notary.CreateAnchor via gRPC stubs.
|
|
_ = canonical
|
|
return nil
|
|
}
|