github.com/mier85/go-sensor@v1.30.1-0.20220920111756-9bf41b3bc7e0/acceptor/docker.go (about)

     1  // (c) Copyright IBM Corp. 2021
     2  // (c) Copyright Instana Inc. 2020
     3  
     4  package acceptor
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/mier85/go-sensor/aws"
    10  	"github.com/mier85/go-sensor/docker"
    11  )
    12  
    13  // DockerData is a representation of a Docker container for com.instana.plugin.docker plugin
    14  type DockerData struct {
    15  	ID               string                             `json:"Id"`
    16  	Command          string                             `json:"Command"`
    17  	CreatedAt        time.Time                          `json:"Created"`
    18  	StartedAt        time.Time                          `json:"Started"`
    19  	Image            string                             `json:"Image"`
    20  	Labels           aws.ContainerLabels                `json:"Labels,omitempty"`
    21  	Ports            string                             `json:"Ports,omitempty"`
    22  	PortBindings     string                             `json:"PortBindings,omitempty"`
    23  	Names            []string                           `json:"Names,omitempty"`
    24  	NetworkMode      string                             `json:"NetworkMode,omitempty"`
    25  	StorageDriver    string                             `json:"StorageDriver,omitempty"`
    26  	DockerVersion    string                             `json:"docker_version,omitempty"`
    27  	DockerAPIVersion string                             `json:"docker_api_version,omitempty"`
    28  	Network          *DockerNetworkAggregatedStatsDelta `json:"network,omitempty"`
    29  	CPU              *DockerCPUStatsDelta               `json:"cpu,omitempty"`
    30  	Memory           *DockerMemoryStatsUpdate           `json:"memory,omitempty"`
    31  	BlockIO          *DockerBlockIOStatsDelta           `json:"blkio,omitempty"`
    32  }
    33  
    34  // NewDockerPluginPayload returns payload for the Docker plugin of Instana acceptor
    35  func NewDockerPluginPayload(entityID string, data DockerData) PluginPayload {
    36  	const pluginName = "com.instana.plugin.docker"
    37  
    38  	return PluginPayload{
    39  		Name:     pluginName,
    40  		EntityID: entityID,
    41  		Data:     data,
    42  	}
    43  }
    44  
    45  // DockerNetworkStatsDelta represents the difference between two network interface stats
    46  type DockerNetworkStatsDelta struct {
    47  	Bytes   int `json:"bytes,omitempty"`
    48  	Packets int `json:"packets,omitempty"`
    49  	Dropped int `json:"dropped,omitempty"`
    50  	Errors  int `json:"errors,omitempty"`
    51  }
    52  
    53  // IsZero returns true is there is no difference between interface stats
    54  func (d DockerNetworkStatsDelta) IsZero() bool {
    55  	return d.Bytes == 0 && d.Packets == 0 && d.Dropped == 0 && d.Errors == 0
    56  }
    57  
    58  // DockerNetworkAggregatedStatsDelta represents the difference between two network interface stats
    59  type DockerNetworkAggregatedStatsDelta struct {
    60  	Rx *DockerNetworkStatsDelta `json:"rx,omitempty"`
    61  	Tx *DockerNetworkStatsDelta `json:"tx,omitempty"`
    62  }
    63  
    64  // NewDockerNetworkAggregatedStatsDelta calculates the aggregated difference between two snapshots
    65  // of network interface stats. It returns nil if aggregated stats for both snapshots are equal.
    66  func NewDockerNetworkAggregatedStatsDelta(prev, next map[string]docker.ContainerNetworkStats) *DockerNetworkAggregatedStatsDelta {
    67  	var rxDelta, txDelta DockerNetworkStatsDelta
    68  
    69  	for _, stats := range next {
    70  		rxDelta.Bytes += stats.RxBytes
    71  		rxDelta.Packets += stats.RxPackets
    72  		rxDelta.Dropped += stats.RxDropped
    73  		rxDelta.Errors += stats.RxErrors
    74  
    75  		txDelta.Bytes += stats.TxBytes
    76  		txDelta.Packets += stats.TxPackets
    77  		txDelta.Dropped += stats.TxDropped
    78  		txDelta.Errors += stats.TxErrors
    79  	}
    80  
    81  	for _, stats := range prev {
    82  		rxDelta.Bytes -= stats.RxBytes
    83  		rxDelta.Packets -= stats.RxPackets
    84  		rxDelta.Dropped -= stats.RxDropped
    85  		rxDelta.Errors -= stats.RxErrors
    86  
    87  		txDelta.Bytes -= stats.TxBytes
    88  		txDelta.Packets -= stats.TxPackets
    89  		txDelta.Dropped -= stats.TxDropped
    90  		txDelta.Errors -= stats.TxErrors
    91  	}
    92  
    93  	if rxDelta.IsZero() && txDelta.IsZero() {
    94  		return nil
    95  	}
    96  
    97  	var delta DockerNetworkAggregatedStatsDelta
    98  	if !rxDelta.IsZero() {
    99  		delta.Rx = &rxDelta
   100  	}
   101  	if !txDelta.IsZero() {
   102  		delta.Tx = &txDelta
   103  	}
   104  
   105  	return &delta
   106  }
   107  
   108  // DockerCPUStatsDelta represents the difference between two CPU usage stats
   109  type DockerCPUStatsDelta struct {
   110  	Total           float64 `json:"total_usage,omitempty"`
   111  	User            float64 `json:"user_usage,omitempty"`
   112  	System          float64 `json:"system_usage,omitempty"`
   113  	ThrottlingCount int     `json:"throttling_count,omitempty"`
   114  	ThrottlingTime  int     `json:"throttling_time,omitempty"`
   115  }
   116  
   117  // NewDockerCPUStatsDelta calculates the difference between two CPU usage stats. It returns nil if stats are equal.
   118  func NewDockerCPUStatsDelta(prev, next docker.ContainerCPUStats) *DockerCPUStatsDelta {
   119  	if prev == next {
   120  		return nil
   121  	}
   122  
   123  	delta := DockerCPUStatsDelta{
   124  		ThrottlingCount: next.Throttling.Periods - prev.Throttling.Periods,
   125  		ThrottlingTime:  next.Throttling.Time - prev.Throttling.Time,
   126  	}
   127  
   128  	if systemDelta := next.System - prev.System; systemDelta > 0 {
   129  		if totalDelta := next.Usage.Total - prev.Usage.Total; totalDelta > 0 {
   130  			delta.Total = (float64(totalDelta) / float64(systemDelta)) * float64(next.OnlineCPUs)
   131  		}
   132  		if kernelDelta := next.Usage.Kernel - prev.Usage.Kernel; kernelDelta > 0 {
   133  			delta.System = (float64(kernelDelta) / float64(systemDelta)) * float64(next.OnlineCPUs)
   134  		}
   135  		if userDelta := next.Usage.User - prev.Usage.User; userDelta > 0 {
   136  			delta.User = (float64(userDelta) / float64(systemDelta)) * float64(next.OnlineCPUs)
   137  		}
   138  	}
   139  
   140  	return &delta
   141  }
   142  
   143  // DockerMemoryStatsUpdate represents the memory stats that have changed since the last measurement
   144  type DockerMemoryStatsUpdate struct {
   145  	ActiveAnon   *int `json:"active_anon,omitempty"`
   146  	ActiveFile   *int `json:"active_file,omitempty"`
   147  	InactiveAnon *int `json:"inactive_anon,omitempty"`
   148  	InactiveFile *int `json:"inactive_file,omitempty"`
   149  	TotalCache   *int `json:"total_cache,omitempty"`
   150  	TotalRss     *int `json:"total_rss,omitempty"`
   151  	Usage        *int `json:"usage,omitempty"`
   152  	MaxUsage     *int `json:"max_usage,omitempty"`
   153  	Limit        *int `json:"limit,omitempty"`
   154  }
   155  
   156  // NewDockerMemoryStatsUpdate returns the fields that have been updated since the last measurement.
   157  // It returns nil if nothing has changed.
   158  func NewDockerMemoryStatsUpdate(prev, next docker.ContainerMemoryStats) *DockerMemoryStatsUpdate {
   159  	if prev == next {
   160  		return nil
   161  	}
   162  
   163  	var delta DockerMemoryStatsUpdate
   164  	if prev.Usage != next.Usage {
   165  		delta.Usage = &next.Usage
   166  	}
   167  	if prev.MaxUsage != next.MaxUsage {
   168  		delta.MaxUsage = &next.MaxUsage
   169  	}
   170  	if prev.Limit != next.Limit {
   171  		delta.Limit = &next.Limit
   172  	}
   173  
   174  	if prev.Stats == next.Stats {
   175  		return &delta
   176  	}
   177  
   178  	if prev.Stats.ActiveAnon != next.Stats.ActiveAnon {
   179  		delta.ActiveAnon = &next.Stats.ActiveAnon
   180  	}
   181  	if prev.Stats.ActiveFile != next.Stats.ActiveFile {
   182  		delta.ActiveFile = &next.Stats.ActiveFile
   183  	}
   184  	if prev.Stats.InactiveAnon != next.Stats.InactiveAnon {
   185  		delta.InactiveAnon = &next.Stats.InactiveAnon
   186  	}
   187  	if prev.Stats.InactiveFile != next.Stats.InactiveFile {
   188  		delta.InactiveFile = &next.Stats.InactiveFile
   189  	}
   190  	if prev.Stats.TotalCache != next.Stats.TotalCache {
   191  		delta.TotalCache = &next.Stats.TotalCache
   192  	}
   193  	if prev.Stats.TotalRss != next.Stats.TotalRss {
   194  		delta.TotalRss = &next.Stats.TotalRss
   195  	}
   196  
   197  	return &delta
   198  }
   199  
   200  // DockerBlockIOStatsDelta represents the difference between two block I/O usage stats
   201  type DockerBlockIOStatsDelta struct {
   202  	Read  int `json:"blk_read,omitempty"`
   203  	Write int `json:"blk_write,omitempty"`
   204  }
   205  
   206  // IsZero returns true if both usage stats are equal
   207  func (d DockerBlockIOStatsDelta) IsZero() bool {
   208  	return d.Read == 0 && d.Write == 0
   209  }
   210  
   211  // NewDockerBlockIOStatsDelta sums up block I/O reads and writes and calculates the difference between two stat snapshots.
   212  // It returns nil if aggregated stats are equal.
   213  func NewDockerBlockIOStatsDelta(prev, next docker.ContainerBlockIOStats) *DockerBlockIOStatsDelta {
   214  	var delta DockerBlockIOStatsDelta
   215  
   216  	for _, stat := range next.ServiceBytes {
   217  		switch stat.Operation {
   218  		case docker.BlockIOReadOp:
   219  			delta.Read += stat.Value
   220  		case docker.BlockIOWriteOp:
   221  			delta.Write += stat.Value
   222  		}
   223  	}
   224  
   225  	for _, stat := range prev.ServiceBytes {
   226  		switch stat.Operation {
   227  		case docker.BlockIOReadOp:
   228  			delta.Read -= stat.Value
   229  		case docker.BlockIOWriteOp:
   230  			delta.Write -= stat.Value
   231  		}
   232  	}
   233  
   234  	if delta.IsZero() {
   235  		return nil
   236  	}
   237  
   238  	return &delta
   239  }