### Install the YAML package Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/gopkg.in/yaml.v2/README.md Use the go get command to install the package into your Go environment. ```bash go get gopkg.in/yaml.v2 ``` -------------------------------- ### Install Go YAML Package Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/gopkg.in/yaml.v3/README.md Use 'go get' to install the yaml.v3 package for Go. ```bash go get gopkg.in/yaml.v3 ``` -------------------------------- ### Start BOSH Metrics Server and Handle Shutdown Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt The Start method begins distributing messages and returns a shutdown function that blocks until all subscriptions are drained. Ensure graceful shutdown by closing subscription channels after messages are processed. ```go // Start begins distributing messages to subscriptions // Returns a shutdown function that blocks until all subscriptions are drained func (s *BoshMetricsServer) Start() func() { done := make(chan struct{}) go func() { for message := range s.messages { s.mu.RLock() for subscription, ch := range s.registry { select { case ch <- message: // Message sent successfully default: // Buffer full, message dropped } } s.mu.RUnlock() } close(done) }() return func() { <-done s.mu.RLock() defer s.mu.RUnlock() for _, subscriptionMsgs := range s.registry { close(subscriptionMsgs) } s.wg.Wait() } } ``` -------------------------------- ### Example output Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/gopkg.in/yaml.v2/README.md The expected output generated by running the provided Go example code. ```text --- t: {Easy! {2 [3 4]}} --- t dump: a: Easy! b: c: 2 d: [3, 4] --- m: map[a:Easy! b:map[c:2 d:[3 4]]] --- m dump: a: Easy! b: c: 2 d: - 3 - 4 ``` -------------------------------- ### Example Metrics Response Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt This is an example of the JSON response format for system metrics. ```json { "egress.auth_err": 2, "egress.processed": 15420, "egress.send_err": 0, "egress.subscription_dropped": {"forwarder-1": 0, "forwarder-2": 3}, "egress.subscription_sent": {"forwarder-1": 7710, "forwarder-2": 7707}, "ingress.read_err": 0, "ingress.received": 15420, "ingress.unmarshall_err": 5 } ``` -------------------------------- ### Start Ingestor and Handle TCP Connections Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt The Start method initiates listening on a local TCP port for incoming JSON events. It returns a shutdown function to close the listener and signal goroutines to stop. Connections are handled concurrently. ```go // Start begins listening for events over TCP // Returns a shutdown function func (i *Ingestor) Start() func() { stop := make(chan struct{}) ingressLis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", i.port)) if err != nil { log.Fatalf("failed to listen on port %d: %v", i.port, err) } log.Printf("ingestor listening on %s\n", ingressLis.Addr().String()) go func() { for { conn, err := ingressLis.Accept() if err != nil { return } go i.handleConnection(conn, stop) } }() return func() { ingressLis.Close() close(stop) } } ``` -------------------------------- ### BOSH Health Monitor Input JSON Examples Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt Example JSON payloads for heartbeat and alert events as received from the BOSH Health Monitor. ```json // Example heartbeat JSON from BOSH Health Monitor (input format) { "kind": "heartbeat", "id": "55b68400-f984-4f76-b341-cf849e07d4f9", "timestamp": 1499293724, "deployment": "cf-deployment", "agent_id": "2accd102-37e7-4dd6-b337-b3f87da97914", "job": "router", "index": "0", "instance_id": "6f60a3ce-9e4d-477f-ba45-7d29bcfab5b9", "job_state": "running", "metrics": [ { "name": "system.load.1m", "value": "2.5", "timestamp": 1499293724, "tags": { "job": "router", "index": "0", "id": "6f60a3ce-9e4d-477f-ba45-7d29bcfab5b9" } }, { "name": "system.mem.percent", "value": "45.2", "timestamp": 1499293724, "tags": { "job": "router", "index": "0" } } ] } ``` ```json // Example alert JSON from BOSH Health Monitor (input format) { "kind": "alert", "id": "93eb25a4-9348-4232-6f71-69e1e01081d7", "severity": 4, "category": "security", "title": "SSH Access Denied", "summary": "Failed password for vcap from 10.244.0.1 port 38732 ssh2", "source": "cf-deployment: router(6f721317-2399-4e38-b38c-9d1b213c2d67)", "deployment": "cf-deployment", "created_at": 1499359162 } ``` -------------------------------- ### Simulate I/O Timeout Error Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/google.golang.org/grpc/README.md Example of an error message encountered when network restrictions prevent fetching the gRPC package. ```console $ go get -u google.golang.org/grpc package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout) ``` -------------------------------- ### Create and Configure BOSH Metrics Server Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt Use NewServer to create a BoshMetricsServer, configuring subscription buffer size with functional options. It requires a channel for events, a token checker, and optional server options. ```go package egress import ( "sync" "github.com/cloudfoundry/bosh-system-metrics-server/pkg/definitions" "google.golang.org/grpc/metadata" ) type tokenChecker interface { CheckToken(token string) error } type ServerOpt func(*BoshMetricsServer) // WithSubscriptionBufferSize sets the buffer size for each subscription channel func WithSubscriptionBufferSize(n int) ServerOpt { return func(s *BoshMetricsServer) { s.subscriptionBufferSize = n } } // NewServer creates a new BoshMetricsServer // m: channel receiving events from the ingress // t: token checker for UAA authentication func NewServer(m chan *definitions.Event, t tokenChecker, opts ...ServerOpt) *BoshMetricsServer { s := &BoshMetricsServer{ messages: m, registry: make(map[string]chan *definitions.Event), tokenChecker: t, subscriptionBufferSize: 1024, } for _, o := range opts { o(s) } return s } ``` -------------------------------- ### Run mkall.sh for Old Build System Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/golang.org/x/sys/unix/README.md Use this command to generate Go files for your current OS and architecture with the old build system. Ensure GOOS and GOARCH are set correctly. ```bash mkall.sh ``` -------------------------------- ### Show mkall.sh Commands (Old Build System) Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/golang.org/x/sys/unix/README.md This command shows the build commands that will be run by mkall.sh without actually executing them, useful for the old build system. ```bash mkall.sh -n ``` -------------------------------- ### Expose Health Monitoring Endpoint Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt Initializes an HTTP server using expvar to expose internal runtime metrics. ```go package monitor import ( "expvar" "fmt" "log" "net" "net/http" ) // NewHealth creates a health metrics server func NewHealth(port uint32) *health { return &health{port} } type health struct { port uint32 } // Start initializes the health endpoint server func (s *health) Start() { lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", s.port)) if err != nil { log.Printf("unable to start health endpoint: %s", err) return } mux := http.NewServeMux() mux.Handle("/health", expvar.Handler()) log.Printf("starting health endpoint on http://%s/health\n", lis.Addr().String()) http.Serve(lis, mux) } ``` ```bash # Fetch health metrics curl http://localhost:14020/health ``` -------------------------------- ### Connect to Metrics Server with Go Client Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt Demonstrates connecting to the Bosh System Metrics Server using TLS and a UAA token. The client subscribes to the event stream and processes heartbeats and alerts. ```go // Example gRPC client connecting to the metrics server package main import ( "context" "log" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" "github.com/cloudfoundry/bosh-system-metrics-server/pkg/definitions" ) func main() { // Load TLS credentials creds, err := credentials.NewClientTLSFromFile("server-ca.crt", "") if err != nil { log.Fatalf("failed to load credentials: %v", err) } conn, err := grpc.Dial("bosh-director:25595", grpc.WithTransportCredentials(creds)) if err != nil { log.Fatalf("failed to connect: %v", err) } defer conn.Close() client := definitions.NewEgressClient(conn) // Add authorization token to context ctx := metadata.AppendToOutgoingContext( context.Background(), "authorization", "bearer ", ) // Subscribe with unique ID for full stream, or shared ID for load balancing stream, err := client.BoshMetrics(ctx, &definitions.EgressRequest{ SubscriptionId: "my-forwarder-instance", }) if err != nil { log.Fatalf("failed to subscribe: %v", err) } // Receive events for { event, err := stream.Recv() if err != nil { log.Printf("stream error: %v", err) break } switch msg := event.Message.(type) { case *definitions.Event_Heartbeat: log.Printf("Heartbeat: deployment=%s job=%s index=%d state=%s", event.Deployment, msg.Heartbeat.Job, msg.Heartbeat.Index, msg.Heartbeat.JobState) for _, m := range msg.Heartbeat.Metrics { log.Printf(" Metric: %s = %f", m.Name, m.Value) } case *definitions.Event_Alert: log.Printf("Alert: severity=%d title=%s summary=%s", msg.Alert.Severity, msg.Alert.Title, msg.Alert.Summary) } } } ``` -------------------------------- ### Deploy and Verify BOSH Director with System Metrics Server Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt Commands to deploy a BOSH director with the system-metrics-server release and verify the deployment. Ensure you have the necessary BOSH deployment and release files. ```bash # Deploy BOSH director with system metrics server bosh create-env bosh-deployment/bosh.yml \ --state=state.json \ --vars-store=creds.yml \ -o bosh-deployment/uaa.yml \ -o bosh-system-metrics-server-release/manifests/server-ops.yml \ -v director_name=my-bosh \ -v internal_ip=10.0.0.6 \ -v external_ip=203.0.113.5 # Verify deployment bosh -e my-bosh vms ``` -------------------------------- ### Run project tests locally Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/google.golang.org/grpc/CONTRIBUTING.md Execute these commands to verify code quality and functionality before submitting a pull request. ```bash ./scripts/vet.sh ``` ```bash go test -cpu 1,4 -timeout 7m ./... ``` ```bash go test -race -cpu 1,4 -timeout 7m ./... ``` -------------------------------- ### Import gRPC-Go Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/google.golang.org/grpc/README.md Add this import statement to your Go source files to include the gRPC package. ```go import "google.golang.org/grpc" ``` -------------------------------- ### Commit and Publish Release Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/github.com/onsi/gomega/RELEASING.md Commands to commit version changes, push to the repository, and create a GitHub release. ```bash git commit -m "vM.m.p" git push gh release create "vM.m.p" git fetch --tags origin master ``` -------------------------------- ### Configure System Metrics Server Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt YAML configuration file for the metrics server, defining ports, certificates, and UAA authentication. ```yaml # config.yml - Server configuration ingress-port: 25594 # Port for plugin TCP connection (localhost only) egress-port: 25595 # Port for gRPC client connections metrics-cert: /var/vcap/jobs/system-metrics-server/config/certs/system-metrics/server.crt metrics-key: /var/vcap/jobs/system-metrics-server/config/certs/system-metrics/server.key uaa-url: https://uaa.example.com:8443 uaa-ca: /var/vcap/jobs/system-metrics-server/config/certs/uaa/ca.crt uaa-client-identity: system_metrics_server uaa-client-password: secret-password health-port: 14020 # Health metrics endpoint (0 to disable) pprof-port: 14021 # pprof debug endpoint (0 to disable) ``` -------------------------------- ### Implement BOSH Health Monitor Plugin Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt Reads JSON events from stdin and forwards them to a local TCP server with automatic reconnection logic. ```go // Plugin main - reads from stdin, writes to server package main import ( "bufio" "flag" "fmt" "log" "net" "os" "time" ) const writeDeadline = 2 * time.Second func main() { serverPort := flag.Int("server-port", 25594, "The destination port to send events on localhost") flag.Parse() log.Printf("Starting system metrics plugin...\n") in := bufio.NewReader(os.Stdin) // Reconnect loop for { forwardMetricsToServer(in, *serverPort) time.Sleep(time.Second) log.Println("reconnecting to system metrics server...") } } func forwardMetricsToServer(in *bufio.Reader, port int) { conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) if err != nil { log.Printf("unable to connect to system metrics server: %s\n", err) return } log.Printf("connected to system metrics server at %s\n", conn.LocalAddr().String()) for { b, err := in.ReadBytes('\n') if err != nil { time.Sleep(time.Second) continue } conn.SetWriteDeadline(time.Now().Add(writeDeadline)) _, err = conn.Write(b) if err != nil { log.Printf("unable to write to system metrics server: %s\n", err) return } } } ``` -------------------------------- ### Enable gRPC Logging Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/google.golang.org/grpc/README.md Set these environment variables to increase the verbosity and severity level of gRPC logs for debugging purposes. ```console $ export GRPC_GO_LOG_VERBOSITY_LEVEL=99 $ export GRPC_GO_LOG_SEVERITY_LEVEL=info ``` -------------------------------- ### BOSH Job Properties for System Metrics Server Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt Reference for configuring the system-metrics-server BOSH job, including properties for ports, TLS, and UAA integration. Defaults are provided where applicable. ```yaml # BOSH job spec properties properties: bosh.director.addr: description: "The address and port of the director" default: "localhost:25555" system_metrics_server.egress_port: description: "The port which the gRPC metrics server will listen on" default: 25595 system_metrics_server.ingress_port: description: "The port for plugin TCP connection (localhost)" default: 25594 system_metrics_server.trusted_uaa_authority: description: "The client authority required to connect" default: "bosh.system_metrics.read" system_metrics_server.tls.cert: description: "The TLS certificate for the system metrics server" system_metrics_server.tls.key: description: "The TLS private key for the system metrics server" system_metrics_server.health_port: description: "The port used to obtain health metrics on localhost" default: 0 system_metrics_server.pprof_port: description: "The port for the pprof endpoint on localhost" default: 0 uaa.client_id: description: "The UAA client identity which has access to check token" uaa.client_secret: description: "The UAA client secret which has access to check token" uaa.url: description: "The UAA url" uaa.ca: description: "The UAA CA certificate" ``` -------------------------------- ### Unmarshal and Marshal YAML Data in Go Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/gopkg.in/yaml.v3/README.md Demonstrates unmarshalling YAML data into a struct and a map, and then marshalling them back to YAML. Ensure struct fields are public for correct unmarshalling. ```Go package main import ( "fmt" "log" "gopkg.in/yaml.v3" ) var data = ` a: Easy! b: c: 2 d: [3, 4] ` // Note: struct fields must be public in order for unmarshal to // correctly populate the data. type T struct { A string B struct { RenamedC int `yaml:"c"` D []int `yaml:",flow"` } } func main() { t := T{} err := yaml.Unmarshal([]byte(data), &t) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- t:\n%v\n\n", t) d, err := yaml.Marshal(&t) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- t dump:\n%s\n\n", string(d)) m := make(map[interface{}]interface{}) err = yaml.Unmarshal([]byte(data), &m) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- m:\n%v\n\n", m) d, err = yaml.Marshal(&m) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- m dump:\n%s\n\n", string(d)) } ``` -------------------------------- ### Configure Go Modules for Restricted Access Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/google.golang.org/grpc/README.md Use these commands to replace the default golang.org import path with a GitHub alias when network access is restricted. ```sh go mod edit -replace=google.golang.org/grpc=github.com/grpc/grpc-go@latest go mod tidy go mod vendor go build -mod=vendor ``` -------------------------------- ### System Call Dispatch Functions Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/golang.org/x/sys/unix/README.md These are the hand-written assembly functions for system call dispatch. They differ in the number of arguments they can pass to the kernel. ```go func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) ``` -------------------------------- ### BOSH Deployment Ops File for System Metrics Server Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt This ops file configures the BOSH deployment to include the system-metrics-server job alongside the Health Monitor. It also sets up UAA client authentication and generates necessary TLS certificates and secrets. ```yaml # server-ops.yml - BOSH deployment operations file # Add the release - type: replace path: /releases/- value: name: bosh-system-metrics-server version: latest url: https://bosh.io/d/github.com/cloudfoundry/bosh-system-metrics-server-release # Add the system-metrics-server job to the director - type: replace path: /instance_groups/name=bosh/jobs/name=health_monitor value: name: system-metrics-server release: bosh-system-metrics-server properties: system_metrics_server: tls: ca: "((system_metrics_server_ssl.ca))" cert: "((system_metrics_server_ssl.certificate))" key: "((system_metrics_server_ssl.private_key))" uaa: url: "https://((external_ip)):8443" ca: "((uaa_ssl.ca))" client_id: "system_metrics_server" client_secret: ((system_metrics_uaa_server_secret)) # Restore the health_monitor job after the server - type: replace path: /instance_groups/name=bosh/jobs/- value: name: health_monitor release: bosh # Create UAA client for forwarders to authenticate - type: replace path: /instance_groups/name=bosh/jobs/name=uaa/properties/uaa/clients/system_metrics_client? value: override: true authorized-grant-types: client_credentials scope: "" authorities: bosh.system_metrics.read secret: ((system_metrics_uaa_client_secret)) # Generate certificates and secrets - type: replace path: /variables/- value: name: system_metrics_server_ssl type: certificate options: ca: default_ca common_name: ((internal_ip)) alternative_names: [((internal_ip))] - type: replace path: /variables/- value: name: system_metrics_uaa_client_secret type: password ``` -------------------------------- ### Define BOSH Event Protocol Buffers Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt Defines the structure for heartbeat and alert messages using Protocol Buffers. ```protobuf // events.proto - Event message definitions syntax = "proto3"; package definitions; message Event { int64 timestamp = 1; // Unix nanoseconds string id = 2; // Unique event ID string deployment = 3; // BOSH deployment name oneof message { Heartbeat heartbeat = 4; Alert alert = 5; } } message Heartbeat { string agent_id = 1; // BOSH agent UUID string job = 2; // Job name (e.g., "consul", "router") int32 index = 3; // Instance index string instance_id = 4; // Instance UUID string job_state = 5; // "running", "failing", etc. message Metric { string name = 1; // e.g., "system.load.1m", "system.mem.percent" double value = 2; int64 timestamp = 3; map tags = 4; } repeated Metric metrics = 6; } message Alert { int32 severity = 1; // 1=alert, 2=critical, 3=error, 4=warning string category = 2; string title = 3; string summary = 4; string source = 5; // Source component identifier } ``` -------------------------------- ### Unmarshal and Marshal YAML data Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/gopkg.in/yaml.v2/README.md Demonstrates unmarshalling YAML into a struct or map, and marshalling data back into YAML format. Struct fields must be exported for the unmarshaller to populate them. ```Go package main import ( "fmt" "log" "gopkg.in/yaml.v2" ) var data = ` a: Easy! b: c: 2 d: [3, 4] ` // Note: struct fields must be public in order for unmarshal to // correctly populate the data. type T struct { A string B struct { RenamedC int `yaml:"c"` D []int `yaml:",flow"` } } func main() { t := T{} err := yaml.Unmarshal([]byte(data), &t) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- t:\n%v\n\n", t) d, err := yaml.Marshal(&t) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- t dump:\n%s\n\n", string(d)) m := make(map[interface{}]interface{}) err = yaml.Unmarshal([]byte(data), &m) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- m:\n%v\n\n", m) d, err = yaml.Marshal(&m) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- m dump:\n%s\n\n", string(d)) } ``` -------------------------------- ### Create Ingestor for System Metrics Plugin Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt The New function creates an Ingestor that listens on a specified TCP port, unmarshals JSON events using a provided function, and forwards them to an output channel. It's designed for local communication. ```go package ingress import ( "bufio" "fmt" "log" "net" "github.com/cloudfoundry/bosh-system-metrics-server/pkg/definitions" ) type unmarshaller func(eventJSON []byte) (*definitions.Event, error) type Ingestor struct { port int unmarshaller unmarshaller output chan *definitions.Event } // New creates a new Ingestor // p: TCP port to listen on (localhost only) // u: function to unmarshal JSON to Event // m: output channel for events func New(p int, u unmarshaller, m chan *definitions.Event) *Ingestor { return &Ingestor{ port: p, unmarshaller: u, output: m, } } ``` -------------------------------- ### Read Configuration in Go Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt Go implementation for unmarshaling the YAML configuration file into a struct. ```go // Reading configuration in Go package config import ( "io/ioutil" "gopkg.in/yaml.v2" ) type Config struct { EgressPort int `yaml:"egress-port"` IngressPort int `yaml:"ingress-port"` CertPath string `yaml:"metrics-cert"` KeyPath string `yaml:"metrics-key"` UaaURL string `yaml:"uaa-url"` UaaCA string `yaml:"uaa-ca"` UaaClientIdentity string `yaml:"uaa-client-identity"` UaaClientPassword string `yaml:"uaa-client-password"` HealthPort int `yaml:"health-port"` PProfPort int `yaml:"pprof-port"` } func Read(configFilePath string) (Config, error) { configContents, err := ioutil.ReadFile(configFilePath) if err != nil { return Config{}, err } c := Config{} err = yaml.Unmarshal(configContents, &c) return c, err } ``` -------------------------------- ### Update CHANGELOG.md Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/github.com/onsi/gomega/RELEASING.md Generates a list of changes since the last tag and prepends them to the CHANGELOG.md file. ```bash LAST_VERSION=$(git tag --sort=version:refname | tail -n1) CHANGES=$(git log --pretty=format:'- %s [%h]' HEAD...$LAST_VERSION) echo -e "## NEXT\n\n$CHANGES\n\n### Features\n\n### Fixes\n\n### Maintenance\n\n$(cat CHANGELOG.md)" > CHANGELOG.md ``` -------------------------------- ### gRPC Egress Service - BoshMetrics Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt The Egress gRPC service streams BOSH system metrics to authenticated clients. Clients must provide a UAA token with the `bosh.system_metrics.read` authority in the gRPC metadata. Subscription IDs are used for load balancing. ```APIDOC ## POST / Egress.BoshMetrics ### Description Streams BOSH system metrics (heartbeats and alerts) to authenticated clients over a secure gRPC connection. Supports subscription-based load balancing. ### Method GRPC STREAM ### Endpoint `bosh-director:25595` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **subscription_id** (string) - Required - An identifier for the client subscription. Clients with the same `subscription_id` will receive events in a round-robin fashion. Clients with different `subscription_id`s will each receive a full copy of the event stream. ### Request Example ```go // Example gRPC client connecting to the metrics server package main import ( "context" "log" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" "github.com/cloudfoundry/bosh-system-metrics-server/pkg/definitions" ) func main() { // Load TLS credentials creds, err := credentials.NewClientTLSFromFile("server-ca.crt", "") if err != nil { log.Fatalf("failed to load credentials: %v", err) } conn, err := grpc.Dial("bosh-director:25595", grpc.WithTransportCredentials(creds)) if err != nil { log.Fatalf("failed to connect: %v", err) } defer conn.Close() client := definitions.NewEgressClient(conn) // Add authorization token to context ctx := metadata.AppendToOutgoingContext( context.Background(), "authorization", "bearer ", ) // Subscribe with unique ID for full stream, or shared ID for load balancing stream, err := client.BoshMetrics(ctx, &definitions.EgressRequest{ SubscriptionId: "my-forwarder-instance", }) if err != nil { log.Fatalf("failed to subscribe: %v", err) } // Receive events for { event, err := stream.Recv() if err != nil { log.Printf("stream error: %v", err) break } switch msg := event.Message.(type) { case *definitions.Event_Heartbeat: log.Printf("Heartbeat: deployment=%s job=%s index=%d state=%s", event.Deployment, msg.Heartbeat.Job, msg.Heartbeat.Index, msg.Heartbeat.JobState) for _, m := range msg.Heartbeat.Metrics { log.Printf(" Metric: %s = %f", m.Name, m.Value) } case *definitions.Event_Alert: log.Printf("Alert: severity=%d title=%s summary=%s", msg.Alert.Severity, msg.Alert.Title, msg.Alert.Summary) } } } ``` ### Response #### Success Response (200) - **Event** (stream) - A stream of `definitions.Event` objects containing either `Heartbeat` or `Alert` messages. #### Response Example ```json { "deployment": "my-deployment", "job": "my-job", "index": 0, "job_state": "running", "metrics": [ { "name": "cpu.user", "value": 10.5 } ] } ``` #### Error Handling - **GRPC Status Unavailable**: If the server is unreachable. - **GRPC Status Unauthenticated**: If the UAA token is missing or invalid. - **GRPC Status Permission Denied**: If the UAA token lacks the `bosh.system_metrics.read` authority. ``` -------------------------------- ### Define gRPC Egress Service Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt Defines the Egress service and BoshMetrics RPC method for streaming metrics. Requires an authorization token in the gRPC metadata. ```protobuf // server.proto - gRPC service definition syntax = "proto3"; package definitions; import "events.proto"; service Egress { // Stream BOSH metrics to authenticated clients // Requires "authorization" metadata with valid UAA token rpc BoshMetrics(EgressRequest) returns (stream definitions.Event) {} } message EgressRequest { // Subscription ID for load balancing // Same ID = events distributed round-robin // Different ID = each gets full event stream string subscription_id = 1; } ``` -------------------------------- ### Rebase commit history Source: https://github.com/cloudfoundry/bosh-system-metrics-server-release/blob/main/src/vendor/google.golang.org/grpc/CONTRIBUTING.md Use this command to curate commit history and incorporate upstream changes. ```bash rebase -i upstream/master ``` -------------------------------- ### Process Incoming TCP Connections and Unmarshal Events Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt The handleConnection method reads data line by line from a TCP connection, unmarshals each line into an Event, and sends it to the output channel. It stops processing if the connection closes or a stop signal is received. ```go func (i *Ingestor) handleConnection(conn net.Conn, stop chan struct{}) { reader := bufio.NewReader(conn) for { b, err := reader.ReadBytes('\n') if err != nil { return } evt, err := i.unmarshaller(b) if err != nil { continue } select { case <-stop: return default: i.output <- evt } } } ``` -------------------------------- ### Handle gRPC BOSH Metrics Stream with Token Validation Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt The BoshMetrics gRPC handler verifies the authorization token from incoming metadata before registering subscriptions and streaming events. It returns an error if the token is missing or invalid. ```go // BoshMetrics is the gRPC handler for streaming metrics // Verifies auth token from "authorization" metadata func (s *BoshMetricsServer) BoshMetrics(r *definitions.EgressRequest, srv definitions.Egress_BoshMetricsServer) error { // Token validation md, _ := metadata.FromIncomingContext(srv.Context()) tokens := md["authorization"] if len(tokens) == 0 { return errors.New("Request does not include authorization token") } if err := s.tokenChecker.CheckToken(tokens[0]); err != nil { return err } // Register subscription and stream events s.wg.Add(1) defer s.wg.Done() m := s.register(r.SubscriptionId) for event := range m { if err := srv.Send(event); err != nil { return err } } return nil } ``` -------------------------------- ### Validate UAA Authorization Tokens Source: https://context7.com/cloudfoundry/bosh-system-metrics-server-release/llms.txt Verifies client tokens against a UAA server endpoint to ensure they possess the required authority. ```go package tokenchecker import ( "crypto/tls" "fmt" "net/http" "net/url" "strings" "time" ) type TokenCheckerConfig struct { UaaURL string // UAA server URL (e.g., "https://uaa.example.com:8443") TLSConfig *tls.Config // TLS config with UAA CA certificate UaaClient string // Client ID for /check_token endpoint UaaPassword string // Client secret Authority string // Required authority (e.g., "bosh.system_metrics.read") } type TokenChecker struct { cfg *TokenCheckerConfig httpClient *http.Client } // New creates a TokenChecker with the given configuration func New(cfg *TokenCheckerConfig) *TokenChecker { return &TokenChecker{ cfg: cfg, httpClient: &http.Client{ Transport: &http.Transport{ TLSClientConfig: cfg.TLSConfig, }, Timeout: 30 * time.Second, }, } } // CheckToken verifies a token has the required authority func (t *TokenChecker) CheckToken(token string) error { form := url.Values{} form.Set("token", token) form.Set("scopes", t.cfg.Authority) req, err := http.NewRequest( http.MethodPost, fmt.Sprintf("%s/check_token", t.cfg.UaaURL), strings.NewReader(form.Encode()), ) if err != nil { return err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.SetBasicAuth(t.cfg.UaaClient, t.cfg.UaaPassword) res, err := t.httpClient.Do(req) if err != nil { return err } if res.StatusCode < 200 || res.StatusCode > 299 { return fmt.Errorf("check_token failed with status: %d", res.StatusCode) } return nil } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.