forked from LaconicNetwork/kompose
bump(github.com/docker/libcompose) v0.3.0
This commit is contained in:
+23
-2
@@ -46,7 +46,10 @@ func ResponseFormat(h http.Header) Format {
|
||||
return FmtUnknown
|
||||
}
|
||||
|
||||
const textType = "text/plain"
|
||||
const (
|
||||
textType = "text/plain"
|
||||
jsonType = "application/json"
|
||||
)
|
||||
|
||||
switch mediatype {
|
||||
case ProtoType:
|
||||
@@ -63,6 +66,22 @@ func ResponseFormat(h http.Header) Format {
|
||||
return FmtUnknown
|
||||
}
|
||||
return FmtText
|
||||
|
||||
case jsonType:
|
||||
var prometheusAPIVersion string
|
||||
|
||||
if params["schema"] == "prometheus/telemetry" && params["version"] != "" {
|
||||
prometheusAPIVersion = params["version"]
|
||||
} else {
|
||||
prometheusAPIVersion = h.Get("X-Prometheus-API-Version")
|
||||
}
|
||||
|
||||
switch prometheusAPIVersion {
|
||||
case "0.0.2", "":
|
||||
return fmtJSON2
|
||||
default:
|
||||
return FmtUnknown
|
||||
}
|
||||
}
|
||||
|
||||
return FmtUnknown
|
||||
@@ -74,6 +93,8 @@ func NewDecoder(r io.Reader, format Format) Decoder {
|
||||
switch format {
|
||||
case FmtProtoDelim:
|
||||
return &protoDecoder{r: r}
|
||||
case fmtJSON2:
|
||||
return newJSON2Decoder(r)
|
||||
}
|
||||
return &textDecoder{r: r}
|
||||
}
|
||||
@@ -111,7 +132,7 @@ func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// textDecoder implements the Decoder interface for the text protocol.
|
||||
// textDecoder implements the Decoder interface for the text protcol.
|
||||
type textDecoder struct {
|
||||
r io.Reader
|
||||
p TextParser
|
||||
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
// Copyright 2015 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package expfmt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
type json2Decoder struct {
|
||||
dec *json.Decoder
|
||||
fams []*dto.MetricFamily
|
||||
}
|
||||
|
||||
func newJSON2Decoder(r io.Reader) Decoder {
|
||||
return &json2Decoder{
|
||||
dec: json.NewDecoder(r),
|
||||
}
|
||||
}
|
||||
|
||||
type histogram002 struct {
|
||||
Labels model.LabelSet `json:"labels"`
|
||||
Values map[string]float64 `json:"value"`
|
||||
}
|
||||
|
||||
type counter002 struct {
|
||||
Labels model.LabelSet `json:"labels"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
func protoLabelSet(base, ext model.LabelSet) ([]*dto.LabelPair, error) {
|
||||
labels := base.Clone().Merge(ext)
|
||||
delete(labels, model.MetricNameLabel)
|
||||
|
||||
names := make([]string, 0, len(labels))
|
||||
for ln := range labels {
|
||||
names = append(names, string(ln))
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
pairs := make([]*dto.LabelPair, 0, len(labels))
|
||||
|
||||
for _, ln := range names {
|
||||
if !model.LabelNameRE.MatchString(ln) {
|
||||
return nil, fmt.Errorf("invalid label name %q", ln)
|
||||
}
|
||||
lv := labels[model.LabelName(ln)]
|
||||
|
||||
pairs = append(pairs, &dto.LabelPair{
|
||||
Name: proto.String(ln),
|
||||
Value: proto.String(string(lv)),
|
||||
})
|
||||
}
|
||||
|
||||
return pairs, nil
|
||||
}
|
||||
|
||||
func (d *json2Decoder) more() error {
|
||||
var entities []struct {
|
||||
BaseLabels model.LabelSet `json:"baseLabels"`
|
||||
Docstring string `json:"docstring"`
|
||||
Metric struct {
|
||||
Type string `json:"type"`
|
||||
Values json.RawMessage `json:"value"`
|
||||
} `json:"metric"`
|
||||
}
|
||||
|
||||
if err := d.dec.Decode(&entities); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, e := range entities {
|
||||
f := &dto.MetricFamily{
|
||||
Name: proto.String(string(e.BaseLabels[model.MetricNameLabel])),
|
||||
Help: proto.String(e.Docstring),
|
||||
Type: dto.MetricType_UNTYPED.Enum(),
|
||||
Metric: []*dto.Metric{},
|
||||
}
|
||||
|
||||
d.fams = append(d.fams, f)
|
||||
|
||||
switch e.Metric.Type {
|
||||
case "counter", "gauge":
|
||||
var values []counter002
|
||||
|
||||
if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
|
||||
return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
|
||||
}
|
||||
|
||||
for _, ctr := range values {
|
||||
labels, err := protoLabelSet(e.BaseLabels, ctr.Labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Metric = append(f.Metric, &dto.Metric{
|
||||
Label: labels,
|
||||
Untyped: &dto.Untyped{
|
||||
Value: proto.Float64(ctr.Value),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
case "histogram":
|
||||
var values []histogram002
|
||||
|
||||
if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
|
||||
return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
|
||||
}
|
||||
|
||||
for _, hist := range values {
|
||||
quants := make([]string, 0, len(values))
|
||||
for q := range hist.Values {
|
||||
quants = append(quants, q)
|
||||
}
|
||||
|
||||
sort.Strings(quants)
|
||||
|
||||
for _, q := range quants {
|
||||
value := hist.Values[q]
|
||||
// The correct label is "quantile" but to not break old expressions
|
||||
// this remains "percentile"
|
||||
hist.Labels["percentile"] = model.LabelValue(q)
|
||||
|
||||
labels, err := protoLabelSet(e.BaseLabels, hist.Labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.Metric = append(f.Metric, &dto.Metric{
|
||||
Label: labels,
|
||||
Untyped: &dto.Untyped{
|
||||
Value: proto.Float64(value),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown metric type %q", e.Metric.Type)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode implements the Decoder interface.
|
||||
func (d *json2Decoder) Decode(v *dto.MetricFamily) error {
|
||||
if len(d.fams) == 0 {
|
||||
if err := d.more(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*v = *d.fams[0]
|
||||
d.fams = d.fams[1:]
|
||||
|
||||
return nil
|
||||
}
|
||||
+14
-9
@@ -14,6 +14,7 @@
|
||||
package expfmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
@@ -284,17 +285,21 @@ func labelPairsToText(
|
||||
return written, nil
|
||||
}
|
||||
|
||||
var (
|
||||
escape = strings.NewReplacer("\\", `\\`, "\n", `\n`)
|
||||
escapeWithDoubleQuote = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
|
||||
)
|
||||
|
||||
// escapeString replaces '\' by '\\', new line character by '\n', and - if
|
||||
// includeDoubleQuote is true - '"' by '\"'.
|
||||
func escapeString(v string, includeDoubleQuote bool) string {
|
||||
if includeDoubleQuote {
|
||||
return escapeWithDoubleQuote.Replace(v)
|
||||
result := bytes.NewBuffer(make([]byte, 0, len(v)))
|
||||
for _, c := range v {
|
||||
switch {
|
||||
case c == '\\':
|
||||
result.WriteString(`\\`)
|
||||
case includeDoubleQuote && c == '"':
|
||||
result.WriteString(`\"`)
|
||||
case c == '\n':
|
||||
result.WriteString(`\n`)
|
||||
default:
|
||||
result.WriteRune(c)
|
||||
}
|
||||
}
|
||||
|
||||
return escape.Replace(v)
|
||||
return result.String()
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,5 +12,5 @@
|
||||
// limitations under the License.
|
||||
|
||||
// Package model contains common data structures that are shared across
|
||||
// Prometheus components and libraries.
|
||||
// Prometheus componenets and libraries.
|
||||
package model
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.3
|
||||
- 1.4
|
||||
- tip
|
||||
|
||||
+1
-10
@@ -8,13 +8,4 @@ Maintainers of this repository:
|
||||
The following individuals have contributed code to this repository
|
||||
(listed in alphabetical order):
|
||||
|
||||
* Armen Baghumian <abaghumian@noggin.com.au>
|
||||
* Bjoern Rabenstein <beorn@soundcloud.com>
|
||||
* David Cournapeau <cournape@gmail.com>
|
||||
* Ji-Hoon, Seol <jihoon.seol@gmail.com>
|
||||
* Jonas Große Sundrup <cherti@letopolis.de>
|
||||
* Julius Volz <julius.volz@gmail.com>
|
||||
* Matthias Rampke <mr@soundcloud.com>
|
||||
* Nicky Gerritsen <nicky@streamone.nl>
|
||||
* Rémi Audebert <contact@halfr.net>
|
||||
* Tobias Schmidt <tobidt@gmail.com>
|
||||
* Tobias Schmidt <ts@soundcloud.com>
|
||||
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
ci:
|
||||
! gofmt -l *.go | read nothing
|
||||
go vet
|
||||
go test -v ./...
|
||||
go get github.com/golang/lint/golint
|
||||
golint *.go
|
||||
-3
@@ -3,8 +3,5 @@
|
||||
This procfs package provides functions to retrieve system, kernel and process
|
||||
metrics from the pseudo-filesystem proc.
|
||||
|
||||
*WARNING*: This package is a work in progress. Its API may still break in
|
||||
backwards-incompatible ways without warnings. Use it at your own risk.
|
||||
|
||||
[](https://godoc.org/github.com/prometheus/procfs)
|
||||
[](https://travis-ci.org/prometheus/procfs)
|
||||
|
||||
+6
-3
@@ -27,7 +27,10 @@ func NewFS(mountPoint string) (FS, error) {
|
||||
return FS(mountPoint), nil
|
||||
}
|
||||
|
||||
// Path returns the path of the given subsystem relative to the procfs root.
|
||||
func (fs FS) Path(p ...string) string {
|
||||
return path.Join(append([]string{string(fs)}, p...)...)
|
||||
func (fs FS) stat(p string) (os.FileInfo, error) {
|
||||
return os.Stat(path.Join(string(fs), p))
|
||||
}
|
||||
|
||||
func (fs FS) open(p string) (*os.File, error) {
|
||||
return os.Open(path.Join(string(fs), p))
|
||||
}
|
||||
|
||||
-224
@@ -1,224 +0,0 @@
|
||||
package procfs
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IPVSStats holds IPVS statistics, as exposed by the kernel in `/proc/net/ip_vs_stats`.
|
||||
type IPVSStats struct {
|
||||
// Total count of connections.
|
||||
Connections uint64
|
||||
// Total incoming packages processed.
|
||||
IncomingPackets uint64
|
||||
// Total outgoing packages processed.
|
||||
OutgoingPackets uint64
|
||||
// Total incoming traffic.
|
||||
IncomingBytes uint64
|
||||
// Total outgoing traffic.
|
||||
OutgoingBytes uint64
|
||||
}
|
||||
|
||||
// IPVSBackendStatus holds current metrics of one virtual / real address pair.
|
||||
type IPVSBackendStatus struct {
|
||||
// The local (virtual) IP address.
|
||||
LocalAddress net.IP
|
||||
// The local (virtual) port.
|
||||
LocalPort uint16
|
||||
// The transport protocol (TCP, UDP).
|
||||
Proto string
|
||||
// The remote (real) IP address.
|
||||
RemoteAddress net.IP
|
||||
// The remote (real) port.
|
||||
RemotePort uint16
|
||||
// The current number of active connections for this virtual/real address pair.
|
||||
ActiveConn uint64
|
||||
// The current number of inactive connections for this virtual/real address pair.
|
||||
InactConn uint64
|
||||
// The current weight of this virtual/real address pair.
|
||||
Weight uint64
|
||||
}
|
||||
|
||||
// NewIPVSStats reads the IPVS statistics.
|
||||
func NewIPVSStats() (IPVSStats, error) {
|
||||
fs, err := NewFS(DefaultMountPoint)
|
||||
if err != nil {
|
||||
return IPVSStats{}, err
|
||||
}
|
||||
|
||||
return fs.NewIPVSStats()
|
||||
}
|
||||
|
||||
// NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem.
|
||||
func (fs FS) NewIPVSStats() (IPVSStats, error) {
|
||||
file, err := os.Open(fs.Path("net/ip_vs_stats"))
|
||||
if err != nil {
|
||||
return IPVSStats{}, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return parseIPVSStats(file)
|
||||
}
|
||||
|
||||
// parseIPVSStats performs the actual parsing of `ip_vs_stats`.
|
||||
func parseIPVSStats(file io.Reader) (IPVSStats, error) {
|
||||
var (
|
||||
statContent []byte
|
||||
statLines []string
|
||||
statFields []string
|
||||
stats IPVSStats
|
||||
)
|
||||
|
||||
statContent, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return IPVSStats{}, err
|
||||
}
|
||||
|
||||
statLines = strings.SplitN(string(statContent), "\n", 4)
|
||||
if len(statLines) != 4 {
|
||||
return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short")
|
||||
}
|
||||
|
||||
statFields = strings.Fields(statLines[2])
|
||||
if len(statFields) != 5 {
|
||||
return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields")
|
||||
}
|
||||
|
||||
stats.Connections, err = strconv.ParseUint(statFields[0], 16, 64)
|
||||
if err != nil {
|
||||
return IPVSStats{}, err
|
||||
}
|
||||
stats.IncomingPackets, err = strconv.ParseUint(statFields[1], 16, 64)
|
||||
if err != nil {
|
||||
return IPVSStats{}, err
|
||||
}
|
||||
stats.OutgoingPackets, err = strconv.ParseUint(statFields[2], 16, 64)
|
||||
if err != nil {
|
||||
return IPVSStats{}, err
|
||||
}
|
||||
stats.IncomingBytes, err = strconv.ParseUint(statFields[3], 16, 64)
|
||||
if err != nil {
|
||||
return IPVSStats{}, err
|
||||
}
|
||||
stats.OutgoingBytes, err = strconv.ParseUint(statFields[4], 16, 64)
|
||||
if err != nil {
|
||||
return IPVSStats{}, err
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs.
|
||||
func NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
|
||||
fs, err := NewFS(DefaultMountPoint)
|
||||
if err != nil {
|
||||
return []IPVSBackendStatus{}, err
|
||||
}
|
||||
|
||||
return fs.NewIPVSBackendStatus()
|
||||
}
|
||||
|
||||
// NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
|
||||
func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
|
||||
file, err := os.Open(fs.Path("net/ip_vs"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return parseIPVSBackendStatus(file)
|
||||
}
|
||||
|
||||
func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
|
||||
var (
|
||||
status []IPVSBackendStatus
|
||||
scanner = bufio.NewScanner(file)
|
||||
proto string
|
||||
localAddress net.IP
|
||||
localPort uint16
|
||||
err error
|
||||
)
|
||||
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(string(scanner.Text()))
|
||||
if len(fields) == 0 {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case fields[0] == "IP" || fields[0] == "Prot" || fields[1] == "RemoteAddress:Port":
|
||||
continue
|
||||
case fields[0] == "TCP" || fields[0] == "UDP":
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
proto = fields[0]
|
||||
localAddress, localPort, err = parseIPPort(fields[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case fields[0] == "->":
|
||||
if len(fields) < 6 {
|
||||
continue
|
||||
}
|
||||
remoteAddress, remotePort, err := parseIPPort(fields[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
weight, err := strconv.ParseUint(fields[3], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
activeConn, err := strconv.ParseUint(fields[4], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inactConn, err := strconv.ParseUint(fields[5], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status = append(status, IPVSBackendStatus{
|
||||
LocalAddress: localAddress,
|
||||
LocalPort: localPort,
|
||||
RemoteAddress: remoteAddress,
|
||||
RemotePort: remotePort,
|
||||
Proto: proto,
|
||||
Weight: weight,
|
||||
ActiveConn: activeConn,
|
||||
InactConn: inactConn,
|
||||
})
|
||||
}
|
||||
}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func parseIPPort(s string) (net.IP, uint16, error) {
|
||||
tmp := strings.SplitN(s, ":", 2)
|
||||
|
||||
if len(tmp) != 2 {
|
||||
return nil, 0, fmt.Errorf("invalid IP:Port: %s", s)
|
||||
}
|
||||
|
||||
if len(tmp[0]) != 8 && len(tmp[0]) != 32 {
|
||||
return nil, 0, fmt.Errorf("invalid IP: %s", tmp[0])
|
||||
}
|
||||
|
||||
ip, err := hex.DecodeString(tmp[0])
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
port, err := strconv.ParseUint(tmp[1], 16, 16)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return ip, uint16(port), nil
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
package procfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
statuslineRE = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[[U_]+\]`)
|
||||
buildlineRE = regexp.MustCompile(`\((\d+)/\d+\)`)
|
||||
)
|
||||
|
||||
// MDStat holds info parsed from /proc/mdstat.
|
||||
type MDStat struct {
|
||||
// Name of the device.
|
||||
Name string
|
||||
// activity-state of the device.
|
||||
ActivityState string
|
||||
// Number of active disks.
|
||||
DisksActive int64
|
||||
// Total number of disks the device consists of.
|
||||
DisksTotal int64
|
||||
// Number of blocks the device holds.
|
||||
BlocksTotal int64
|
||||
// Number of blocks on the device that are in sync.
|
||||
BlocksSynced int64
|
||||
}
|
||||
|
||||
// ParseMDStat parses an mdstat-file and returns a struct with the relevant infos.
|
||||
func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
|
||||
mdStatusFilePath := fs.Path("mdstat")
|
||||
content, err := ioutil.ReadFile(mdStatusFilePath)
|
||||
if err != nil {
|
||||
return []MDStat{}, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
|
||||
}
|
||||
|
||||
mdStates := []MDStat{}
|
||||
lines := strings.Split(string(content), "\n")
|
||||
for i, l := range lines {
|
||||
if l == "" {
|
||||
continue
|
||||
}
|
||||
if l[0] == ' ' {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(l, "Personalities") || strings.HasPrefix(l, "unused") {
|
||||
continue
|
||||
}
|
||||
|
||||
mainLine := strings.Split(l, " ")
|
||||
if len(mainLine) < 3 {
|
||||
return mdStates, fmt.Errorf("error parsing mdline: %s", l)
|
||||
}
|
||||
mdName := mainLine[0]
|
||||
activityState := mainLine[2]
|
||||
|
||||
if len(lines) <= i+3 {
|
||||
return mdStates, fmt.Errorf(
|
||||
"error parsing %s: too few lines for md device %s",
|
||||
mdStatusFilePath,
|
||||
mdName,
|
||||
)
|
||||
}
|
||||
|
||||
active, total, size, err := evalStatusline(lines[i+1])
|
||||
if err != nil {
|
||||
return mdStates, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
|
||||
}
|
||||
|
||||
// j is the line number of the syncing-line.
|
||||
j := i + 2
|
||||
if strings.Contains(lines[i+2], "bitmap") { // skip bitmap line
|
||||
j = i + 3
|
||||
}
|
||||
|
||||
// If device is syncing at the moment, get the number of currently
|
||||
// synced bytes, otherwise that number equals the size of the device.
|
||||
syncedBlocks := size
|
||||
if strings.Contains(lines[j], "recovery") || strings.Contains(lines[j], "resync") {
|
||||
syncedBlocks, err = evalBuildline(lines[j])
|
||||
if err != nil {
|
||||
return mdStates, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
|
||||
}
|
||||
}
|
||||
|
||||
mdStates = append(mdStates, MDStat{
|
||||
Name: mdName,
|
||||
ActivityState: activityState,
|
||||
DisksActive: active,
|
||||
DisksTotal: total,
|
||||
BlocksTotal: size,
|
||||
BlocksSynced: syncedBlocks,
|
||||
})
|
||||
}
|
||||
|
||||
return mdStates, nil
|
||||
}
|
||||
|
||||
func evalStatusline(statusline string) (active, total, size int64, err error) {
|
||||
matches := statuslineRE.FindStringSubmatch(statusline)
|
||||
if len(matches) != 4 {
|
||||
return 0, 0, 0, fmt.Errorf("unexpected statusline: %s", statusline)
|
||||
}
|
||||
|
||||
size, err = strconv.ParseInt(matches[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
|
||||
}
|
||||
|
||||
total, err = strconv.ParseInt(matches[2], 10, 64)
|
||||
if err != nil {
|
||||
return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
|
||||
}
|
||||
|
||||
active, err = strconv.ParseInt(matches[3], 10, 64)
|
||||
if err != nil {
|
||||
return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
|
||||
}
|
||||
|
||||
return active, total, size, nil
|
||||
}
|
||||
|
||||
func evalBuildline(buildline string) (syncedBlocks int64, err error) {
|
||||
matches := buildlineRE.FindStringSubmatch(buildline)
|
||||
if len(matches) != 2 {
|
||||
return 0, fmt.Errorf("unexpected buildline: %s", buildline)
|
||||
}
|
||||
|
||||
syncedBlocks, err = strconv.ParseInt(matches[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%s in buildline: %s", err, buildline)
|
||||
}
|
||||
|
||||
return syncedBlocks, nil
|
||||
}
|
||||
+14
-77
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -23,13 +24,9 @@ func (p Procs) Len() int { return len(p) }
|
||||
func (p Procs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID }
|
||||
|
||||
// Self returns a process for the current process read via /proc/self.
|
||||
// Self returns a process for the current process.
|
||||
func Self() (Proc, error) {
|
||||
fs, err := NewFS(DefaultMountPoint)
|
||||
if err != nil {
|
||||
return Proc{}, err
|
||||
}
|
||||
return fs.Self()
|
||||
return NewProc(os.Getpid())
|
||||
}
|
||||
|
||||
// NewProc returns a process for the given pid under /proc.
|
||||
@@ -38,42 +35,32 @@ func NewProc(pid int) (Proc, error) {
|
||||
if err != nil {
|
||||
return Proc{}, err
|
||||
}
|
||||
|
||||
return fs.NewProc(pid)
|
||||
}
|
||||
|
||||
// AllProcs returns a list of all currently available processes under /proc.
|
||||
// AllProcs returns a list of all currently avaible processes under /proc.
|
||||
func AllProcs() (Procs, error) {
|
||||
fs, err := NewFS(DefaultMountPoint)
|
||||
if err != nil {
|
||||
return Procs{}, err
|
||||
}
|
||||
return fs.AllProcs()
|
||||
}
|
||||
|
||||
// Self returns a process for the current process.
|
||||
func (fs FS) Self() (Proc, error) {
|
||||
p, err := os.Readlink(fs.Path("self"))
|
||||
if err != nil {
|
||||
return Proc{}, err
|
||||
}
|
||||
pid, err := strconv.Atoi(strings.Replace(p, string(fs), "", -1))
|
||||
if err != nil {
|
||||
return Proc{}, err
|
||||
}
|
||||
return fs.NewProc(pid)
|
||||
return fs.AllProcs()
|
||||
}
|
||||
|
||||
// NewProc returns a process for the given pid.
|
||||
func (fs FS) NewProc(pid int) (Proc, error) {
|
||||
if _, err := os.Stat(fs.Path(strconv.Itoa(pid))); err != nil {
|
||||
if _, err := fs.stat(strconv.Itoa(pid)); err != nil {
|
||||
return Proc{}, err
|
||||
}
|
||||
|
||||
return Proc{PID: pid, fs: fs}, nil
|
||||
}
|
||||
|
||||
// AllProcs returns a list of all currently available processes.
|
||||
// AllProcs returns a list of all currently avaible processes.
|
||||
func (fs FS) AllProcs() (Procs, error) {
|
||||
d, err := os.Open(fs.Path())
|
||||
d, err := fs.open("")
|
||||
if err != nil {
|
||||
return Procs{}, err
|
||||
}
|
||||
@@ -98,7 +85,7 @@ func (fs FS) AllProcs() (Procs, error) {
|
||||
|
||||
// CmdLine returns the command line of a process.
|
||||
func (p Proc) CmdLine() ([]string, error) {
|
||||
f, err := os.Open(p.path("cmdline"))
|
||||
f, err := p.open("cmdline")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -109,39 +96,9 @@ func (p Proc) CmdLine() ([]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) < 1 {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
return strings.Split(string(data[:len(data)-1]), string(byte(0))), nil
|
||||
}
|
||||
|
||||
// Comm returns the command name of a process.
|
||||
func (p Proc) Comm() (string, error) {
|
||||
f, err := os.Open(p.path("comm"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(data)), nil
|
||||
}
|
||||
|
||||
// Executable returns the absolute path of the executable command of a process.
|
||||
func (p Proc) Executable() (string, error) {
|
||||
exe, err := os.Readlink(p.path("exe"))
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return exe, err
|
||||
}
|
||||
|
||||
// FileDescriptors returns the currently open file descriptors of a process.
|
||||
func (p Proc) FileDescriptors() ([]uintptr, error) {
|
||||
names, err := p.fileDescriptors()
|
||||
@@ -161,26 +118,6 @@ func (p Proc) FileDescriptors() ([]uintptr, error) {
|
||||
return fds, nil
|
||||
}
|
||||
|
||||
// FileDescriptorTargets returns the targets of all file descriptors of a process.
|
||||
// If a file descriptor is not a symlink to a file (like a socket), that value will be the empty string.
|
||||
func (p Proc) FileDescriptorTargets() ([]string, error) {
|
||||
names, err := p.fileDescriptors()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
targets := make([]string, len(names))
|
||||
|
||||
for i, name := range names {
|
||||
target, err := os.Readlink(p.path("fd", name))
|
||||
if err == nil {
|
||||
targets[i] = target
|
||||
}
|
||||
}
|
||||
|
||||
return targets, nil
|
||||
}
|
||||
|
||||
// FileDescriptorsLen returns the number of currently open file descriptors of
|
||||
// a process.
|
||||
func (p Proc) FileDescriptorsLen() (int, error) {
|
||||
@@ -193,7 +130,7 @@ func (p Proc) FileDescriptorsLen() (int, error) {
|
||||
}
|
||||
|
||||
func (p Proc) fileDescriptors() ([]string, error) {
|
||||
d, err := os.Open(p.path("fd"))
|
||||
d, err := p.open("fd")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -207,6 +144,6 @@ func (p Proc) fileDescriptors() ([]string, error) {
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (p Proc) path(pa ...string) string {
|
||||
return p.fs.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...)
|
||||
func (p Proc) open(pa string) (*os.File, error) {
|
||||
return p.fs.open(path.Join(strconv.Itoa(p.PID), pa))
|
||||
}
|
||||
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
package procfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ProcIO models the content of /proc/<pid>/io.
|
||||
type ProcIO struct {
|
||||
// Chars read.
|
||||
RChar uint64
|
||||
// Chars written.
|
||||
WChar uint64
|
||||
// Read syscalls.
|
||||
SyscR uint64
|
||||
// Write syscalls.
|
||||
SyscW uint64
|
||||
// Bytes read.
|
||||
ReadBytes uint64
|
||||
// Bytes written.
|
||||
WriteBytes uint64
|
||||
// Bytes written, but taking into account truncation. See
|
||||
// Documentation/filesystems/proc.txt in the kernel sources for
|
||||
// detailed explanation.
|
||||
CancelledWriteBytes int64
|
||||
}
|
||||
|
||||
// NewIO creates a new ProcIO instance from a given Proc instance.
|
||||
func (p Proc) NewIO() (ProcIO, error) {
|
||||
pio := ProcIO{}
|
||||
|
||||
f, err := os.Open(p.path("io"))
|
||||
if err != nil {
|
||||
return pio, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return pio, err
|
||||
}
|
||||
|
||||
ioFormat := "rchar: %d\nwchar: %d\nsyscr: %d\nsyscw: %d\n" +
|
||||
"read_bytes: %d\nwrite_bytes: %d\n" +
|
||||
"cancelled_write_bytes: %d\n"
|
||||
|
||||
_, err = fmt.Sscanf(string(data), ioFormat, &pio.RChar, &pio.WChar, &pio.SyscR,
|
||||
&pio.SyscW, &pio.ReadBytes, &pio.WriteBytes, &pio.CancelledWriteBytes)
|
||||
if err != nil {
|
||||
return pio, err
|
||||
}
|
||||
|
||||
return pio, nil
|
||||
}
|
||||
+19
-45
@@ -3,56 +3,29 @@ package procfs
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ProcLimits represents the soft limits for each of the process's resource
|
||||
// limits. For more information see getrlimit(2):
|
||||
// http://man7.org/linux/man-pages/man2/getrlimit.2.html.
|
||||
// limits.
|
||||
type ProcLimits struct {
|
||||
// CPU time limit in seconds.
|
||||
CPUTime int
|
||||
// Maximum size of files that the process may create.
|
||||
FileSize int
|
||||
// Maximum size of the process's data segment (initialized data,
|
||||
// uninitialized data, and heap).
|
||||
DataSize int
|
||||
// Maximum size of the process stack in bytes.
|
||||
StackSize int
|
||||
// Maximum size of a core file.
|
||||
CoreFileSize int
|
||||
// Limit of the process's resident set in pages.
|
||||
ResidentSet int
|
||||
// Maximum number of processes that can be created for the real user ID of
|
||||
// the calling process.
|
||||
Processes int
|
||||
// Value one greater than the maximum file descriptor number that can be
|
||||
// opened by this process.
|
||||
OpenFiles int
|
||||
// Maximum number of bytes of memory that may be locked into RAM.
|
||||
LockedMemory int
|
||||
// Maximum size of the process's virtual memory address space in bytes.
|
||||
AddressSpace int
|
||||
// Limit on the combined number of flock(2) locks and fcntl(2) leases that
|
||||
// this process may establish.
|
||||
FileLocks int
|
||||
// Limit of signals that may be queued for the real user ID of the calling
|
||||
// process.
|
||||
PendingSignals int
|
||||
// Limit on the number of bytes that can be allocated for POSIX message
|
||||
// queues for the real user ID of the calling process.
|
||||
MsqqueueSize int
|
||||
// Limit of the nice priority set using setpriority(2) or nice(2).
|
||||
NicePriority int
|
||||
// Limit of the real-time priority set using sched_setscheduler(2) or
|
||||
// sched_setparam(2).
|
||||
CPUTime int
|
||||
FileSize int
|
||||
DataSize int
|
||||
StackSize int
|
||||
CoreFileSize int
|
||||
ResidentSet int
|
||||
Processes int
|
||||
OpenFiles int
|
||||
LockedMemory int
|
||||
AddressSpace int
|
||||
FileLocks int
|
||||
PendingSignals int
|
||||
MsqqueueSize int
|
||||
NicePriority int
|
||||
RealtimePriority int
|
||||
// Limit (in microseconds) on the amount of CPU time that a process
|
||||
// scheduled under a real-time scheduling policy may consume without making
|
||||
// a blocking system call.
|
||||
RealtimeTimeout int
|
||||
RealtimeTimeout int
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -66,7 +39,7 @@ var (
|
||||
|
||||
// NewLimits returns the current soft limits of the process.
|
||||
func (p Proc) NewLimits() (ProcLimits, error) {
|
||||
f, err := os.Open(p.path("limits"))
|
||||
f, err := p.open("limits")
|
||||
if err != nil {
|
||||
return ProcLimits{}, err
|
||||
}
|
||||
@@ -87,7 +60,7 @@ func (p Proc) NewLimits() (ProcLimits, error) {
|
||||
case "Max cpu time":
|
||||
l.CPUTime, err = parseInt(fields[1])
|
||||
case "Max file size":
|
||||
l.FileSize, err = parseInt(fields[1])
|
||||
l.FileLocks, err = parseInt(fields[1])
|
||||
case "Max data size":
|
||||
l.DataSize, err = parseInt(fields[1])
|
||||
case "Max stack size":
|
||||
@@ -117,6 +90,7 @@ func (p Proc) NewLimits() (ProcLimits, error) {
|
||||
case "Max realtime timeout":
|
||||
l.RealtimeTimeout, err = parseInt(fields[1])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return ProcLimits{}, err
|
||||
}
|
||||
|
||||
+9
-9
@@ -7,15 +7,15 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Originally, this USER_HZ value was dynamically retrieved via a sysconf call
|
||||
// which required cgo. However, that caused a lot of problems regarding
|
||||
// Originally, this USER_HZ value was dynamically retrieved via a sysconf call which
|
||||
// required cgo. However, that caused a lot of problems regarding
|
||||
// cross-compilation. Alternatives such as running a binary to determine the
|
||||
// value, or trying to derive it in some other way were all problematic. After
|
||||
// much research it was determined that USER_HZ is actually hardcoded to 100 on
|
||||
// all Go-supported platforms as of the time of this writing. This is why we
|
||||
// decided to hardcode it here as well. It is not impossible that there could
|
||||
// be systems with exceptions, but they should be very exotic edge cases, and
|
||||
// in that case, the worst outcome will be two misreported metrics.
|
||||
// value, or trying to derive it in some other way were all problematic.
|
||||
// After much research it was determined that USER_HZ is actually hardcoded to
|
||||
// 100 on all Go-supported platforms as of the time of this writing. This is
|
||||
// why we decided to hardcode it here as well. It is not impossible that there
|
||||
// could be systems with exceptions, but they should be very exotic edge cases,
|
||||
// and in that case, the worst outcome will be two misreported metrics.
|
||||
//
|
||||
// See also the following discussions:
|
||||
//
|
||||
@@ -91,7 +91,7 @@ type ProcStat struct {
|
||||
|
||||
// NewStat returns the current status information of the process.
|
||||
func (p Proc) NewStat() (ProcStat, error) {
|
||||
f, err := os.Open(p.path("stat"))
|
||||
f, err := p.open("stat")
|
||||
if err != nil {
|
||||
return ProcStat{}, err
|
||||
}
|
||||
|
||||
+1
-2
@@ -3,7 +3,6 @@ package procfs
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -26,7 +25,7 @@ func NewStat() (Stat, error) {
|
||||
|
||||
// NewStat returns an information about current kernel/system statistics.
|
||||
func (fs FS) NewStat() (Stat, error) {
|
||||
f, err := os.Open(fs.Path("stat"))
|
||||
f, err := fs.open("stat")
|
||||
if err != nil {
|
||||
return Stat{}, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user