mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 18:20:27 +00:00
167 lines
5.2 KiB
Go
167 lines
5.2 KiB
Go
package report
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Report is the core data type. It's produced by probes, and consumed and
|
|
// stored by apps. It's composed of multiple topologies, each representing
|
|
// a different (related, but not equivalent) view of the network.
|
|
type Report struct {
|
|
// Endpoint nodes are individual (address, port) tuples on each host.
|
|
// They come from inspecting active connections and can (theoretically)
|
|
// be traced back to a process. Edges are present.
|
|
Endpoint Topology
|
|
|
|
// Address nodes are addresses (e.g. ifconfig) on each host. Certain
|
|
// information may be present in this topology that can't be mapped to
|
|
// endpoints (e.g. ICMP). Edges are present.
|
|
Address Topology
|
|
|
|
// Process nodes are processes on each host. Edges are not present.
|
|
Process Topology
|
|
|
|
// Container nodes represent all Docker containers on hosts running probes.
|
|
// Metadata includes things like containter id, name, image id etc.
|
|
// Edges are not present.
|
|
Container Topology
|
|
|
|
// ContainerImages nodes represent all Docker containers images on
|
|
// hosts running probes. Metadata includes things like image id, name etc.
|
|
// Edges are not present.
|
|
ContainerImage Topology
|
|
|
|
// Host nodes are physical hosts that run probes. Metadata includes things
|
|
// like operating system, load, etc. The information is scraped by the
|
|
// probes with each published report. Edges are not present.
|
|
Host Topology
|
|
|
|
// Overlay nodes are active peers in any software-defined network that's
|
|
// overlaid on the infrastructure. The information is scraped by polling
|
|
// their status endpoints. Edges could be present, but aren't currently.
|
|
Overlay Topology
|
|
|
|
// Sampling data for this report.
|
|
Sampling Sampling
|
|
|
|
// Window is the amount of time that this report purports to represent.
|
|
// Windows must be carefully merged. They should only be added when
|
|
// reports cover non-overlapping periods of time. By default, we assume
|
|
// that's true, and add windows in merge operations. When that's not true,
|
|
// such as in the app, we expect the component to overwrite the window
|
|
// before serving it to consumers.
|
|
Window time.Duration
|
|
}
|
|
|
|
// MakeReport makes a clean report, ready to Merge() other reports into.
|
|
func MakeReport() Report {
|
|
return Report{
|
|
Endpoint: MakeTopology(),
|
|
Address: MakeTopology(),
|
|
Process: MakeTopology(),
|
|
Container: MakeTopology(),
|
|
ContainerImage: MakeTopology(),
|
|
Host: MakeTopology(),
|
|
Overlay: MakeTopology(),
|
|
Sampling: Sampling{},
|
|
Window: 0,
|
|
}
|
|
}
|
|
|
|
// Copy returns a value copy of the report.
|
|
func (r Report) Copy() Report {
|
|
return Report{
|
|
Endpoint: r.Endpoint.Copy(),
|
|
Address: r.Address.Copy(),
|
|
Process: r.Process.Copy(),
|
|
Container: r.Container.Copy(),
|
|
ContainerImage: r.ContainerImage.Copy(),
|
|
Host: r.Host.Copy(),
|
|
Overlay: r.Overlay.Copy(),
|
|
Sampling: r.Sampling,
|
|
Window: r.Window,
|
|
}
|
|
}
|
|
|
|
// Merge merges another Report into the receiver and returns the result. The
|
|
// original is not modified.
|
|
func (r Report) Merge(other Report) Report {
|
|
cp := r.Copy()
|
|
cp.Endpoint = r.Endpoint.Merge(other.Endpoint)
|
|
cp.Address = r.Address.Merge(other.Address)
|
|
cp.Process = r.Process.Merge(other.Process)
|
|
cp.Container = r.Container.Merge(other.Container)
|
|
cp.ContainerImage = r.ContainerImage.Merge(other.ContainerImage)
|
|
cp.Host = r.Host.Merge(other.Host)
|
|
cp.Overlay = r.Overlay.Merge(other.Overlay)
|
|
cp.Sampling = r.Sampling.Merge(other.Sampling)
|
|
cp.Window += other.Window
|
|
return cp
|
|
}
|
|
|
|
// Topologies returns a slice of Topologies in this report
|
|
func (r Report) Topologies() []Topology {
|
|
return []Topology{
|
|
r.Endpoint,
|
|
r.Address,
|
|
r.Process,
|
|
r.Container,
|
|
r.ContainerImage,
|
|
r.Host,
|
|
r.Overlay,
|
|
}
|
|
}
|
|
|
|
// Validate checks the report for various inconsistencies.
|
|
func (r Report) Validate() error {
|
|
var errs []string
|
|
for _, topology := range r.Topologies() {
|
|
if err := topology.Validate(); err != nil {
|
|
errs = append(errs, err.Error())
|
|
}
|
|
}
|
|
if r.Sampling.Count > r.Sampling.Total {
|
|
errs = append(errs, fmt.Sprintf("sampling count (%d) bigger than total (%d)", r.Sampling.Count, r.Sampling.Total))
|
|
}
|
|
if len(errs) > 0 {
|
|
return fmt.Errorf("%d error(s): %s", len(errs), strings.Join(errs, "; "))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Sampling describes how the packet data sources for this report were
|
|
// sampled. It can be used to calculate effective sample rates. We can't
|
|
// just put the rate here, because that can't be accurately merged. Counts
|
|
// in e.g. edge metadata structures have already been adjusted to
|
|
// compensate for the sample rate.
|
|
type Sampling struct {
|
|
Count uint64 // observed and processed
|
|
Total uint64 // observed overall
|
|
}
|
|
|
|
// Rate returns the effective sampling rate.
|
|
func (s Sampling) Rate() float64 {
|
|
if s.Total <= 0 {
|
|
return 1.0
|
|
}
|
|
return float64(s.Count) / float64(s.Total)
|
|
}
|
|
|
|
// Merge combines two sampling structures via simple addition and returns the
|
|
// result. The original is not modified.
|
|
func (s Sampling) Merge(other Sampling) Sampling {
|
|
return Sampling{
|
|
Count: s.Count + other.Count,
|
|
Total: s.Total + other.Total,
|
|
}
|
|
}
|
|
|
|
const (
|
|
// HostNodeID is a metadata foreign key, linking a node in any topology to
|
|
// a node in the host topology. That host node is the origin host, where
|
|
// the node was originally detected.
|
|
HostNodeID = "host_node_id"
|
|
)
|