Unverified Commit 37ef6eeb authored by k8s-ci-robot's avatar k8s-ci-robot Committed by GitHub

Merge pull request #68431 from dashpole/cadvisor_godep_update

Update cAdvisor godeps to v0.31.0
parents a720b18e 788196e4
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -104,18 +104,23 @@ func containerLabels(c *cadvisorapi.ContainerInfo) map[string]string { ...@@ -104,18 +104,23 @@ func containerLabels(c *cadvisorapi.ContainerInfo) map[string]string {
func New(imageFsInfoProvider ImageFsInfoProvider, rootPath string, usingLegacyStats bool) (Interface, error) { func New(imageFsInfoProvider ImageFsInfoProvider, rootPath string, usingLegacyStats bool) (Interface, error) {
sysFs := sysfs.NewRealSysFs() sysFs := sysfs.NewRealSysFs()
ignoreMetrics := cadvisormetrics.MetricSet{ includedMetrics := cadvisormetrics.MetricSet{
cadvisormetrics.NetworkTcpUsageMetrics: struct{}{}, cadvisormetrics.CpuUsageMetrics: struct{}{},
cadvisormetrics.NetworkUdpUsageMetrics: struct{}{}, cadvisormetrics.MemoryUsageMetrics: struct{}{},
cadvisormetrics.PerCpuUsageMetrics: struct{}{}, cadvisormetrics.CpuLoadMetrics: struct{}{},
cadvisormetrics.ProcessSchedulerMetrics: struct{}{}, cadvisormetrics.DiskIOMetrics: struct{}{},
cadvisormetrics.NetworkUsageMetrics: struct{}{},
cadvisormetrics.AcceleratorUsageMetrics: struct{}{},
cadvisormetrics.AppMetrics: struct{}{},
} }
if !usingLegacyStats { if usingLegacyStats {
ignoreMetrics[cadvisormetrics.DiskUsageMetrics] = struct{}{} includedMetrics[cadvisormetrics.DiskUsageMetrics] = struct{}{}
} }
// collect metrics for all cgroups
rawContainerCgroupPathPrefixWhiteList := []string{"/"}
// Create and start the cAdvisor container manager. // Create and start the cAdvisor container manager.
m, err := manager.New(memory.New(statsCacheDuration, nil), sysFs, maxHousekeepingInterval, allowDynamicHousekeeping, ignoreMetrics, http.DefaultClient) m, err := manager.New(memory.New(statsCacheDuration, nil), sysFs, maxHousekeepingInterval, allowDynamicHousekeeping, includedMetrics, http.DefaultClient, rawContainerCgroupPathPrefixWhiteList)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -43,6 +43,7 @@ go_library( ...@@ -43,6 +43,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/util/logs:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/logs:go_default_library",
"//vendor/github.com/emicklei/go-restful:go_default_library", "//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/google/cadvisor/container:go_default_library",
"//vendor/github.com/google/cadvisor/info/v1:go_default_library", "//vendor/github.com/google/cadvisor/info/v1:go_default_library",
"//vendor/github.com/google/cadvisor/metrics:go_default_library", "//vendor/github.com/google/cadvisor/metrics:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library", "//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
......
...@@ -33,6 +33,7 @@ import ( ...@@ -33,6 +33,7 @@ import (
restful "github.com/emicklei/go-restful" restful "github.com/emicklei/go-restful"
"github.com/golang/glog" "github.com/golang/glog"
cadvisormetrics "github.com/google/cadvisor/container"
cadvisorapi "github.com/google/cadvisor/info/v1" cadvisorapi "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/metrics" "github.com/google/cadvisor/metrics"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
...@@ -276,7 +277,18 @@ func (s *Server) InstallDefaultHandlers() { ...@@ -276,7 +277,18 @@ func (s *Server) InstallDefaultHandlers() {
// cAdvisor metrics are exposed under the secured handler as well // cAdvisor metrics are exposed under the secured handler as well
r := prometheus.NewRegistry() r := prometheus.NewRegistry()
r.MustRegister(metrics.NewPrometheusCollector(prometheusHostAdapter{s.host}, containerPrometheusLabelsFunc(s.host)))
includedMetrics := cadvisormetrics.MetricSet{
cadvisormetrics.CpuUsageMetrics: struct{}{},
cadvisormetrics.MemoryUsageMetrics: struct{}{},
cadvisormetrics.CpuLoadMetrics: struct{}{},
cadvisormetrics.DiskIOMetrics: struct{}{},
cadvisormetrics.DiskUsageMetrics: struct{}{},
cadvisormetrics.NetworkUsageMetrics: struct{}{},
cadvisormetrics.AcceleratorUsageMetrics: struct{}{},
cadvisormetrics.AppMetrics: struct{}{},
}
r.MustRegister(metrics.NewPrometheusCollector(prometheusHostAdapter{s.host}, containerPrometheusLabelsFunc(s.host), includedMetrics))
s.restfulCont.Handle(cadvisorMetricsPath, s.restfulCont.Handle(cadvisorMetricsPath,
promhttp.HandlerFor(r, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError}), promhttp.HandlerFor(r, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError}),
) )
......
...@@ -33,6 +33,7 @@ filegroup( ...@@ -33,6 +33,7 @@ filegroup(
"//vendor/github.com/Nvveen/Gotty:all-srcs", "//vendor/github.com/Nvveen/Gotty:all-srcs",
"//vendor/github.com/PuerkitoBio/purell:all-srcs", "//vendor/github.com/PuerkitoBio/purell:all-srcs",
"//vendor/github.com/PuerkitoBio/urlesc:all-srcs", "//vendor/github.com/PuerkitoBio/urlesc:all-srcs",
"//vendor/github.com/Rican7/retry:all-srcs",
"//vendor/github.com/armon/circbuf:all-srcs", "//vendor/github.com/armon/circbuf:all-srcs",
"//vendor/github.com/asaskevich/govalidator:all-srcs", "//vendor/github.com/asaskevich/govalidator:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/aws:all-srcs", "//vendor/github.com/aws/aws-sdk-go/aws:all-srcs",
...@@ -302,6 +303,7 @@ filegroup( ...@@ -302,6 +303,7 @@ filegroup(
"//vendor/github.com/mattn/go-shellwords:all-srcs", "//vendor/github.com/mattn/go-shellwords:all-srcs",
"//vendor/github.com/mattn/go-sqlite3:all-srcs", "//vendor/github.com/mattn/go-sqlite3:all-srcs",
"//vendor/github.com/matttproud/golang_protobuf_extensions/pbutil:all-srcs", "//vendor/github.com/matttproud/golang_protobuf_extensions/pbutil:all-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib:all-srcs",
"//vendor/github.com/mholt/caddy/caddyfile:all-srcs", "//vendor/github.com/mholt/caddy/caddyfile:all-srcs",
"//vendor/github.com/miekg/dns:all-srcs", "//vendor/github.com/miekg/dns:all-srcs",
"//vendor/github.com/mindprince/gonvml:all-srcs", "//vendor/github.com/mindprince/gonvml:all-srcs",
...@@ -328,6 +330,7 @@ filegroup( ...@@ -328,6 +330,7 @@ filegroup(
"//vendor/github.com/pkg/sftp:all-srcs", "//vendor/github.com/pkg/sftp:all-srcs",
"//vendor/github.com/pmezard/go-difflib/difflib:all-srcs", "//vendor/github.com/pmezard/go-difflib/difflib:all-srcs",
"//vendor/github.com/pquerna/cachecontrol:all-srcs", "//vendor/github.com/pquerna/cachecontrol:all-srcs",
"//vendor/github.com/pquerna/ffjson/fflib/v1:all-srcs",
"//vendor/github.com/prometheus/client_golang/prometheus:all-srcs", "//vendor/github.com/prometheus/client_golang/prometheus:all-srcs",
"//vendor/github.com/prometheus/client_model/go:all-srcs", "//vendor/github.com/prometheus/client_model/go:all-srcs",
"//vendor/github.com/prometheus/common/expfmt:all-srcs", "//vendor/github.com/prometheus/common/expfmt:all-srcs",
......
language: go
go:
- 1.6
- tip
sudo: false
before_install:
# Install tools necessary to report code-coverage to Coveralls.io
- go get github.com/mattn/goveralls
# Export some environment variables
- export GO_TEST_COVERAGE_FILE_NAME='coverage.out'
install:
# Get all imported packages
- make install-deps install-deps-dev
# Basic build errors
- make build
script:
# Lint
- make format-lint
- make import-lint
- make copyright-lint
# Run tests
- make test-with-coverage-profile
after_success:
# Report our code-coverage to Coveralls.io
- goveralls -service=travis-ci -coverprofile="${GO_TEST_COVERAGE_FILE_NAME}"
matrix:
allow_failures:
- go: tip
fast_finish: true
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["retry.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/Rican7/retry",
importpath = "github.com/Rican7/retry",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/Rican7/retry/strategy:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/Rican7/retry/backoff:all-srcs",
"//vendor/github.com/Rican7/retry/jitter:all-srcs",
"//vendor/github.com/Rican7/retry/strategy:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
Copyright (C) 2016 Trevor N. Suarez (Rican7)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Define some VCS context
PARENT_BRANCH ?= master
# Set the mode for code-coverage
GO_TEST_COVERAGE_MODE ?= count
GO_TEST_COVERAGE_FILE_NAME ?= coverage.out
# Set flags for `gofmt`
GOFMT_FLAGS ?= -s
# Set a default `min_confidence` value for `golint`
GOLINT_MIN_CONFIDENCE ?= 0.3
all: install-deps build install
clean:
go clean -i -x ./...
build:
go build -v ./...
install:
go install ./...
install-deps:
go get -d -t ./...
install-deps-dev: install-deps
go get github.com/golang/lint/golint
go get golang.org/x/tools/cmd/goimports
update-deps:
go get -d -t -u ./...
update-deps-dev: update-deps
go get -u github.com/golang/lint/golint
go get -u golang.org/x/tools/cmd/goimports
test:
go test -v ./...
test-with-coverage:
go test -cover ./...
test-with-coverage-formatted:
go test -cover ./... | column -t | sort -r
test-with-coverage-profile:
echo "mode: ${GO_TEST_COVERAGE_MODE}" > ${GO_TEST_COVERAGE_FILE_NAME}
for package in $$(go list ./...); do \
go test -covermode ${GO_TEST_COVERAGE_MODE} -coverprofile "coverage_$${package##*/}.out" "$${package}"; \
sed '1d' "coverage_$${package##*/}.out" >> ${GO_TEST_COVERAGE_FILE_NAME}; \
done
format-lint:
errors=$$(gofmt -l ${GOFMT_FLAGS} .); if [ "$${errors}" != "" ]; then echo "$${errors}"; exit 1; fi
import-lint:
errors=$$(goimports -l .); if [ "$${errors}" != "" ]; then echo "$${errors}"; exit 1; fi
style-lint:
errors=$$(golint -min_confidence=${GOLINT_MIN_CONFIDENCE} ./...); if [ "$${errors}" != "" ]; then echo "$${errors}"; exit 1; fi
copyright-lint:
@old_dates=$$(git diff --diff-filter=ACMRTUXB --name-only "${PARENT_BRANCH}" | xargs grep -E '[Cc]opyright(\s+)[©Cc]?(\s+)[0-9]{4}' | grep -E -v "[Cc]opyright(\s+)[©Cc]?(\s+)$$(date '+%Y')"); if [ "$${old_dates}" != "" ]; then printf "The following files contain outdated copyrights:\n$${old_dates}\n\nThis can be fixed with 'make copyright-fix'\n"; exit 1; fi
lint: install-deps-dev format-lint import-lint style-lint copyright-lint
format-fix:
gofmt -w ${GOFMT_FLAGS} .
import-fix:
goimports -w .
copyright-fix:
@git diff --diff-filter=ACMRTUXB --name-only "${PARENT_BRANCH}" | xargs -I '_FILENAME' -- sh -c 'sed -i.bak "s/\([Cc]opyright\([[:space:]][©Cc]\{0,1\}[[:space:]]*\)\)[0-9]\{4\}/\1"$$(date '+%Y')"/g" _FILENAME && rm _FILENAME.bak'
vet:
go vet ./...
.PHONY: all clean build install install-deps install-deps-dev update-deps update-deps-dev test test-with-coverage test-with-coverage-formatted test-with-coverage-profile format-lint import-lint style-lint copyright-lint lint format-fix import-fix copyright-fix vet
# retry
[![Build Status](https://travis-ci.org/Rican7/retry.svg?branch=master)](https://travis-ci.org/Rican7/retry)
[![Coverage Status](https://coveralls.io/repos/github/Rican7/retry/badge.svg)](https://coveralls.io/github/Rican7/retry)
[![Go Report Card](https://goreportcard.com/badge/Rican7/retry)](http://goreportcard.com/report/Rican7/retry)
[![GoDoc](https://godoc.org/github.com/Rican7/retry?status.png)](https://godoc.org/github.com/Rican7/retry)
[![Latest Stable Version](https://img.shields.io/github/release/Rican7/retry.svg?style=flat)](https://github.com/Rican7/retry/releases)
A simple, stateless, functional mechanism to perform actions repetitively until successful.
## Project Status
This project is currently in "pre-release". While the code is heavily tested, the API may change.
Vendor (commit or lock) this dependency if you plan on using it.
## Install
`go get github.com/Rican7/retry`
## Examples
### Basic
```go
retry.Retry(func(attempt uint) error {
return nil // Do something that may or may not cause an error
})
```
### File Open
```go
const logFilePath = "/var/log/myapp.log"
var logFile *os.File
err := retry.Retry(func(attempt uint) error {
var err error
logFile, err = os.Open(logFilePath)
return err
})
if nil != err {
log.Fatalf("Unable to open file %q with error %q", logFilePath, err)
}
logFile.Chdir() // Do something with the file
```
### HTTP request with strategies and backoff
```go
var response *http.Response
action := func(attempt uint) error {
var err error
response, err = http.Get("https://api.github.com/repos/Rican7/retry")
if nil == err && nil != response && response.StatusCode > 200 {
err = fmt.Errorf("failed to fetch (attempt #%d) with status code: %d", attempt, response.StatusCode)
}
return err
}
err := retry.Retry(
action,
strategy.Limit(5),
strategy.Backoff(backoff.Fibonacci(10*time.Millisecond)),
)
if nil != err {
log.Fatalf("Failed to fetch repository with error %q", err)
}
```
### Retry with backoff jitter
```go
action := func(attempt uint) error {
return errors.New("something happened")
}
seed := time.Now().UnixNano()
random := rand.New(rand.NewSource(seed))
retry.Retry(
action,
strategy.Limit(5),
strategy.BackoffWithJitter(
backoff.BinaryExponential(10*time.Millisecond),
jitter.Deviation(random, 0.5),
),
)
```
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["backoff.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/Rican7/retry/backoff",
importpath = "github.com/Rican7/retry/backoff",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package backoff provides stateless methods of calculating durations based on
// a number of attempts made.
//
// Copyright © 2016 Trevor N. Suarez (Rican7)
package backoff
import (
"math"
"time"
)
// Algorithm defines a function that calculates a time.Duration based on
// the given retry attempt number.
type Algorithm func(attempt uint) time.Duration
// Incremental creates a Algorithm that increments the initial duration
// by the given increment for each attempt.
func Incremental(initial, increment time.Duration) Algorithm {
return func(attempt uint) time.Duration {
return initial + (increment * time.Duration(attempt))
}
}
// Linear creates a Algorithm that linearly multiplies the factor
// duration by the attempt number for each attempt.
func Linear(factor time.Duration) Algorithm {
return func(attempt uint) time.Duration {
return (factor * time.Duration(attempt))
}
}
// Exponential creates a Algorithm that multiplies the factor duration by
// an exponentially increasing factor for each attempt, where the factor is
// calculated as the given base raised to the attempt number.
func Exponential(factor time.Duration, base float64) Algorithm {
return func(attempt uint) time.Duration {
return (factor * time.Duration(math.Pow(base, float64(attempt))))
}
}
// BinaryExponential creates a Algorithm that multiplies the factor
// duration by an exponentially increasing factor for each attempt, where the
// factor is calculated as `2` raised to the attempt number (2^attempt).
func BinaryExponential(factor time.Duration) Algorithm {
return Exponential(factor, 2)
}
// Fibonacci creates a Algorithm that multiplies the factor duration by
// an increasing factor for each attempt, where the factor is the Nth number in
// the Fibonacci sequence.
func Fibonacci(factor time.Duration) Algorithm {
return func(attempt uint) time.Duration {
return (factor * time.Duration(fibonacciNumber(attempt)))
}
}
// fibonacciNumber calculates the Fibonacci sequence number for the given
// sequence position.
func fibonacciNumber(n uint) uint {
if 0 == n {
return 0
} else if 1 == n {
return 1
} else {
return fibonacciNumber(n-1) + fibonacciNumber(n-2)
}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["jitter.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/Rican7/retry/jitter",
importpath = "github.com/Rican7/retry/jitter",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package jitter provides methods of transforming durations.
//
// Copyright © 2016 Trevor N. Suarez (Rican7)
package jitter
import (
"math"
"math/rand"
"time"
)
// Transformation defines a function that calculates a time.Duration based on
// the given duration.
type Transformation func(duration time.Duration) time.Duration
// Full creates a Transformation that transforms a duration into a result
// duration in [0, n) randomly, where n is the given duration.
//
// The given generator is what is used to determine the random transformation.
// If a nil generator is passed, a default one will be provided.
//
// Inspired by https://www.awsarchitectureblog.com/2015/03/backoff.html
func Full(generator *rand.Rand) Transformation {
random := fallbackNewRandom(generator)
return func(duration time.Duration) time.Duration {
return time.Duration(random.Int63n(int64(duration)))
}
}
// Equal creates a Transformation that transforms a duration into a result
// duration in [n/2, n) randomly, where n is the given duration.
//
// The given generator is what is used to determine the random transformation.
// If a nil generator is passed, a default one will be provided.
//
// Inspired by https://www.awsarchitectureblog.com/2015/03/backoff.html
func Equal(generator *rand.Rand) Transformation {
random := fallbackNewRandom(generator)
return func(duration time.Duration) time.Duration {
return (duration / 2) + time.Duration(random.Int63n(int64(duration))/2)
}
}
// Deviation creates a Transformation that transforms a duration into a result
// duration that deviates from the input randomly by a given factor.
//
// The given generator is what is used to determine the random transformation.
// If a nil generator is passed, a default one will be provided.
//
// Inspired by https://developers.google.com/api-client-library/java/google-http-java-client/backoff
func Deviation(generator *rand.Rand, factor float64) Transformation {
random := fallbackNewRandom(generator)
return func(duration time.Duration) time.Duration {
min := int64(math.Floor(float64(duration) * (1 - factor)))
max := int64(math.Ceil(float64(duration) * (1 + factor)))
return time.Duration(random.Int63n(max-min) + min)
}
}
// NormalDistribution creates a Transformation that transforms a duration into a
// result duration based on a normal distribution of the input and the given
// standard deviation.
//
// The given generator is what is used to determine the random transformation.
// If a nil generator is passed, a default one will be provided.
func NormalDistribution(generator *rand.Rand, standardDeviation float64) Transformation {
random := fallbackNewRandom(generator)
return func(duration time.Duration) time.Duration {
return time.Duration(random.NormFloat64()*standardDeviation + float64(duration))
}
}
// fallbackNewRandom returns the passed in random instance if it's not nil,
// and otherwise returns a new random instance seeded with the current time.
func fallbackNewRandom(random *rand.Rand) *rand.Rand {
// Return the passed in value if it's already not null
if nil != random {
return random
}
seed := time.Now().UnixNano()
return rand.New(rand.NewSource(seed))
}
// Package retry provides a simple, stateless, functional mechanism to perform
// actions repetitively until successful.
//
// Copyright © 2016 Trevor N. Suarez (Rican7)
package retry
import "github.com/Rican7/retry/strategy"
// Action defines a callable function that package retry can handle.
type Action func(attempt uint) error
// Retry takes an action and performs it, repetitively, until successful.
//
// Optionally, strategies may be passed that assess whether or not an attempt
// should be made.
func Retry(action Action, strategies ...strategy.Strategy) error {
var err error
for attempt := uint(0); (0 == attempt || nil != err) && shouldAttempt(attempt, strategies...); attempt++ {
err = action(attempt)
}
return err
}
// shouldAttempt evaluates the provided strategies with the given attempt to
// determine if the Retry loop should make another attempt.
func shouldAttempt(attempt uint, strategies ...strategy.Strategy) bool {
shouldAttempt := true
for i := 0; shouldAttempt && i < len(strategies); i++ {
shouldAttempt = shouldAttempt && strategies[i](attempt)
}
return shouldAttempt
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["strategy.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/Rican7/retry/strategy",
importpath = "github.com/Rican7/retry/strategy",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/Rican7/retry/backoff:go_default_library",
"//vendor/github.com/Rican7/retry/jitter:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package strategy provides a way to change the way that retry is performed.
//
// Copyright © 2016 Trevor N. Suarez (Rican7)
package strategy
import (
"time"
"github.com/Rican7/retry/backoff"
"github.com/Rican7/retry/jitter"
)
// Strategy defines a function that Retry calls before every successive attempt
// to determine whether it should make the next attempt or not. Returning `true`
// allows for the next attempt to be made. Returning `false` halts the retrying
// process and returns the last error returned by the called Action.
//
// The strategy will be passed an "attempt" number on each successive retry
// iteration, starting with a `0` value before the first attempt is actually
// made. This allows for a pre-action delay, etc.
type Strategy func(attempt uint) bool
// Limit creates a Strategy that limits the number of attempts that Retry will
// make.
func Limit(attemptLimit uint) Strategy {
return func(attempt uint) bool {
return (attempt <= attemptLimit)
}
}
// Delay creates a Strategy that waits the given duration before the first
// attempt is made.
func Delay(duration time.Duration) Strategy {
return func(attempt uint) bool {
if 0 == attempt {
time.Sleep(duration)
}
return true
}
}
// Wait creates a Strategy that waits the given durations for each attempt after
// the first. If the number of attempts is greater than the number of durations
// provided, then the strategy uses the last duration provided.
func Wait(durations ...time.Duration) Strategy {
return func(attempt uint) bool {
if 0 < attempt && 0 < len(durations) {
durationIndex := int(attempt - 1)
if len(durations) <= durationIndex {
durationIndex = len(durations) - 1
}
time.Sleep(durations[durationIndex])
}
return true
}
}
// Backoff creates a Strategy that waits before each attempt, with a duration as
// defined by the given backoff.Algorithm.
func Backoff(algorithm backoff.Algorithm) Strategy {
return BackoffWithJitter(algorithm, noJitter())
}
// BackoffWithJitter creates a Strategy that waits before each attempt, with a
// duration as defined by the given backoff.Algorithm and jitter.Transformation.
func BackoffWithJitter(algorithm backoff.Algorithm, transformation jitter.Transformation) Strategy {
return func(attempt uint) bool {
if 0 < attempt {
time.Sleep(transformation(algorithm(attempt)))
}
return true
}
}
// noJitter creates a jitter.Transformation that simply returns the input.
func noJitter() jitter.Transformation {
return func(duration time.Duration) time.Duration {
return duration
}
}
...@@ -31,7 +31,10 @@ import ( ...@@ -31,7 +31,10 @@ import (
) )
type NvidiaManager struct { type NvidiaManager struct {
sync.RWMutex sync.Mutex
// true if there are NVIDIA devices present on the node
devicesPresent bool
// true if the NVML library (libnvidia-ml.so.1) was loaded successfully // true if the NVML library (libnvidia-ml.so.1) was loaded successfully
nvmlInitialized bool nvmlInitialized bool
...@@ -51,20 +54,9 @@ func (nm *NvidiaManager) Setup() { ...@@ -51,20 +54,9 @@ func (nm *NvidiaManager) Setup() {
return return
} }
nm.initializeNVML() nm.devicesPresent = true
if nm.nvmlInitialized {
return initializeNVML(nm)
}
go func() {
glog.V(2).Info("Starting goroutine to initialize NVML")
// TODO: use globalHousekeepingInterval
for range time.Tick(time.Minute) {
nm.initializeNVML()
if nm.nvmlInitialized {
return
}
}
}()
} }
// detectDevices returns true if a device with given pci id is present on the node. // detectDevices returns true if a device with given pci id is present on the node.
...@@ -91,20 +83,18 @@ func detectDevices(vendorId string) bool { ...@@ -91,20 +83,18 @@ func detectDevices(vendorId string) bool {
} }
// initializeNVML initializes the NVML library and sets up the nvmlDevices map. // initializeNVML initializes the NVML library and sets up the nvmlDevices map.
func (nm *NvidiaManager) initializeNVML() { // This is defined as a variable to help in testing.
var initializeNVML = func(nm *NvidiaManager) {
if err := gonvml.Initialize(); err != nil { if err := gonvml.Initialize(); err != nil {
// This is under a logging level because otherwise we may cause // This is under a logging level because otherwise we may cause
// log spam if the drivers/nvml is not installed on the system. // log spam if the drivers/nvml is not installed on the system.
glog.V(4).Infof("Could not initialize NVML: %v", err) glog.V(4).Infof("Could not initialize NVML: %v", err)
return return
} }
nm.nvmlInitialized = true
numDevices, err := gonvml.DeviceCount() numDevices, err := gonvml.DeviceCount()
if err != nil { if err != nil {
glog.Warningf("GPU metrics would not be available. Failed to get the number of nvidia devices: %v", err) glog.Warningf("GPU metrics would not be available. Failed to get the number of nvidia devices: %v", err)
nm.Lock()
// Even though we won't have GPU metrics, the library was initialized and should be shutdown when exiting.
nm.nvmlInitialized = true
nm.Unlock()
return return
} }
glog.V(1).Infof("NVML initialized. Number of nvidia devices: %v", numDevices) glog.V(1).Infof("NVML initialized. Number of nvidia devices: %v", numDevices)
...@@ -122,10 +112,6 @@ func (nm *NvidiaManager) initializeNVML() { ...@@ -122,10 +112,6 @@ func (nm *NvidiaManager) initializeNVML() {
} }
nm.nvidiaDevices[int(minorNumber)] = device nm.nvidiaDevices[int(minorNumber)] = device
} }
nm.Lock()
// Doing this at the end to avoid race in accessing nvidiaDevices in GetCollector.
nm.nvmlInitialized = true
nm.Unlock()
} }
// Destroy shuts down NVML. // Destroy shuts down NVML.
...@@ -139,12 +125,21 @@ func (nm *NvidiaManager) Destroy() { ...@@ -139,12 +125,21 @@ func (nm *NvidiaManager) Destroy() {
// present in the devices.list file in the given devicesCgroupPath. // present in the devices.list file in the given devicesCgroupPath.
func (nm *NvidiaManager) GetCollector(devicesCgroupPath string) (AcceleratorCollector, error) { func (nm *NvidiaManager) GetCollector(devicesCgroupPath string) (AcceleratorCollector, error) {
nc := &NvidiaCollector{} nc := &NvidiaCollector{}
nm.RLock()
if !nm.devicesPresent {
return nc, nil
}
// Makes sure that we don't call initializeNVML() concurrently and
// that we only call initializeNVML() when it's not initialized.
nm.Lock()
if !nm.nvmlInitialized {
initializeNVML(nm)
}
if !nm.nvmlInitialized || len(nm.nvidiaDevices) == 0 { if !nm.nvmlInitialized || len(nm.nvidiaDevices) == 0 {
nm.RUnlock() nm.Unlock()
return nc, nil return nc, nil
} }
nm.RUnlock() nm.Unlock()
nvidiaMinorNumbers, err := parseDevicesCgroup(devicesCgroupPath) nvidiaMinorNumbers, err := parseDevicesCgroup(devicesCgroupPath)
if err != nil { if err != nil {
return nc, err return nc, err
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
package memory package memory
import ( import (
"fmt" "errors"
"sync" "sync"
"time" "time"
...@@ -26,6 +26,9 @@ import ( ...@@ -26,6 +26,9 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
) )
// ErrDataNotFound is the error resulting if failed to find a container in memory cache.
var ErrDataNotFound = errors.New("unable to find data in memory cache")
// TODO(vmarmol): See about refactoring this class, we have an unecessary redirection of containerCache and InMemoryCache. // TODO(vmarmol): See about refactoring this class, we have an unecessary redirection of containerCache and InMemoryCache.
// containerCache is used to store per-container information // containerCache is used to store per-container information
type containerCache struct { type containerCache struct {
...@@ -101,7 +104,7 @@ func (self *InMemoryCache) RecentStats(name string, start, end time.Time, maxSta ...@@ -101,7 +104,7 @@ func (self *InMemoryCache) RecentStats(name string, start, end time.Time, maxSta
self.lock.RLock() self.lock.RLock()
defer self.lock.RUnlock() defer self.lock.RUnlock()
if cstore, ok = self.containerCacheMap[name]; !ok { if cstore, ok = self.containerCacheMap[name]; !ok {
return fmt.Errorf("unable to find data for container %v", name) return ErrDataNotFound
} }
return nil return nil
}() }()
......
...@@ -32,6 +32,7 @@ filegroup( ...@@ -32,6 +32,7 @@ filegroup(
"//vendor/github.com/google/cadvisor/container/crio:all-srcs", "//vendor/github.com/google/cadvisor/container/crio:all-srcs",
"//vendor/github.com/google/cadvisor/container/docker:all-srcs", "//vendor/github.com/google/cadvisor/container/docker:all-srcs",
"//vendor/github.com/google/cadvisor/container/libcontainer:all-srcs", "//vendor/github.com/google/cadvisor/container/libcontainer:all-srcs",
"//vendor/github.com/google/cadvisor/container/mesos:all-srcs",
"//vendor/github.com/google/cadvisor/container/raw:all-srcs", "//vendor/github.com/google/cadvisor/container/raw:all-srcs",
"//vendor/github.com/google/cadvisor/container/rkt:all-srcs", "//vendor/github.com/google/cadvisor/container/rkt:all-srcs",
"//vendor/github.com/google/cadvisor/container/systemd:all-srcs", "//vendor/github.com/google/cadvisor/container/systemd:all-srcs",
......
...@@ -36,6 +36,7 @@ const ( ...@@ -36,6 +36,7 @@ const (
ContainerTypeSystemd ContainerTypeSystemd
ContainerTypeCrio ContainerTypeCrio
ContainerTypeContainerd ContainerTypeContainerd
ContainerTypeMesos
) )
// Interface for container operation handlers. // Interface for container operation handlers.
......
...@@ -47,8 +47,8 @@ type containerdFactory struct { ...@@ -47,8 +47,8 @@ type containerdFactory struct {
// Information about the mounted cgroup subsystems. // Information about the mounted cgroup subsystems.
cgroupSubsystems libcontainer.CgroupSubsystems cgroupSubsystems libcontainer.CgroupSubsystems
// Information about mounted filesystems. // Information about mounted filesystems.
fsInfo fs.FsInfo fsInfo fs.FsInfo
ignoreMetrics container.MetricSet includedMetrics container.MetricSet
} }
func (self *containerdFactory) String() string { func (self *containerdFactory) String() string {
...@@ -70,7 +70,7 @@ func (self *containerdFactory) NewContainerHandler(name string, inHostNamespace ...@@ -70,7 +70,7 @@ func (self *containerdFactory) NewContainerHandler(name string, inHostNamespace
&self.cgroupSubsystems, &self.cgroupSubsystems,
inHostNamespace, inHostNamespace,
metadataEnvs, metadataEnvs,
self.ignoreMetrics, self.includedMetrics,
) )
} }
...@@ -117,7 +117,7 @@ func (self *containerdFactory) DebugInfo() map[string][]string { ...@@ -117,7 +117,7 @@ func (self *containerdFactory) DebugInfo() map[string][]string {
} }
// Register root container before running this function! // Register root container before running this function!
func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error { func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) error {
client, err := Client() client, err := Client()
if err != nil { if err != nil {
return fmt.Errorf("unable to create containerd client: %v", err) return fmt.Errorf("unable to create containerd client: %v", err)
...@@ -140,7 +140,7 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c ...@@ -140,7 +140,7 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c
fsInfo: fsInfo, fsInfo: fsInfo,
machineInfoFactory: factory, machineInfoFactory: factory,
version: containerdVersion, version: containerdVersion,
ignoreMetrics: ignoreMetrics, includedMetrics: includedMetrics,
} }
container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw}) container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw})
......
...@@ -48,7 +48,7 @@ type containerdContainerHandler struct { ...@@ -48,7 +48,7 @@ type containerdContainerHandler struct {
// Image name used for this container. // Image name used for this container.
image string image string
// Filesystem handler. // Filesystem handler.
ignoreMetrics container.MetricSet includedMetrics container.MetricSet
libcontainerHandler *containerlibcontainer.Handler libcontainerHandler *containerlibcontainer.Handler
} }
...@@ -64,7 +64,7 @@ func newContainerdContainerHandler( ...@@ -64,7 +64,7 @@ func newContainerdContainerHandler(
cgroupSubsystems *containerlibcontainer.CgroupSubsystems, cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
inHostNamespace bool, inHostNamespace bool,
metadataEnvs []string, metadataEnvs []string,
ignoreMetrics container.MetricSet, includedMetrics container.MetricSet,
) (container.ContainerHandler, error) { ) (container.ContainerHandler, error) {
// Create the cgroup paths. // Create the cgroup paths.
cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints)) cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints))
...@@ -127,7 +127,7 @@ func newContainerdContainerHandler( ...@@ -127,7 +127,7 @@ func newContainerdContainerHandler(
Aliases: []string{id, name}, Aliases: []string{id, name},
} }
libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), ignoreMetrics) libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), includedMetrics)
handler := &containerdContainerHandler{ handler := &containerdContainerHandler{
machineInfoFactory: machineInfoFactory, machineInfoFactory: machineInfoFactory,
...@@ -135,7 +135,7 @@ func newContainerdContainerHandler( ...@@ -135,7 +135,7 @@ func newContainerdContainerHandler(
fsInfo: fsInfo, fsInfo: fsInfo,
envs: make(map[string]string), envs: make(map[string]string),
labels: cntr.Labels, labels: cntr.Labels,
ignoreMetrics: ignoreMetrics, includedMetrics: includedMetrics,
reference: containerReference, reference: containerReference,
libcontainerHandler: libcontainerHandler, libcontainerHandler: libcontainerHandler,
} }
...@@ -159,9 +159,9 @@ func (self *containerdContainerHandler) ContainerReference() (info.ContainerRefe ...@@ -159,9 +159,9 @@ func (self *containerdContainerHandler) ContainerReference() (info.ContainerRefe
func (self *containerdContainerHandler) needNet() bool { func (self *containerdContainerHandler) needNet() bool {
// Since containerd does not handle networking ideally we need to return based // Since containerd does not handle networking ideally we need to return based
// on ignoreMetrics list. Here the assumption is the presence of cri-containerd // on includedMetrics list. Here the assumption is the presence of cri-containerd
// label // label
if !self.ignoreMetrics.Has(container.NetworkUsageMetrics) { if self.includedMetrics.Has(container.NetworkUsageMetrics) {
//TODO change it to exported cri-containerd constants //TODO change it to exported cri-containerd constants
return self.labels["io.cri-containerd.kind"] == "sandbox" return self.labels["io.cri-containerd.kind"] == "sandbox"
} }
...@@ -186,7 +186,7 @@ func (self *containerdContainerHandler) getFsStats(stats *info.ContainerStats) e ...@@ -186,7 +186,7 @@ func (self *containerdContainerHandler) getFsStats(stats *info.ContainerStats) e
return err return err
} }
if !self.ignoreMetrics.Has(container.DiskIOMetrics) { if self.includedMetrics.Has(container.DiskIOMetrics) {
common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo) common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo)
} }
return nil return nil
......
...@@ -55,7 +55,7 @@ type crioFactory struct { ...@@ -55,7 +55,7 @@ type crioFactory struct {
// Information about mounted filesystems. // Information about mounted filesystems.
fsInfo fs.FsInfo fsInfo fs.FsInfo
ignoreMetrics container.MetricSet includedMetrics container.MetricSet
client crioClient client crioClient
} }
...@@ -81,7 +81,7 @@ func (self *crioFactory) NewContainerHandler(name string, inHostNamespace bool) ...@@ -81,7 +81,7 @@ func (self *crioFactory) NewContainerHandler(name string, inHostNamespace bool)
&self.cgroupSubsystems, &self.cgroupSubsystems,
inHostNamespace, inHostNamespace,
metadataEnvs, metadataEnvs,
self.ignoreMetrics, self.includedMetrics,
) )
return return
} }
...@@ -136,7 +136,7 @@ var ( ...@@ -136,7 +136,7 @@ var (
) )
// Register root container before running this function! // Register root container before running this function!
func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error { func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) error {
client, err := Client() client, err := Client()
if err != nil { if err != nil {
return err return err
...@@ -162,7 +162,7 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c ...@@ -162,7 +162,7 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c
machineInfoFactory: factory, machineInfoFactory: factory,
storageDriver: storageDriver(info.StorageDriver), storageDriver: storageDriver(info.StorageDriver),
storageDir: info.StorageRoot, storageDir: info.StorageRoot,
ignoreMetrics: ignoreMetrics, includedMetrics: includedMetrics,
} }
container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw}) container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw})
......
...@@ -63,7 +63,7 @@ type crioContainerHandler struct { ...@@ -63,7 +63,7 @@ type crioContainerHandler struct {
// The IP address of the container // The IP address of the container
ipAddress string ipAddress string
ignoreMetrics container.MetricSet includedMetrics container.MetricSet
reference info.ContainerReference reference info.ContainerReference
...@@ -83,7 +83,7 @@ func newCrioContainerHandler( ...@@ -83,7 +83,7 @@ func newCrioContainerHandler(
cgroupSubsystems *containerlibcontainer.CgroupSubsystems, cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
inHostNamespace bool, inHostNamespace bool,
metadataEnvs []string, metadataEnvs []string,
ignoreMetrics container.MetricSet, includedMetrics container.MetricSet,
) (container.ContainerHandler, error) { ) (container.ContainerHandler, error) {
// Create the cgroup paths. // Create the cgroup paths.
cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints)) cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints))
...@@ -141,7 +141,7 @@ func newCrioContainerHandler( ...@@ -141,7 +141,7 @@ func newCrioContainerHandler(
Namespace: CrioNamespace, Namespace: CrioNamespace,
} }
libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootFs, cInfo.Pid, ignoreMetrics) libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootFs, cInfo.Pid, includedMetrics)
// TODO: extract object mother method // TODO: extract object mother method
handler := &crioContainerHandler{ handler := &crioContainerHandler{
...@@ -152,7 +152,7 @@ func newCrioContainerHandler( ...@@ -152,7 +152,7 @@ func newCrioContainerHandler(
rootfsStorageDir: rootfsStorageDir, rootfsStorageDir: rootfsStorageDir,
envs: make(map[string]string), envs: make(map[string]string),
labels: cInfo.Labels, labels: cInfo.Labels,
ignoreMetrics: ignoreMetrics, includedMetrics: includedMetrics,
reference: containerReference, reference: containerReference,
libcontainerHandler: libcontainerHandler, libcontainerHandler: libcontainerHandler,
} }
...@@ -171,7 +171,7 @@ func newCrioContainerHandler( ...@@ -171,7 +171,7 @@ func newCrioContainerHandler(
handler.ipAddress = cInfo.IP handler.ipAddress = cInfo.IP
// we optionally collect disk usage metrics // we optionally collect disk usage metrics
if !ignoreMetrics.Has(container.DiskUsageMetrics) { if includedMetrics.Has(container.DiskUsageMetrics) {
handler.fsHandler = common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, storageLogDir, fsInfo) handler.fsHandler = common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, storageLogDir, fsInfo)
} }
// TODO for env vars we wanted to show from container.Config.Env from whitelist // TODO for env vars we wanted to show from container.Config.Env from whitelist
...@@ -199,14 +199,14 @@ func (self *crioContainerHandler) ContainerReference() (info.ContainerReference, ...@@ -199,14 +199,14 @@ func (self *crioContainerHandler) ContainerReference() (info.ContainerReference,
} }
func (self *crioContainerHandler) needNet() bool { func (self *crioContainerHandler) needNet() bool {
if !self.ignoreMetrics.Has(container.NetworkUsageMetrics) { if self.includedMetrics.Has(container.NetworkUsageMetrics) {
return self.labels["io.kubernetes.container.name"] == "POD" return self.labels["io.kubernetes.container.name"] == "POD"
} }
return false return false
} }
func (self *crioContainerHandler) GetSpec() (info.ContainerSpec, error) { func (self *crioContainerHandler) GetSpec() (info.ContainerSpec, error) {
hasFilesystem := !self.ignoreMetrics.Has(container.DiskUsageMetrics) hasFilesystem := self.includedMetrics.Has(container.DiskUsageMetrics)
spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, self.needNet(), hasFilesystem) spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, self.needNet(), hasFilesystem)
spec.Labels = self.labels spec.Labels = self.labels
...@@ -222,11 +222,11 @@ func (self *crioContainerHandler) getFsStats(stats *info.ContainerStats) error { ...@@ -222,11 +222,11 @@ func (self *crioContainerHandler) getFsStats(stats *info.ContainerStats) error {
return err return err
} }
if !self.ignoreMetrics.Has(container.DiskIOMetrics) { if self.includedMetrics.Has(container.DiskIOMetrics) {
common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo) common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo)
} }
if self.ignoreMetrics.Has(container.DiskUsageMetrics) { if !self.includedMetrics.Has(container.DiskUsageMetrics) {
return nil return nil
} }
var device string var device string
......
...@@ -110,7 +110,7 @@ type dockerFactory struct { ...@@ -110,7 +110,7 @@ type dockerFactory struct {
dockerAPIVersion []int dockerAPIVersion []int
ignoreMetrics container.MetricSet includedMetrics container.MetricSet
thinPoolName string thinPoolName string
thinPoolWatcher *devicemapper.ThinPoolWatcher thinPoolWatcher *devicemapper.ThinPoolWatcher
...@@ -141,7 +141,7 @@ func (self *dockerFactory) NewContainerHandler(name string, inHostNamespace bool ...@@ -141,7 +141,7 @@ func (self *dockerFactory) NewContainerHandler(name string, inHostNamespace bool
inHostNamespace, inHostNamespace,
metadataEnvs, metadataEnvs,
self.dockerVersion, self.dockerVersion,
self.ignoreMetrics, self.includedMetrics,
self.thinPoolName, self.thinPoolName,
self.thinPoolWatcher, self.thinPoolWatcher,
self.zfsWatcher, self.zfsWatcher,
...@@ -309,7 +309,7 @@ func ensureThinLsKernelVersion(kernelVersion string) error { ...@@ -309,7 +309,7 @@ func ensureThinLsKernelVersion(kernelVersion string) error {
} }
// Register root container before running this function! // Register root container before running this function!
func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error { func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) error {
client, err := Client() client, err := Client()
if err != nil { if err != nil {
return fmt.Errorf("unable to communicate with docker daemon: %v", err) return fmt.Errorf("unable to communicate with docker daemon: %v", err)
...@@ -363,7 +363,7 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c ...@@ -363,7 +363,7 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c
machineInfoFactory: factory, machineInfoFactory: factory,
storageDriver: storageDriver(dockerInfo.Driver), storageDriver: storageDriver(dockerInfo.Driver),
storageDir: RootDir(), storageDir: RootDir(),
ignoreMetrics: ignoreMetrics, includedMetrics: includedMetrics,
thinPoolName: thinPoolName, thinPoolName: thinPoolName,
thinPoolWatcher: thinPoolWatcher, thinPoolWatcher: thinPoolWatcher,
zfsWatcher: zfsWatcher, zfsWatcher: zfsWatcher,
......
...@@ -83,7 +83,7 @@ type dockerContainerHandler struct { ...@@ -83,7 +83,7 @@ type dockerContainerHandler struct {
// The IP address of the container // The IP address of the container
ipAddress string ipAddress string
ignoreMetrics container.MetricSet includedMetrics container.MetricSet
// the devicemapper poolname // the devicemapper poolname
poolName string poolName string
...@@ -128,7 +128,7 @@ func newDockerContainerHandler( ...@@ -128,7 +128,7 @@ func newDockerContainerHandler(
inHostNamespace bool, inHostNamespace bool,
metadataEnvs []string, metadataEnvs []string,
dockerVersion []int, dockerVersion []int,
ignoreMetrics container.MetricSet, includedMetrics container.MetricSet,
thinPoolName string, thinPoolName string,
thinPoolWatcher *devicemapper.ThinPoolWatcher, thinPoolWatcher *devicemapper.ThinPoolWatcher,
zfsWatcher *zfs.ZfsWatcher, zfsWatcher *zfs.ZfsWatcher,
...@@ -203,7 +203,7 @@ func newDockerContainerHandler( ...@@ -203,7 +203,7 @@ func newDockerContainerHandler(
rootfsStorageDir: rootfsStorageDir, rootfsStorageDir: rootfsStorageDir,
envs: make(map[string]string), envs: make(map[string]string),
labels: ctnr.Config.Labels, labels: ctnr.Config.Labels,
ignoreMetrics: ignoreMetrics, includedMetrics: includedMetrics,
zfsParent: zfsParent, zfsParent: zfsParent,
} }
// Timestamp returned by Docker is in time.RFC3339Nano format. // Timestamp returned by Docker is in time.RFC3339Nano format.
...@@ -212,7 +212,7 @@ func newDockerContainerHandler( ...@@ -212,7 +212,7 @@ func newDockerContainerHandler(
// This should not happen, report the error just in case // This should not happen, report the error just in case
return nil, fmt.Errorf("failed to parse the create timestamp %q for container %q: %v", ctnr.Created, id, err) return nil, fmt.Errorf("failed to parse the create timestamp %q for container %q: %v", ctnr.Created, id, err)
} }
handler.libcontainerHandler = containerlibcontainer.NewHandler(cgroupManager, rootFs, ctnr.State.Pid, ignoreMetrics) handler.libcontainerHandler = containerlibcontainer.NewHandler(cgroupManager, rootFs, ctnr.State.Pid, includedMetrics)
// Add the name and bare ID as aliases of the container. // Add the name and bare ID as aliases of the container.
handler.reference = info.ContainerReference{ handler.reference = info.ContainerReference{
...@@ -244,7 +244,7 @@ func newDockerContainerHandler( ...@@ -244,7 +244,7 @@ func newDockerContainerHandler(
handler.ipAddress = ipAddress handler.ipAddress = ipAddress
if !ignoreMetrics.Has(container.DiskUsageMetrics) { if includedMetrics.Has(container.DiskUsageMetrics) {
handler.fsHandler = &dockerFsHandler{ handler.fsHandler = &dockerFsHandler{
fsHandler: common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, otherStorageDir, fsInfo), fsHandler: common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, otherStorageDir, fsInfo),
thinPoolWatcher: thinPoolWatcher, thinPoolWatcher: thinPoolWatcher,
...@@ -345,14 +345,14 @@ func (self *dockerContainerHandler) ContainerReference() (info.ContainerReferenc ...@@ -345,14 +345,14 @@ func (self *dockerContainerHandler) ContainerReference() (info.ContainerReferenc
} }
func (self *dockerContainerHandler) needNet() bool { func (self *dockerContainerHandler) needNet() bool {
if !self.ignoreMetrics.Has(container.NetworkUsageMetrics) { if self.includedMetrics.Has(container.NetworkUsageMetrics) {
return !self.networkMode.IsContainer() return !self.networkMode.IsContainer()
} }
return false return false
} }
func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) { func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) {
hasFilesystem := !self.ignoreMetrics.Has(container.DiskUsageMetrics) hasFilesystem := self.includedMetrics.Has(container.DiskUsageMetrics)
spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, self.needNet(), hasFilesystem) spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, self.needNet(), hasFilesystem)
spec.Labels = self.labels spec.Labels = self.labels
...@@ -369,11 +369,11 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error ...@@ -369,11 +369,11 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
return err return err
} }
if !self.ignoreMetrics.Has(container.DiskIOMetrics) { if self.includedMetrics.Has(container.DiskIOMetrics) {
common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo) common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo)
} }
if self.ignoreMetrics.Has(container.DiskUsageMetrics) { if !self.includedMetrics.Has(container.DiskUsageMetrics) {
return nil return nil
} }
var device string var device string
......
...@@ -51,6 +51,7 @@ const ( ...@@ -51,6 +51,7 @@ const (
NetworkUsageMetrics MetricKind = "network" NetworkUsageMetrics MetricKind = "network"
NetworkTcpUsageMetrics MetricKind = "tcp" NetworkTcpUsageMetrics MetricKind = "tcp"
NetworkUdpUsageMetrics MetricKind = "udp" NetworkUdpUsageMetrics MetricKind = "udp"
AcceleratorUsageMetrics MetricKind = "accelerator"
AppMetrics MetricKind = "app" AppMetrics MetricKind = "app"
) )
......
...@@ -43,16 +43,16 @@ type Handler struct { ...@@ -43,16 +43,16 @@ type Handler struct {
cgroupManager cgroups.Manager cgroupManager cgroups.Manager
rootFs string rootFs string
pid int pid int
ignoreMetrics container.MetricSet includedMetrics container.MetricSet
pidMetricsCache map[int]*info.CpuSchedstat pidMetricsCache map[int]*info.CpuSchedstat
} }
func NewHandler(cgroupManager cgroups.Manager, rootFs string, pid int, ignoreMetrics container.MetricSet) *Handler { func NewHandler(cgroupManager cgroups.Manager, rootFs string, pid int, includedMetrics container.MetricSet) *Handler {
return &Handler{ return &Handler{
cgroupManager: cgroupManager, cgroupManager: cgroupManager,
rootFs: rootFs, rootFs: rootFs,
pid: pid, pid: pid,
ignoreMetrics: ignoreMetrics, includedMetrics: includedMetrics,
pidMetricsCache: make(map[int]*info.CpuSchedstat), pidMetricsCache: make(map[int]*info.CpuSchedstat),
} }
} }
...@@ -66,10 +66,10 @@ func (h *Handler) GetStats() (*info.ContainerStats, error) { ...@@ -66,10 +66,10 @@ func (h *Handler) GetStats() (*info.ContainerStats, error) {
libcontainerStats := &libcontainer.Stats{ libcontainerStats := &libcontainer.Stats{
CgroupStats: cgroupStats, CgroupStats: cgroupStats,
} }
withPerCPU := !h.ignoreMetrics.Has(container.PerCpuUsageMetrics) withPerCPU := h.includedMetrics.Has(container.PerCpuUsageMetrics)
stats := newContainerStats(libcontainerStats, withPerCPU) stats := newContainerStats(libcontainerStats, withPerCPU)
if !h.ignoreMetrics.Has(container.ProcessSchedulerMetrics) { if h.includedMetrics.Has(container.ProcessSchedulerMetrics) {
pids, err := h.cgroupManager.GetAllPids() pids, err := h.cgroupManager.GetAllPids()
if err != nil { if err != nil {
glog.V(4).Infof("Could not get PIDs for container %d: %v", h.pid, err) glog.V(4).Infof("Could not get PIDs for container %d: %v", h.pid, err)
...@@ -85,7 +85,7 @@ func (h *Handler) GetStats() (*info.ContainerStats, error) { ...@@ -85,7 +85,7 @@ func (h *Handler) GetStats() (*info.ContainerStats, error) {
if h.pid == 0 { if h.pid == 0 {
return stats, nil return stats, nil
} }
if !h.ignoreMetrics.Has(container.NetworkUsageMetrics) { if h.includedMetrics.Has(container.NetworkUsageMetrics) {
netStats, err := networkStatsFromProc(h.rootFs, h.pid) netStats, err := networkStatsFromProc(h.rootFs, h.pid)
if err != nil { if err != nil {
glog.V(4).Infof("Unable to get network stats from pid %d: %v", h.pid, err) glog.V(4).Infof("Unable to get network stats from pid %d: %v", h.pid, err)
...@@ -93,7 +93,7 @@ func (h *Handler) GetStats() (*info.ContainerStats, error) { ...@@ -93,7 +93,7 @@ func (h *Handler) GetStats() (*info.ContainerStats, error) {
stats.Network.Interfaces = append(stats.Network.Interfaces, netStats...) stats.Network.Interfaces = append(stats.Network.Interfaces, netStats...)
} }
} }
if !h.ignoreMetrics.Has(container.NetworkTcpUsageMetrics) { if h.includedMetrics.Has(container.NetworkTcpUsageMetrics) {
t, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp") t, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp")
if err != nil { if err != nil {
glog.V(4).Infof("Unable to get tcp stats from pid %d: %v", h.pid, err) glog.V(4).Infof("Unable to get tcp stats from pid %d: %v", h.pid, err)
...@@ -108,7 +108,7 @@ func (h *Handler) GetStats() (*info.ContainerStats, error) { ...@@ -108,7 +108,7 @@ func (h *Handler) GetStats() (*info.ContainerStats, error) {
stats.Network.Tcp6 = t6 stats.Network.Tcp6 = t6
} }
} }
if !h.ignoreMetrics.Has(container.NetworkUdpUsageMetrics) { if h.includedMetrics.Has(container.NetworkUdpUsageMetrics) {
u, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp") u, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp")
if err != nil { if err != nil {
glog.V(4).Infof("Unable to get udp stats from pid %d: %v", h.pid, err) glog.V(4).Infof("Unable to get udp stats from pid %d: %v", h.pid, err)
...@@ -498,14 +498,17 @@ func setMemoryStats(s *cgroups.Stats, ret *info.ContainerStats) { ...@@ -498,14 +498,17 @@ func setMemoryStats(s *cgroups.Stats, ret *info.ContainerStats) {
ret.Memory.Usage = s.MemoryStats.Usage.Usage ret.Memory.Usage = s.MemoryStats.Usage.Usage
ret.Memory.MaxUsage = s.MemoryStats.Usage.MaxUsage ret.Memory.MaxUsage = s.MemoryStats.Usage.MaxUsage
ret.Memory.Failcnt = s.MemoryStats.Usage.Failcnt ret.Memory.Failcnt = s.MemoryStats.Usage.Failcnt
ret.Memory.Cache = s.MemoryStats.Stats["cache"]
if s.MemoryStats.UseHierarchy { if s.MemoryStats.UseHierarchy {
ret.Memory.Cache = s.MemoryStats.Stats["total_cache"]
ret.Memory.RSS = s.MemoryStats.Stats["total_rss"] ret.Memory.RSS = s.MemoryStats.Stats["total_rss"]
ret.Memory.Swap = s.MemoryStats.Stats["total_swap"] ret.Memory.Swap = s.MemoryStats.Stats["total_swap"]
ret.Memory.MappedFile = s.MemoryStats.Stats["total_mapped_file"]
} else { } else {
ret.Memory.Cache = s.MemoryStats.Stats["cache"]
ret.Memory.RSS = s.MemoryStats.Stats["rss"] ret.Memory.RSS = s.MemoryStats.Stats["rss"]
ret.Memory.Swap = s.MemoryStats.Stats["swap"] ret.Memory.Swap = s.MemoryStats.Stats["swap"]
ret.Memory.MappedFile = s.MemoryStats.Stats["mapped_file"]
} }
if v, ok := s.MemoryStats.Stats["pgfault"]; ok { if v, ok := s.MemoryStats.Stats["pgfault"]; ok {
ret.Memory.ContainerData.Pgfault = v ret.Memory.ContainerData.Pgfault = v
......
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"client.go",
"factory.go",
"handler.go",
"mesos_agent.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/google/cadvisor/container/mesos",
importpath = "github.com/google/cadvisor/container/mesos",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/Rican7/retry:go_default_library",
"//vendor/github.com/Rican7/retry/strategy:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/google/cadvisor/container:go_default_library",
"//vendor/github.com/google/cadvisor/container/common:go_default_library",
"//vendor/github.com/google/cadvisor/container/libcontainer:go_default_library",
"//vendor/github.com/google/cadvisor/fs:go_default_library",
"//vendor/github.com/google/cadvisor/info/v1:go_default_library",
"//vendor/github.com/google/cadvisor/manager/watcher:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/agent:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/agent/calls:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/client:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/codecs:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/httpcli:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Copyright 2018 Google Inc. All Rights Reserved.
//
// 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 mesos
import (
"fmt"
"github.com/Rican7/retry"
"github.com/Rican7/retry/strategy"
"github.com/mesos/mesos-go/api/v1/lib"
"github.com/mesos/mesos-go/api/v1/lib/agent"
"github.com/mesos/mesos-go/api/v1/lib/agent/calls"
mclient "github.com/mesos/mesos-go/api/v1/lib/client"
"github.com/mesos/mesos-go/api/v1/lib/encoding/codecs"
"github.com/mesos/mesos-go/api/v1/lib/httpcli"
"net/url"
"sync"
)
const (
maxRetryAttempts = 3
invalidPID = -1
)
var (
mesosClientOnce sync.Once
mesosClient *client
)
type client struct {
hc *httpcli.Client
}
type mesosAgentClient interface {
ContainerInfo(id string) (*containerInfo, error)
ContainerPid(id string) (int, error)
}
type containerInfo struct {
cntr *mContainer
labels map[string]string
}
// Client is an interface to query mesos agent http endpoints
func Client() (mesosAgentClient, error) {
mesosClientOnce.Do(func() {
// Start Client
apiURL := url.URL{
Scheme: "http",
Host: *MesosAgentAddress,
Path: "/api/v1",
}
mesosClient = &client{
hc: httpcli.New(
httpcli.Endpoint(apiURL.String()),
httpcli.Codec(codecs.ByMediaType[codecs.MediaTypeProtobuf]),
httpcli.Do(httpcli.With(httpcli.Timeout(*MesosAgentTimeout))),
),
}
})
return mesosClient, nil
}
// ContainerInfo returns the container information of the given container id
func (self *client) ContainerInfo(id string) (*containerInfo, error) {
c, err := self.getContainer(id)
if err != nil {
return nil, err
}
// Get labels of the container
l, err := self.getLabels(c)
if err != nil {
return nil, err
}
return &containerInfo{
cntr: c,
labels: l,
}, nil
}
// Get the Pid of the container
func (self *client) ContainerPid(id string) (int, error) {
var pid int
var err error
err = retry.Retry(
func(attempt uint) error {
c, err := self.ContainerInfo(id)
if err != nil {
return err
}
if c.cntr.ContainerStatus != nil {
pid = int(*c.cntr.ContainerStatus.ExecutorPID)
} else {
err = fmt.Errorf("error fetching Pid")
}
return err
},
strategy.Limit(maxRetryAttempts),
)
if err != nil {
return invalidPID, fmt.Errorf("failed to fetch pid")
}
return pid, err
}
func (self *client) getContainer(id string) (*mContainer, error) {
// Get all containers
cntrs, err := self.getContainers()
if err != nil {
return nil, err
}
// Check if there is a container with given id and return the container
for _, c := range cntrs.Containers {
if c.ContainerID.Value == id {
return &c, nil
}
}
return nil, fmt.Errorf("can't locate container %s", id)
}
func (self *client) getContainers() (mContainers, error) {
req := calls.NonStreaming(calls.GetContainers())
result, err := self.fetchAndDecode(req)
if err != nil {
return nil, fmt.Errorf("failed to get mesos containers: %v", err)
}
cntrs := result.GetContainers
return cntrs, nil
}
func (self *client) getLabels(c *mContainer) (map[string]string, error) {
// Get mesos agent state which contains all containers labels
var s state
req := calls.NonStreaming(calls.GetState())
result, err := self.fetchAndDecode(req)
if err != nil {
return map[string]string{}, fmt.Errorf("failed to get mesos agent state: %v", err)
}
s.st = result.GetState
// Fetch labels from state object
labels, err := s.FetchLabels(c.FrameworkID.Value, c.ExecutorID.Value)
if err != nil {
return labels, fmt.Errorf("error while fetching labels from executor: %v", err)
}
return labels, nil
}
func (self *client) fetchAndDecode(req calls.RequestFunc) (*agent.Response, error) {
var res mesos.Response
var err error
// Send request
err = retry.Retry(
func(attempt uint) error {
res, err = mesosClient.hc.Send(req, mclient.ResponseClassSingleton, nil)
return err
},
strategy.Limit(maxRetryAttempts),
)
if err != nil {
return nil, fmt.Errorf("error fetching %s: %s", req.Call(), err)
}
// Decode the result
var target agent.Response
err = res.Decode(&target)
if err != nil {
return nil, fmt.Errorf("error while decoding response body from %s: %s", res, err)
}
return &target, nil
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// 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 mesos
import (
"flag"
"fmt"
"path"
"regexp"
"strings"
"time"
"github.com/golang/glog"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/manager/watcher"
)
var MesosAgentAddress = flag.String("mesos_agent", "127.0.0.1:5051", "Mesos agent address")
var MesosAgentTimeout = flag.Duration("mesos_agent_timeout", 10*time.Second, "Mesos agent timeout")
// The namespace under which mesos aliases are unique.
const MesosNamespace = "mesos"
// Regexp that identifies mesos cgroups, containers started with
// --cgroup-parent have another prefix than 'mesos'
var mesosCgroupRegexp = regexp.MustCompile(`([a-z-0-9]{36})`)
// mesosFactory implements the interface ContainerHandlerFactory
type mesosFactory struct {
machineInfoFactory info.MachineInfoFactory
// Information about the cgroup subsystems.
cgroupSubsystems libcontainer.CgroupSubsystems
// Information about mounted filesystems.
fsInfo fs.FsInfo
includedMetrics map[container.MetricKind]struct{}
client mesosAgentClient
}
func (self *mesosFactory) String() string {
return MesosNamespace
}
func (self *mesosFactory) NewContainerHandler(name string, inHostNamespace bool) (container.ContainerHandler, error) {
client, err := Client()
if err != nil {
return nil, err
}
return newMesosContainerHandler(
name,
&self.cgroupSubsystems,
self.machineInfoFactory,
self.fsInfo,
self.includedMetrics,
inHostNamespace,
client,
)
}
// ContainerNameToMesosId returns the Mesos ID from the full container name.
func ContainerNameToMesosId(name string) string {
id := path.Base(name)
if matches := mesosCgroupRegexp.FindStringSubmatch(id); matches != nil {
return matches[1]
}
return id
}
// isContainerName returns true if the cgroup with associated name
// corresponds to a mesos container.
func isContainerName(name string) bool {
// always ignore .mount cgroup even if associated with mesos and delegate to systemd
if strings.HasSuffix(name, ".mount") {
return false
}
return mesosCgroupRegexp.MatchString(path.Base(name))
}
// The mesos factory can handle any container.
func (self *mesosFactory) CanHandleAndAccept(name string) (handle bool, accept bool, err error) {
// if the container is not associated with mesos, we can't handle it or accept it.
if !isContainerName(name) {
return false, false, nil
}
// Check if the container is known to mesos and it is active.
id := ContainerNameToMesosId(name)
_, err = self.client.ContainerInfo(id)
if err != nil {
return false, true, fmt.Errorf("error getting running container: %v", err)
}
return true, true, nil
}
func (self *mesosFactory) DebugInfo() map[string][]string {
return map[string][]string{}
}
func Register(
machineInfoFactory info.MachineInfoFactory,
fsInfo fs.FsInfo,
includedMetrics container.MetricSet,
) error {
client, err := Client()
if err != nil {
return fmt.Errorf("unable to create mesos agent client: %v", err)
}
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
if err != nil {
return fmt.Errorf("failed to get cgroup subsystems: %v", err)
}
glog.V(1).Infof("Registering mesos factory")
factory := &mesosFactory{
machineInfoFactory: machineInfoFactory,
cgroupSubsystems: cgroupSubsystems,
fsInfo: fsInfo,
includedMetrics: includedMetrics,
client: client,
}
container.RegisterContainerHandlerFactory(factory, []watcher.ContainerWatchSource{watcher.Raw})
return nil
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// 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.
// Handler for "mesos" containers.
package mesos
import (
"fmt"
"path"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common"
containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
)
type mesosContainerHandler struct {
// Name of the container for this handler.
name string
// machineInfoFactory provides info.MachineInfo
machineInfoFactory info.MachineInfoFactory
// Absolute path to the cgroup hierarchies of this container.
// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
cgroupPaths map[string]string
// File System Info
fsInfo fs.FsInfo
// Metrics to be included.
includedMetrics container.MetricSet
labels map[string]string
// Reference to the container
reference info.ContainerReference
libcontainerHandler *containerlibcontainer.Handler
}
func isRootCgroup(name string) bool {
return name == "/"
}
func newMesosContainerHandler(
name string,
cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
machineInfoFactory info.MachineInfoFactory,
fsInfo fs.FsInfo,
includedMetrics container.MetricSet,
inHostNamespace bool,
client mesosAgentClient,
) (container.ContainerHandler, error) {
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
for key, val := range cgroupSubsystems.MountPoints {
cgroupPaths[key] = path.Join(val, name)
}
// Generate the equivalent cgroup manager for this container.
cgroupManager := &cgroupfs.Manager{
Cgroups: &libcontainerconfigs.Cgroup{
Name: name,
},
Paths: cgroupPaths,
}
rootFs := "/"
if !inHostNamespace {
rootFs = "/rootfs"
}
id := ContainerNameToMesosId(name)
cinfo, err := client.ContainerInfo(id)
if err != nil {
return nil, err
}
labels := cinfo.labels
pid, err := client.ContainerPid(id)
if err != nil {
return nil, err
}
libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootFs, pid, includedMetrics)
reference := info.ContainerReference{
Id: id,
Name: name,
Namespace: MesosNamespace,
Aliases: []string{id, name},
}
handler := &mesosContainerHandler{
name: name,
machineInfoFactory: machineInfoFactory,
cgroupPaths: cgroupPaths,
fsInfo: fsInfo,
includedMetrics: includedMetrics,
labels: labels,
reference: reference,
libcontainerHandler: libcontainerHandler,
}
return handler, nil
}
func (self *mesosContainerHandler) ContainerReference() (info.ContainerReference, error) {
// We only know the container by its one name.
return self.reference, nil
}
// Nothing to start up.
func (self *mesosContainerHandler) Start() {}
// Nothing to clean up.
func (self *mesosContainerHandler) Cleanup() {}
func (self *mesosContainerHandler) GetSpec() (info.ContainerSpec, error) {
// TODO: Since we dont collect disk usage and network stats for mesos containers, we set
// hasFilesystem and hasNetwork to false. Revisit when we support disk usage, network
// stats for mesos containers.
hasNetwork := false
hasFilesystem := false
spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, hasNetwork, hasFilesystem)
if err != nil {
return spec, err
}
spec.Labels = self.labels
return spec, nil
}
func (self *mesosContainerHandler) getFsStats(stats *info.ContainerStats) error {
mi, err := self.machineInfoFactory.GetMachineInfo()
if err != nil {
return err
}
if self.includedMetrics.Has(container.DiskIOMetrics) {
common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo)
}
return nil
}
func (self *mesosContainerHandler) GetStats() (*info.ContainerStats, error) {
stats, err := self.libcontainerHandler.GetStats()
if err != nil {
return stats, err
}
// Get filesystem stats.
err = self.getFsStats(stats)
if err != nil {
return stats, err
}
return stats, nil
}
func (self *mesosContainerHandler) GetCgroupPath(resource string) (string, error) {
path, ok := self.cgroupPaths[resource]
if !ok {
return "", fmt.Errorf("could not find path for resource %q for container %q\n", resource, self.name)
}
return path, nil
}
func (self *mesosContainerHandler) GetContainerLabels() map[string]string {
return self.labels
}
func (self *mesosContainerHandler) GetContainerIPAddress() string {
// the IP address for the mesos container corresponds to the system ip address.
return "127.0.0.1"
}
func (self *mesosContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
return common.ListContainers(self.name, self.cgroupPaths, listType)
}
func (self *mesosContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
return self.libcontainerHandler.GetProcesses()
}
func (self *mesosContainerHandler) Exists() bool {
return common.CgroupExists(self.cgroupPaths)
}
func (self *mesosContainerHandler) Type() container.ContainerType {
return container.ContainerTypeMesos
}
// Copyright 2018 Google Inc. All Rights Reserved.
//
// 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 mesos
import (
"fmt"
"github.com/mesos/mesos-go/api/v1/lib"
"github.com/mesos/mesos-go/api/v1/lib/agent"
)
const (
cpus = "cpus"
schedulerSLA = "scheduler_sla"
framework = "framework"
source = "source"
revocable = "revocable"
nonRevocable = "non_revocable"
)
type mContainers *agent.Response_GetContainers
type mContainer = agent.Response_GetContainers_Container
type (
state struct {
st *agent.Response_GetState
}
)
// GetFramework finds a framework with the given id and returns nil if not found. Note that
// this is different from the framework name.
func (s *state) GetFramework(id string) (*mesos.FrameworkInfo, error) {
for _, fw := range s.st.GetFrameworks.Frameworks {
if fw.FrameworkInfo.ID.Value == id {
return &fw.FrameworkInfo, nil
}
}
return nil, fmt.Errorf("unable to find framework id %s", id)
}
// GetExecutor finds an executor with the given ID and returns nil if not found. Note that
// this is different from the executor name.
func (s *state) GetExecutor(id string) (*mesos.ExecutorInfo, error) {
for _, exec := range s.st.GetExecutors.Executors {
if exec.ExecutorInfo.ExecutorID.Value == id {
return &exec.ExecutorInfo, nil
}
}
return nil, fmt.Errorf("unable to find executor with id %s", id)
}
// GetTask returns a task launched by given executor.
func (s *state) GetTask(exID string) (*mesos.Task, error) {
// Check if task is in Launched Tasks list
for _, t := range s.st.GetTasks.LaunchedTasks {
if s.isMatchingTask(&t, exID) {
return &t, nil
}
}
// Check if task is in Queued Tasks list
for _, t := range s.st.GetTasks.QueuedTasks {
if s.isMatchingTask(&t, exID) {
return &t, nil
}
}
return nil, fmt.Errorf("unable to find task matching executor id %s", exID)
}
func (s *state) isMatchingTask(t *mesos.Task, exID string) bool {
// MESOS-9111: For tasks launched through mesos command/default executor, the
// executorID(which is same as the taskID) field is not filled in the TaskInfo object.
// The workaround is compare with taskID field if executorID is empty
if t.ExecutorID != nil {
if t.ExecutorID.Value == exID {
return true
}
} else {
if t.TaskID.Value == exID {
return true
}
}
return false
}
func (s *state) fetchLabelsFromTask(exID string, labels map[string]string) error {
t, err := s.GetTask(exID)
if err != nil {
return err
}
// Identify revocability. Can be removed once we have a proper label
for _, resource := range t.Resources {
if resource.Name == cpus {
if resource.Revocable != nil {
labels[schedulerSLA] = revocable
} else {
labels[schedulerSLA] = nonRevocable
}
break
}
}
for _, l := range t.Labels.Labels {
labels[l.Key] = *l.Value
}
return nil
}
func (s *state) FetchLabels(fwID string, exID string) (map[string]string, error) {
labels := make(map[string]string)
// Look for the framework which launched the container.
fw, err := s.GetFramework(fwID)
if err != nil {
return labels, fmt.Errorf("framework ID %q not found: %v", fwID, err)
}
labels[framework] = fw.Name
// Get the executor info of the container which contains all the task info.
exec, err := s.GetExecutor(exID)
if err != nil {
return labels, fmt.Errorf("executor ID %q not found: %v", exID, err)
}
labels[source] = *exec.Source
err = s.fetchLabelsFromTask(exID, labels)
if err != nil {
return labels, fmt.Errorf("failed to fetch labels from task with executor ID %s", exID)
}
return labels, nil
}
...@@ -17,6 +17,7 @@ package raw ...@@ -17,6 +17,7 @@ package raw
import ( import (
"flag" "flag"
"fmt" "fmt"
"strings"
"github.com/google/cadvisor/container" "github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common" "github.com/google/cadvisor/container/common"
...@@ -43,8 +44,11 @@ type rawFactory struct { ...@@ -43,8 +44,11 @@ type rawFactory struct {
// Watcher for inotify events. // Watcher for inotify events.
watcher *common.InotifyWatcher watcher *common.InotifyWatcher
// List of metrics to be ignored. // List of metrics to be included.
ignoreMetrics map[container.MetricKind]struct{} includedMetrics map[container.MetricKind]struct{}
// List of raw container cgroup path prefix whitelist.
rawPrefixWhiteList []string
} }
func (self *rawFactory) String() string { func (self *rawFactory) String() string {
...@@ -56,12 +60,19 @@ func (self *rawFactory) NewContainerHandler(name string, inHostNamespace bool) ( ...@@ -56,12 +60,19 @@ func (self *rawFactory) NewContainerHandler(name string, inHostNamespace bool) (
if !inHostNamespace { if !inHostNamespace {
rootFs = "/rootfs" rootFs = "/rootfs"
} }
return newRawContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo, self.watcher, rootFs, self.ignoreMetrics) return newRawContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo, self.watcher, rootFs, self.includedMetrics)
} }
// The raw factory can handle any container. If --docker_only is set to false, non-docker containers are ignored. // The raw factory can handle any container. If --docker_only is set to false, non-docker containers are ignored.
func (self *rawFactory) CanHandleAndAccept(name string) (bool, bool, error) { func (self *rawFactory) CanHandleAndAccept(name string) (bool, bool, error) {
accept := name == "/" || !*dockerOnly accept := name == "/" || !*dockerOnly
for _, prefix := range self.rawPrefixWhiteList {
if strings.HasPrefix(name, prefix) {
accept = true
break
}
}
return true, accept, nil return true, accept, nil
} }
...@@ -69,7 +80,7 @@ func (self *rawFactory) DebugInfo() map[string][]string { ...@@ -69,7 +80,7 @@ func (self *rawFactory) DebugInfo() map[string][]string {
return common.DebugInfo(self.watcher.GetWatches()) return common.DebugInfo(self.watcher.GetWatches())
} }
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics map[container.MetricKind]struct{}) error { func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics map[container.MetricKind]struct{}, rawPrefixWhiteList []string) error {
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems() cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
if err != nil { if err != nil {
return fmt.Errorf("failed to get cgroup subsystems: %v", err) return fmt.Errorf("failed to get cgroup subsystems: %v", err)
...@@ -89,7 +100,8 @@ func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, igno ...@@ -89,7 +100,8 @@ func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, igno
fsInfo: fsInfo, fsInfo: fsInfo,
cgroupSubsystems: &cgroupSubsystems, cgroupSubsystems: &cgroupSubsystems,
watcher: watcher, watcher: watcher,
ignoreMetrics: ignoreMetrics, includedMetrics: includedMetrics,
rawPrefixWhiteList: rawPrefixWhiteList,
} }
container.RegisterContainerHandlerFactory(factory, []watch.ContainerWatchSource{watch.Raw}) container.RegisterContainerHandlerFactory(factory, []watch.ContainerWatchSource{watch.Raw})
return nil return nil
......
...@@ -49,7 +49,7 @@ func isRootCgroup(name string) bool { ...@@ -49,7 +49,7 @@ func isRootCgroup(name string) bool {
return name == "/" return name == "/"
} }
func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, watcher *common.InotifyWatcher, rootFs string, ignoreMetrics container.MetricSet) (container.ContainerHandler, error) { func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, watcher *common.InotifyWatcher, rootFs string, includedMetrics container.MetricSet) (container.ContainerHandler, error) {
cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name) cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
cHints, err := common.GetContainerHintsFromFile(*common.ArgContainerHints) cHints, err := common.GetContainerHintsFromFile(*common.ArgContainerHints)
...@@ -78,7 +78,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSu ...@@ -78,7 +78,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSu
pid = 1 pid = 1
} }
handler := libcontainer.NewHandler(cgroupManager, rootFs, pid, ignoreMetrics) handler := libcontainer.NewHandler(cgroupManager, rootFs, pid, includedMetrics)
return &rawContainerHandler{ return &rawContainerHandler{
name: name, name: name,
......
...@@ -35,7 +35,7 @@ type rktFactory struct { ...@@ -35,7 +35,7 @@ type rktFactory struct {
fsInfo fs.FsInfo fsInfo fs.FsInfo
ignoreMetrics container.MetricSet includedMetrics container.MetricSet
rktPath string rktPath string
} }
...@@ -54,7 +54,7 @@ func (self *rktFactory) NewContainerHandler(name string, inHostNamespace bool) ( ...@@ -54,7 +54,7 @@ func (self *rktFactory) NewContainerHandler(name string, inHostNamespace bool) (
if !inHostNamespace { if !inHostNamespace {
rootFs = "/rootfs" rootFs = "/rootfs"
} }
return newRktContainerHandler(name, client, self.rktPath, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo, rootFs, self.ignoreMetrics) return newRktContainerHandler(name, client, self.rktPath, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo, rootFs, self.includedMetrics)
} }
func (self *rktFactory) CanHandleAndAccept(name string) (bool, bool, error) { func (self *rktFactory) CanHandleAndAccept(name string) (bool, bool, error) {
...@@ -67,7 +67,7 @@ func (self *rktFactory) DebugInfo() map[string][]string { ...@@ -67,7 +67,7 @@ func (self *rktFactory) DebugInfo() map[string][]string {
return map[string][]string{} return map[string][]string{}
} }
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error { func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) error {
_, err := Client() _, err := Client()
if err != nil { if err != nil {
return fmt.Errorf("unable to communicate with Rkt api service: %v", err) return fmt.Errorf("unable to communicate with Rkt api service: %v", err)
...@@ -91,7 +91,7 @@ func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, igno ...@@ -91,7 +91,7 @@ func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, igno
machineInfoFactory: machineInfoFactory, machineInfoFactory: machineInfoFactory,
fsInfo: fsInfo, fsInfo: fsInfo,
cgroupSubsystems: &cgroupSubsystems, cgroupSubsystems: &cgroupSubsystems,
ignoreMetrics: ignoreMetrics, includedMetrics: includedMetrics,
rktPath: rktPath, rktPath: rktPath,
} }
container.RegisterContainerHandlerFactory(factory, []watcher.ContainerWatchSource{watcher.Rkt}) container.RegisterContainerHandlerFactory(factory, []watcher.ContainerWatchSource{watcher.Rkt})
......
...@@ -48,7 +48,7 @@ type rktContainerHandler struct { ...@@ -48,7 +48,7 @@ type rktContainerHandler struct {
// Filesystem handler. // Filesystem handler.
fsHandler common.FsHandler fsHandler common.FsHandler
ignoreMetrics container.MetricSet includedMetrics container.MetricSet
apiPod *rktapi.Pod apiPod *rktapi.Pod
...@@ -59,7 +59,7 @@ type rktContainerHandler struct { ...@@ -59,7 +59,7 @@ type rktContainerHandler struct {
libcontainerHandler *libcontainer.Handler libcontainerHandler *libcontainer.Handler
} }
func newRktContainerHandler(name string, rktClient rktapi.PublicAPIClient, rktPath string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, rootFs string, ignoreMetrics container.MetricSet) (container.ContainerHandler, error) { func newRktContainerHandler(name string, rktClient rktapi.PublicAPIClient, rktPath string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, rootFs string, includedMetrics container.MetricSet) (container.ContainerHandler, error) {
aliases := make([]string, 1) aliases := make([]string, 1)
isPod := false isPod := false
...@@ -109,7 +109,7 @@ func newRktContainerHandler(name string, rktClient rktapi.PublicAPIClient, rktPa ...@@ -109,7 +109,7 @@ func newRktContainerHandler(name string, rktClient rktapi.PublicAPIClient, rktPa
Paths: cgroupPaths, Paths: cgroupPaths,
} }
libcontainerHandler := libcontainer.NewHandler(cgroupManager, rootFs, pid, ignoreMetrics) libcontainerHandler := libcontainer.NewHandler(cgroupManager, rootFs, pid, includedMetrics)
rootfsStorageDir := getRootFs(rktPath, parsed) rootfsStorageDir := getRootFs(rktPath, parsed)
...@@ -125,14 +125,14 @@ func newRktContainerHandler(name string, rktClient rktapi.PublicAPIClient, rktPa ...@@ -125,14 +125,14 @@ func newRktContainerHandler(name string, rktClient rktapi.PublicAPIClient, rktPa
fsInfo: fsInfo, fsInfo: fsInfo,
isPod: isPod, isPod: isPod,
rootfsStorageDir: rootfsStorageDir, rootfsStorageDir: rootfsStorageDir,
ignoreMetrics: ignoreMetrics, includedMetrics: includedMetrics,
apiPod: apiPod, apiPod: apiPod,
labels: labels, labels: labels,
reference: containerReference, reference: containerReference,
libcontainerHandler: libcontainerHandler, libcontainerHandler: libcontainerHandler,
} }
if !ignoreMetrics.Has(container.DiskUsageMetrics) { if includedMetrics.Has(container.DiskUsageMetrics) {
handler.fsHandler = common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, "", fsInfo) handler.fsHandler = common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, "", fsInfo)
} }
...@@ -170,8 +170,8 @@ func (handler *rktContainerHandler) Cleanup() { ...@@ -170,8 +170,8 @@ func (handler *rktContainerHandler) Cleanup() {
} }
func (handler *rktContainerHandler) GetSpec() (info.ContainerSpec, error) { func (handler *rktContainerHandler) GetSpec() (info.ContainerSpec, error) {
hasNetwork := handler.isPod && !handler.ignoreMetrics.Has(container.NetworkUsageMetrics) hasNetwork := handler.isPod && handler.includedMetrics.Has(container.NetworkUsageMetrics)
hasFilesystem := !handler.ignoreMetrics.Has(container.DiskUsageMetrics) hasFilesystem := handler.includedMetrics.Has(container.DiskUsageMetrics)
spec, err := common.GetSpec(handler.cgroupPaths, handler.machineInfoFactory, hasNetwork, hasFilesystem) spec, err := common.GetSpec(handler.cgroupPaths, handler.machineInfoFactory, hasNetwork, hasFilesystem)
...@@ -186,11 +186,11 @@ func (handler *rktContainerHandler) getFsStats(stats *info.ContainerStats) error ...@@ -186,11 +186,11 @@ func (handler *rktContainerHandler) getFsStats(stats *info.ContainerStats) error
return err return err
} }
if !handler.ignoreMetrics.Has(container.DiskIOMetrics) { if handler.includedMetrics.Has(container.DiskIOMetrics) {
common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo) common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo)
} }
if handler.ignoreMetrics.Has(container.DiskUsageMetrics) { if !handler.includedMetrics.Has(container.DiskUsageMetrics) {
return nil return nil
} }
......
...@@ -50,7 +50,7 @@ func (f *systemdFactory) DebugInfo() map[string][]string { ...@@ -50,7 +50,7 @@ func (f *systemdFactory) DebugInfo() map[string][]string {
} }
// Register registers the systemd container factory. // Register registers the systemd container factory.
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error { func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) error {
glog.V(1).Infof("Registering systemd factory") glog.V(1).Infof("Registering systemd factory")
factory := &systemdFactory{} factory := &systemdFactory{}
container.RegisterContainerHandlerFactory(factory, []watcher.ContainerWatchSource{watcher.Raw}) container.RegisterContainerHandlerFactory(factory, []watcher.ContainerWatchSource{watcher.Raw})
......
...@@ -422,7 +422,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er ...@@ -422,7 +422,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er
} }
} }
if err != nil { if err != nil {
glog.Errorf("Stat fs failed. Error: %v", err) glog.V(4).Infof("Stat fs failed. Error: %v", err)
} else { } else {
deviceSet[device] = struct{}{} deviceSet[device] = struct{}{}
fs.DeviceInfo = DeviceInfo{ fs.DeviceInfo = DeviceInfo{
...@@ -533,6 +533,21 @@ func (self *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) { ...@@ -533,6 +533,21 @@ func (self *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) {
} }
mount, found := self.mounts[dir] mount, found := self.mounts[dir]
// try the parent dir if not found until we reach the root dir
// this is an issue on btrfs systems where the directory is not
// the subvolume
for !found {
pathdir, _ := filepath.Split(dir)
// break when we reach root
if pathdir == "/" {
break
}
// trim "/" from the new parent path otherwise the next possible
// filepath.Split in the loop will not split the string any further
dir = strings.TrimSuffix(pathdir, "/")
mount, found = self.mounts[dir]
}
if found && mount.Fstype == "btrfs" && mount.Major == 0 && strings.HasPrefix(mount.Source, "/dev/") { if found && mount.Fstype == "btrfs" && mount.Major == 0 && strings.HasPrefix(mount.Source, "/dev/") {
major, minor, err := getBtrfsMajorMinorIds(mount) major, minor, err := getBtrfsMajorMinorIds(mount)
if err != nil { if err != nil {
......
...@@ -358,6 +358,9 @@ type MemoryStats struct { ...@@ -358,6 +358,9 @@ type MemoryStats struct {
// Units: Bytes. // Units: Bytes.
Swap uint64 `json:"swap"` Swap uint64 `json:"swap"`
// The amount of memory used for mapped files (includes tmpfs/shmem)
MappedFile uint64 `json:"mapped_file"`
// The amount of working set memory, this includes recently accessed memory, // The amount of working set memory, this includes recently accessed memory,
// dirty memory, and kernel memory. Working set is <= "usage". // dirty memory, and kernel memory. Working set is <= "usage".
// Units: Bytes. // Units: Bytes.
......
...@@ -19,6 +19,7 @@ go_library( ...@@ -19,6 +19,7 @@ go_library(
"//vendor/github.com/google/cadvisor/container/containerd:go_default_library", "//vendor/github.com/google/cadvisor/container/containerd:go_default_library",
"//vendor/github.com/google/cadvisor/container/crio:go_default_library", "//vendor/github.com/google/cadvisor/container/crio:go_default_library",
"//vendor/github.com/google/cadvisor/container/docker:go_default_library", "//vendor/github.com/google/cadvisor/container/docker:go_default_library",
"//vendor/github.com/google/cadvisor/container/mesos:go_default_library",
"//vendor/github.com/google/cadvisor/container/raw:go_default_library", "//vendor/github.com/google/cadvisor/container/raw:go_default_library",
"//vendor/github.com/google/cadvisor/container/rkt:go_default_library", "//vendor/github.com/google/cadvisor/container/rkt:go_default_library",
"//vendor/github.com/google/cadvisor/container/systemd:go_default_library", "//vendor/github.com/google/cadvisor/container/systemd:go_default_library",
......
...@@ -33,6 +33,7 @@ import ( ...@@ -33,6 +33,7 @@ import (
"github.com/google/cadvisor/container/containerd" "github.com/google/cadvisor/container/containerd"
"github.com/google/cadvisor/container/crio" "github.com/google/cadvisor/container/crio"
"github.com/google/cadvisor/container/docker" "github.com/google/cadvisor/container/docker"
"github.com/google/cadvisor/container/mesos"
"github.com/google/cadvisor/container/raw" "github.com/google/cadvisor/container/raw"
"github.com/google/cadvisor/container/rkt" "github.com/google/cadvisor/container/rkt"
"github.com/google/cadvisor/container/systemd" "github.com/google/cadvisor/container/systemd"
...@@ -141,7 +142,7 @@ type Manager interface { ...@@ -141,7 +142,7 @@ type Manager interface {
} }
// New takes a memory storage and returns a new manager. // New takes a memory storage and returns a new manager.
func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingInterval time.Duration, allowDynamicHousekeeping bool, ignoreMetricsSet container.MetricSet, collectorHttpClient *http.Client) (Manager, error) { func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingInterval time.Duration, allowDynamicHousekeeping bool, includedMetricsSet container.MetricSet, collectorHttpClient *http.Client, rawContainerCgroupPathPrefixWhiteList []string) (Manager, error) {
if memoryCache == nil { if memoryCache == nil {
return nil, fmt.Errorf("manager requires memory storage") return nil, fmt.Errorf("manager requires memory storage")
} }
...@@ -203,20 +204,21 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingIn ...@@ -203,20 +204,21 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingIn
eventsChannel := make(chan watcher.ContainerEvent, 16) eventsChannel := make(chan watcher.ContainerEvent, 16)
newManager := &manager{ newManager := &manager{
containers: make(map[namespacedContainerName]*containerData), containers: make(map[namespacedContainerName]*containerData),
quitChannels: make([]chan error, 0, 2), quitChannels: make([]chan error, 0, 2),
memoryCache: memoryCache, memoryCache: memoryCache,
fsInfo: fsInfo, fsInfo: fsInfo,
cadvisorContainer: selfContainer, cadvisorContainer: selfContainer,
inHostNamespace: inHostNamespace, inHostNamespace: inHostNamespace,
startupTime: time.Now(), startupTime: time.Now(),
maxHousekeepingInterval: maxHousekeepingInterval, maxHousekeepingInterval: maxHousekeepingInterval,
allowDynamicHousekeeping: allowDynamicHousekeeping, allowDynamicHousekeeping: allowDynamicHousekeeping,
ignoreMetrics: ignoreMetricsSet, includedMetrics: includedMetricsSet,
containerWatchers: []watcher.ContainerWatcher{}, containerWatchers: []watcher.ContainerWatcher{},
eventsChannel: eventsChannel, eventsChannel: eventsChannel,
collectorHttpClient: collectorHttpClient, collectorHttpClient: collectorHttpClient,
nvidiaManager: &accelerators.NvidiaManager{}, nvidiaManager: &accelerators.NvidiaManager{},
rawContainerCgroupPathPrefixWhiteList: rawContainerCgroupPathPrefixWhiteList,
} }
machineInfo, err := machine.Info(sysfs, fsInfo, inHostNamespace) machineInfo, err := machine.Info(sysfs, fsInfo, inHostNamespace)
...@@ -283,21 +285,23 @@ type manager struct { ...@@ -283,21 +285,23 @@ type manager struct {
startupTime time.Time startupTime time.Time
maxHousekeepingInterval time.Duration maxHousekeepingInterval time.Duration
allowDynamicHousekeeping bool allowDynamicHousekeeping bool
ignoreMetrics container.MetricSet includedMetrics container.MetricSet
containerWatchers []watcher.ContainerWatcher containerWatchers []watcher.ContainerWatcher
eventsChannel chan watcher.ContainerEvent eventsChannel chan watcher.ContainerEvent
collectorHttpClient *http.Client collectorHttpClient *http.Client
nvidiaManager accelerators.AcceleratorManager nvidiaManager accelerators.AcceleratorManager
// List of raw container cgroup path prefix whitelist.
rawContainerCgroupPathPrefixWhiteList []string
} }
// Start the container manager. // Start the container manager.
func (self *manager) Start() error { func (self *manager) Start() error {
err := docker.Register(self, self.fsInfo, self.ignoreMetrics) err := docker.Register(self, self.fsInfo, self.includedMetrics)
if err != nil { if err != nil {
glog.V(5).Infof("Registration of the Docker container factory failed: %v.", err) glog.V(5).Infof("Registration of the Docker container factory failed: %v.", err)
} }
err = rkt.Register(self, self.fsInfo, self.ignoreMetrics) err = rkt.Register(self, self.fsInfo, self.includedMetrics)
if err != nil { if err != nil {
glog.V(5).Infof("Registration of the rkt container factory failed: %v", err) glog.V(5).Infof("Registration of the rkt container factory failed: %v", err)
} else { } else {
...@@ -308,22 +312,27 @@ func (self *manager) Start() error { ...@@ -308,22 +312,27 @@ func (self *manager) Start() error {
self.containerWatchers = append(self.containerWatchers, watcher) self.containerWatchers = append(self.containerWatchers, watcher)
} }
err = containerd.Register(self, self.fsInfo, self.ignoreMetrics) err = containerd.Register(self, self.fsInfo, self.includedMetrics)
if err != nil { if err != nil {
glog.V(5).Infof("Registration of the containerd container factory failed: %v", err) glog.V(5).Infof("Registration of the containerd container factory failed: %v", err)
} }
err = crio.Register(self, self.fsInfo, self.ignoreMetrics) err = crio.Register(self, self.fsInfo, self.includedMetrics)
if err != nil { if err != nil {
glog.V(5).Infof("Registration of the crio container factory failed: %v", err) glog.V(5).Infof("Registration of the crio container factory failed: %v", err)
} }
err = systemd.Register(self, self.fsInfo, self.ignoreMetrics) err = mesos.Register(self, self.fsInfo, self.includedMetrics)
if err != nil {
glog.V(5).Infof("Registration of the mesos container factory failed: %v", err)
}
err = systemd.Register(self, self.fsInfo, self.includedMetrics)
if err != nil { if err != nil {
glog.V(5).Infof("Registration of the systemd container factory failed: %v", err) glog.V(5).Infof("Registration of the systemd container factory failed: %v", err)
} }
err = raw.Register(self, self.fsInfo, self.ignoreMetrics) err = raw.Register(self, self.fsInfo, self.includedMetrics, self.rawContainerCgroupPathPrefixWhiteList)
if err != nil { if err != nil {
glog.Errorf("Registration of the raw container factory failed: %v", err) glog.Errorf("Registration of the raw container factory failed: %v", err)
} }
...@@ -619,6 +628,11 @@ func (self *manager) AllDockerContainers(query *info.ContainerInfoRequest) (map[ ...@@ -619,6 +628,11 @@ func (self *manager) AllDockerContainers(query *info.ContainerInfoRequest) (map[
for name, cont := range containers { for name, cont := range containers {
inf, err := self.containerDataToContainerInfo(cont, query) inf, err := self.containerDataToContainerInfo(cont, query)
if err != nil { if err != nil {
// Ignore the error because of race condition and return best-effort result.
if err == memory.ErrDataNotFound {
glog.Warningf("Error getting data for container %s because of race condition", name)
continue
}
return nil, err return nil, err
} }
output[name] = *inf output[name] = *inf
...@@ -1072,22 +1086,25 @@ func (m *manager) destroyContainerLocked(containerName string) error { ...@@ -1072,22 +1086,25 @@ func (m *manager) destroyContainerLocked(containerName string) error {
// Detect all containers that have been added or deleted from the specified container. // Detect all containers that have been added or deleted from the specified container.
func (m *manager) getContainersDiff(containerName string) (added []info.ContainerReference, removed []info.ContainerReference, err error) { func (m *manager) getContainersDiff(containerName string) (added []info.ContainerReference, removed []info.ContainerReference, err error) {
m.containersLock.RLock()
defer m.containersLock.RUnlock()
// Get all subcontainers recursively. // Get all subcontainers recursively.
m.containersLock.RLock()
cont, ok := m.containers[namespacedContainerName{ cont, ok := m.containers[namespacedContainerName{
Name: containerName, Name: containerName,
}] }]
m.containersLock.RUnlock()
if !ok { if !ok {
return nil, nil, fmt.Errorf("failed to find container %q while checking for new containers", containerName) return nil, nil, fmt.Errorf("failed to find container %q while checking for new containers", containerName)
} }
allContainers, err := cont.handler.ListContainers(container.ListRecursive) allContainers, err := cont.handler.ListContainers(container.ListRecursive)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
allContainers = append(allContainers, info.ContainerReference{Name: containerName}) allContainers = append(allContainers, info.ContainerReference{Name: containerName})
m.containersLock.RLock()
defer m.containersLock.RUnlock()
// Determine which were added and which were removed. // Determine which were added and which were removed.
allContainersSet := make(map[string]*containerData) allContainersSet := make(map[string]*containerData)
for name, d := range m.containers { for name, d := range m.containers {
......
...@@ -8,6 +8,7 @@ go_library( ...@@ -8,6 +8,7 @@ go_library(
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/google/cadvisor/container:go_default_library",
"//vendor/github.com/google/cadvisor/info/v1:go_default_library", "//vendor/github.com/google/cadvisor/info/v1:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library", "//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
], ],
......
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
"regexp" "regexp"
"time" "time"
"github.com/google/cadvisor/container"
info "github.com/google/cadvisor/info/v1" info "github.com/google/cadvisor/info/v1"
"github.com/golang/glog" "github.com/golang/glog"
...@@ -114,7 +115,7 @@ type PrometheusCollector struct { ...@@ -114,7 +115,7 @@ type PrometheusCollector struct {
// ContainerLabelsFunc specifies which base labels will be attached to all // ContainerLabelsFunc specifies which base labels will be attached to all
// exported metrics. If left to nil, the DefaultContainerLabels function // exported metrics. If left to nil, the DefaultContainerLabels function
// will be used instead. // will be used instead.
func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCollector { func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc, includedMetrics container.MetricSet) *PrometheusCollector {
if f == nil { if f == nil {
f = DefaultContainerLabels f = DefaultContainerLabels
} }
...@@ -134,7 +135,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -134,7 +135,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
getValues: func(s *info.ContainerStats) metricValues { getValues: func(s *info.ContainerStats) metricValues {
return metricValues{{value: float64(time.Now().Unix())}} return metricValues{{value: float64(time.Now().Unix())}}
}, },
}, { },
},
}
if includedMetrics.Has(container.CpuUsageMetrics) {
c.containerMetrics = append(c.containerMetrics, []containerMetric{
{
name: "container_cpu_user_seconds_total", name: "container_cpu_user_seconds_total",
help: "Cumulative user cpu time consumed in seconds.", help: "Cumulative user cpu time consumed in seconds.",
valueType: prometheus.CounterValue, valueType: prometheus.CounterValue,
...@@ -197,7 +203,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -197,7 +203,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
getValues: func(s *info.ContainerStats) metricValues { getValues: func(s *info.ContainerStats) metricValues {
return metricValues{{value: float64(s.Cpu.CFS.ThrottledTime) / float64(time.Second)}} return metricValues{{value: float64(s.Cpu.CFS.ThrottledTime) / float64(time.Second)}}
}, },
}, { },
}...)
}
if includedMetrics.Has(container.ProcessSchedulerMetrics) {
c.containerMetrics = append(c.containerMetrics, []containerMetric{
{
name: "container_cpu_schedstat_run_seconds_total", name: "container_cpu_schedstat_run_seconds_total",
help: "Time duration the processes of the container have run on the CPU.", help: "Time duration the processes of the container have run on the CPU.",
valueType: prometheus.CounterValue, valueType: prometheus.CounterValue,
...@@ -218,7 +229,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -218,7 +229,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
getValues: func(s *info.ContainerStats) metricValues { getValues: func(s *info.ContainerStats) metricValues {
return metricValues{{value: float64(s.Cpu.Schedstat.RunPeriods)}} return metricValues{{value: float64(s.Cpu.Schedstat.RunPeriods)}}
}, },
}, { },
}...)
}
if includedMetrics.Has(container.CpuLoadMetrics) {
c.containerMetrics = append(c.containerMetrics, []containerMetric{
{
name: "container_cpu_load_average_10s", name: "container_cpu_load_average_10s",
help: "Value of container cpu load average over the last 10 seconds.", help: "Value of container cpu load average over the last 10 seconds.",
valueType: prometheus.GaugeValue, valueType: prometheus.GaugeValue,
...@@ -226,6 +242,40 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -226,6 +242,40 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
return metricValues{{value: float64(s.Cpu.LoadAverage)}} return metricValues{{value: float64(s.Cpu.LoadAverage)}}
}, },
}, { }, {
name: "container_tasks_state",
help: "Number of tasks in given state",
extraLabels: []string{"state"},
valueType: prometheus.GaugeValue,
getValues: func(s *info.ContainerStats) metricValues {
return metricValues{
{
value: float64(s.TaskStats.NrSleeping),
labels: []string{"sleeping"},
},
{
value: float64(s.TaskStats.NrRunning),
labels: []string{"running"},
},
{
value: float64(s.TaskStats.NrStopped),
labels: []string{"stopped"},
},
{
value: float64(s.TaskStats.NrUninterruptible),
labels: []string{"uninterruptible"},
},
{
value: float64(s.TaskStats.NrIoWait),
labels: []string{"iowaiting"},
},
}
},
},
}...)
}
if includedMetrics.Has(container.MemoryUsageMetrics) {
c.containerMetrics = append(c.containerMetrics, []containerMetric{
{
name: "container_memory_cache", name: "container_memory_cache",
help: "Number of bytes of page cache memory.", help: "Number of bytes of page cache memory.",
valueType: prometheus.GaugeValue, valueType: prometheus.GaugeValue,
...@@ -240,6 +290,13 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -240,6 +290,13 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
return metricValues{{value: float64(s.Memory.RSS)}} return metricValues{{value: float64(s.Memory.RSS)}}
}, },
}, { }, {
name: "container_memory_mapped_file",
help: "Size of memory mapped files in bytes.",
valueType: prometheus.GaugeValue,
getValues: func(s *info.ContainerStats) metricValues {
return metricValues{{value: float64(s.Memory.MappedFile)}}
},
}, {
name: "container_memory_swap", name: "container_memory_swap",
help: "Container swap usage in bytes.", help: "Container swap usage in bytes.",
valueType: prometheus.GaugeValue, valueType: prometheus.GaugeValue,
...@@ -300,7 +357,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -300,7 +357,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
}, },
} }
}, },
}, { },
}...)
}
if includedMetrics.Has(container.AcceleratorUsageMetrics) {
c.containerMetrics = append(c.containerMetrics, []containerMetric{
{
name: "container_accelerator_memory_total_bytes", name: "container_accelerator_memory_total_bytes",
help: "Total accelerator memory.", help: "Total accelerator memory.",
valueType: prometheus.GaugeValue, valueType: prometheus.GaugeValue,
...@@ -345,7 +407,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -345,7 +407,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
} }
return values return values
}, },
}, { },
}...)
}
if includedMetrics.Has(container.DiskUsageMetrics) {
c.containerMetrics = append(c.containerMetrics, []containerMetric{
{
name: "container_fs_inodes_free", name: "container_fs_inodes_free",
help: "Number of available Inodes", help: "Number of available Inodes",
valueType: prometheus.GaugeValue, valueType: prometheus.GaugeValue,
...@@ -385,7 +452,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -385,7 +452,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
return float64(fs.Usage) return float64(fs.Usage)
}) })
}, },
}, { },
}...)
}
if includedMetrics.Has(container.DiskIOMetrics) {
c.containerMetrics = append(c.containerMetrics, []containerMetric{
{
name: "container_fs_reads_bytes_total", name: "container_fs_reads_bytes_total",
help: "Cumulative count of bytes read", help: "Cumulative count of bytes read",
valueType: prometheus.CounterValue, valueType: prometheus.CounterValue,
...@@ -547,7 +619,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -547,7 +619,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
return float64(fs.WeightedIoTime) / float64(time.Second) return float64(fs.WeightedIoTime) / float64(time.Second)
}) })
}, },
}, { },
}...)
}
if includedMetrics.Has(container.NetworkUsageMetrics) {
c.containerMetrics = append(c.containerMetrics, []containerMetric{
{
name: "container_network_receive_bytes_total", name: "container_network_receive_bytes_total",
help: "Cumulative count of bytes received", help: "Cumulative count of bytes received",
valueType: prometheus.CounterValue, valueType: prometheus.CounterValue,
...@@ -667,7 +744,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -667,7 +744,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
} }
return values return values
}, },
}, { },
}...)
}
if includedMetrics.Has(container.NetworkTcpUsageMetrics) {
c.containerMetrics = append(c.containerMetrics, []containerMetric{
{
name: "container_network_tcp_usage_total", name: "container_network_tcp_usage_total",
help: "tcp connection usage statistic for container", help: "tcp connection usage statistic for container",
valueType: prometheus.GaugeValue, valueType: prometheus.GaugeValue,
...@@ -720,7 +802,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -720,7 +802,12 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
}, },
} }
}, },
}, { },
}...)
}
if includedMetrics.Has(container.NetworkUdpUsageMetrics) {
c.containerMetrics = append(c.containerMetrics, []containerMetric{
{
name: "container_network_udp_usage_total", name: "container_network_udp_usage_total",
help: "udp connection usage statistic for container", help: "udp connection usage statistic for container",
valueType: prometheus.GaugeValue, valueType: prometheus.GaugeValue,
...@@ -745,37 +832,8 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo ...@@ -745,37 +832,8 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCo
}, },
} }
}, },
}, {
name: "container_tasks_state",
help: "Number of tasks in given state",
extraLabels: []string{"state"},
valueType: prometheus.GaugeValue,
getValues: func(s *info.ContainerStats) metricValues {
return metricValues{
{
value: float64(s.TaskStats.NrSleeping),
labels: []string{"sleeping"},
},
{
value: float64(s.TaskStats.NrRunning),
labels: []string{"running"},
},
{
value: float64(s.TaskStats.NrStopped),
labels: []string{"stopped"},
},
{
value: float64(s.TaskStats.NrUninterruptible),
labels: []string{"uninterruptible"},
},
{
value: float64(s.TaskStats.NrIoWait),
labels: []string{"iowaiting"},
},
}
},
}, },
}, }...)
} }
return c return c
...@@ -842,6 +900,19 @@ func DefaultContainerLabels(container *info.ContainerInfo) map[string]string { ...@@ -842,6 +900,19 @@ func DefaultContainerLabels(container *info.ContainerInfo) map[string]string {
return set return set
} }
// BaseContainerLabels implements ContainerLabelsFunc. It only exports the
// container name, first alias, and image name.
func BaseContainerLabels(container *info.ContainerInfo) map[string]string {
set := map[string]string{LabelID: container.Name}
if len(container.Aliases) > 0 {
set[LabelName] = container.Aliases[0]
}
if image := container.Spec.Image; len(image) > 0 {
set[LabelImage] = image
}
return set
}
func (c *PrometheusCollector) collectContainersInfo(ch chan<- prometheus.Metric) { func (c *PrometheusCollector) collectContainersInfo(ch chan<- prometheus.Metric) {
containers, err := c.infoProvider.SubcontainersInfo("/", &info.ContainerInfoRequest{NumStats: 1}) containers, err := c.infoProvider.SubcontainersInfo("/", &info.ContainerInfoRequest{NumStats: 1})
if err != nil { if err != nil {
...@@ -889,6 +960,9 @@ func (c *PrometheusCollector) collectContainersInfo(ch chan<- prometheus.Metric) ...@@ -889,6 +960,9 @@ func (c *PrometheusCollector) collectContainersInfo(ch chan<- prometheus.Metric)
} }
// Now for the actual metrics // Now for the actual metrics
if len(container.Stats) == 0 {
continue
}
stats := container.Stats[0] stats := container.Stats[0]
for _, cm := range c.containerMetrics { for _, cm := range c.containerMetrics {
if cm.condition != nil && !cm.condition(container.Spec) { if cm.condition != nil && !cm.condition(container.Spec) {
......
Copyright 2013-2015, Mesosphere, Inc.
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.
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"client.go",
"doc.go",
"filters.go",
"fixedpoint.go",
"labels.go",
"mesos.pb.go",
"mesos.pb_ffjson.go",
"ranges.go",
"resources.go",
"values.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib",
importpath = "github.com/mesos/mesos-go/api/v1/lib",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/gogo/protobuf/gogoproto:go_default_library",
"//vendor/github.com/gogo/protobuf/proto:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/roles:go_default_library",
"//vendor/github.com/pquerna/ffjson/fflib/v1:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/agent:all-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/client:all-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/debug:all-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding:all-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/httpcli:all-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/recordio:all-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/roles:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"agent.pb.go",
"agent.pb_ffjson.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/agent",
importpath = "github.com/mesos/mesos-go/api/v1/lib/agent",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/gogo/protobuf/gogoproto:go_default_library",
"//vendor/github.com/gogo/protobuf/proto:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib:go_default_library",
"//vendor/github.com/pquerna/ffjson/fflib/v1:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/agent/calls:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"calls.go",
"calls_generated.go",
"gen.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/agent/calls",
importpath = "github.com/mesos/mesos-go/api/v1/lib/agent/calls",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/mesos/mesos-go/api/v1/lib:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/agent:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package calls
import (
"time"
"github.com/mesos/mesos-go/api/v1/lib"
"github.com/mesos/mesos-go/api/v1/lib/agent"
)
func GetHealth() *agent.Call { return &agent.Call{Type: agent.Call_GET_HEALTH} }
func GetFlags() *agent.Call { return &agent.Call{Type: agent.Call_GET_FLAGS} }
func GetVersion() *agent.Call { return &agent.Call{Type: agent.Call_GET_VERSION} }
func GetMetrics(d *time.Duration) (call *agent.Call) {
call = &agent.Call{
Type: agent.Call_GET_METRICS,
GetMetrics: &agent.Call_GetMetrics{},
}
if d != nil {
call.GetMetrics.Timeout = &mesos.DurationInfo{
Nanoseconds: d.Nanoseconds(),
}
}
return
}
func GetLoggingLevel() *agent.Call { return &agent.Call{Type: agent.Call_GET_LOGGING_LEVEL} }
func SetLoggingLevel(level uint32, d time.Duration) *agent.Call {
return &agent.Call{
Type: agent.Call_SET_LOGGING_LEVEL,
SetLoggingLevel: &agent.Call_SetLoggingLevel{
Duration: mesos.DurationInfo{Nanoseconds: d.Nanoseconds()},
Level: level,
},
}
}
func ListFiles(path string) *agent.Call {
return &agent.Call{
Type: agent.Call_LIST_FILES,
ListFiles: &agent.Call_ListFiles{
Path: path,
},
}
}
func ReadFile(path string, offset uint64) *agent.Call {
return &agent.Call{
Type: agent.Call_READ_FILE,
ReadFile: &agent.Call_ReadFile{
Path: path,
Offset: offset,
},
}
}
func ReadFileWithLength(path string, offset, length uint64) (call *agent.Call) {
call = ReadFile(path, offset)
call.ReadFile.Length = &length
return
}
func GetState() *agent.Call { return &agent.Call{Type: agent.Call_GET_STATE} }
func GetContainers() *agent.Call { return &agent.Call{Type: agent.Call_GET_CONTAINERS} }
func GetFrameworks() *agent.Call { return &agent.Call{Type: agent.Call_GET_FRAMEWORKS} }
func GetExecutors() *agent.Call { return &agent.Call{Type: agent.Call_GET_EXECUTORS} }
func GetOperations() *agent.Call { return &agent.Call{Type: agent.Call_GET_OPERATIONS} }
func GetTasks() *agent.Call { return &agent.Call{Type: agent.Call_GET_TASKS} }
func GetAgent() *agent.Call { return &agent.Call{Type: agent.Call_GET_AGENT} }
func GetResourceProviders() *agent.Call { return &agent.Call{Type: agent.Call_GET_RESOURCE_PROVIDERS} }
func LaunchNestedContainer(cid mesos.ContainerID, cmd *mesos.CommandInfo, ci *mesos.ContainerInfo) *agent.Call {
return &agent.Call{
Type: agent.Call_LAUNCH_NESTED_CONTAINER,
LaunchNestedContainer: &agent.Call_LaunchNestedContainer{
ContainerID: cid,
Command: cmd,
Container: ci,
},
}
}
func LaunchContainer(cid mesos.ContainerID, cmd *mesos.CommandInfo, ci *mesos.ContainerInfo, r []mesos.Resource) *agent.Call {
return &agent.Call{
Type: agent.Call_LAUNCH_CONTAINER,
LaunchContainer: &agent.Call_LaunchContainer{
ContainerID: cid,
Command: cmd,
Container: ci,
Resources: r,
},
}
}
func WaitNestedContainer(cid mesos.ContainerID) *agent.Call {
return &agent.Call{
Type: agent.Call_WAIT_NESTED_CONTAINER,
WaitNestedContainer: &agent.Call_WaitNestedContainer{
ContainerID: cid,
},
}
}
func WaitContainer(cid mesos.ContainerID) *agent.Call {
return &agent.Call{
Type: agent.Call_WAIT_CONTAINER,
WaitContainer: &agent.Call_WaitContainer{
ContainerID: cid,
},
}
}
func KillNestedContainer(cid mesos.ContainerID) *agent.Call {
return &agent.Call{
Type: agent.Call_KILL_NESTED_CONTAINER,
KillNestedContainer: &agent.Call_KillNestedContainer{
ContainerID: cid,
},
}
}
func KillContainer(cid mesos.ContainerID) *agent.Call {
return &agent.Call{
Type: agent.Call_KILL_CONTAINER,
KillContainer: &agent.Call_KillContainer{
ContainerID: cid,
},
}
}
func RemoveNestedContainer(cid mesos.ContainerID) *agent.Call {
return &agent.Call{
Type: agent.Call_REMOVE_NESTED_CONTAINER,
RemoveNestedContainer: &agent.Call_RemoveNestedContainer{
ContainerID: cid,
},
}
}
func RemoveContainer(cid mesos.ContainerID) *agent.Call {
return &agent.Call{
Type: agent.Call_REMOVE_CONTAINER,
RemoveContainer: &agent.Call_RemoveContainer{
ContainerID: cid,
},
}
}
func LaunchNestedContainerSession(cid mesos.ContainerID, cmd *mesos.CommandInfo, ci *mesos.ContainerInfo) *agent.Call {
return &agent.Call{
Type: agent.Call_LAUNCH_NESTED_CONTAINER_SESSION,
LaunchNestedContainerSession: &agent.Call_LaunchNestedContainerSession{
ContainerID: cid,
Command: cmd,
Container: ci,
},
}
}
func AttachContainerOutput(cid mesos.ContainerID) *agent.Call {
return &agent.Call{
Type: agent.Call_ATTACH_CONTAINER_OUTPUT,
AttachContainerOutput: &agent.Call_AttachContainerOutput{
ContainerID: cid,
},
}
}
// AttachContainerInput returns a Call that is used to initiate attachment to a container's stdin.
// Callers should first send this Call followed by one or more AttachContainerInputXxx calls.
func AttachContainerInput(cid mesos.ContainerID) *agent.Call {
return &agent.Call{
Type: agent.Call_ATTACH_CONTAINER_INPUT,
AttachContainerInput: &agent.Call_AttachContainerInput{
Type: agent.Call_AttachContainerInput_CONTAINER_ID,
ContainerID: &cid,
},
}
}
func AttachContainerInputData(data []byte) *agent.Call {
return &agent.Call{
Type: agent.Call_ATTACH_CONTAINER_INPUT,
AttachContainerInput: &agent.Call_AttachContainerInput{
Type: agent.Call_AttachContainerInput_PROCESS_IO,
ProcessIO: &agent.ProcessIO{
Type: agent.ProcessIO_DATA,
Data: &agent.ProcessIO_Data{
Type: agent.ProcessIO_Data_STDIN,
Data: data,
},
},
},
}
}
func AttachContainerInputTTY(t *mesos.TTYInfo) *agent.Call {
return &agent.Call{
Type: agent.Call_ATTACH_CONTAINER_INPUT,
AttachContainerInput: &agent.Call_AttachContainerInput{
Type: agent.Call_AttachContainerInput_PROCESS_IO,
ProcessIO: &agent.ProcessIO{
Type: agent.ProcessIO_CONTROL,
Control: &agent.ProcessIO_Control{
Type: agent.ProcessIO_Control_TTY_INFO,
TTYInfo: t,
},
},
},
}
}
func AddResourceProviderConfig(rpi mesos.ResourceProviderInfo) *agent.Call {
return &agent.Call{
Type: agent.Call_ADD_RESOURCE_PROVIDER_CONFIG,
AddResourceProviderConfig: &agent.Call_AddResourceProviderConfig{
Info: rpi,
},
}
}
func UpdateResourceProviderConfig(rpi mesos.ResourceProviderInfo) *agent.Call {
return &agent.Call{
Type: agent.Call_UPDATE_RESOURCE_PROVIDER_CONFIG,
UpdateResourceProviderConfig: &agent.Call_UpdateResourceProviderConfig{
Info: rpi,
},
}
}
func RemoveResourceProviderConfig(typ, name string) *agent.Call {
return &agent.Call{
Type: agent.Call_REMOVE_RESOURCE_PROVIDER_CONFIG,
RemoveResourceProviderConfig: &agent.Call_RemoveResourceProviderConfig{
Type: typ,
Name: name,
},
}
}
func PruneImages(excluded []mesos.Image) *agent.Call {
return &agent.Call{
Type: agent.Call_PRUNE_IMAGES,
PruneImages: &agent.Call_PruneImages{
ExcludedImages: excluded,
},
}
}
package calls
// go generate -import github.com/mesos/mesos-go/api/v1/lib/agent -type C:agent.Call
// GENERATED CODE FOLLOWS; DO NOT EDIT.
import (
"context"
"github.com/mesos/mesos-go/api/v1/lib"
"github.com/mesos/mesos-go/api/v1/lib/encoding"
"github.com/mesos/mesos-go/api/v1/lib/agent"
)
type (
// Request generates a Call that's sent to a Mesos agent. Subsequent invocations are expected to
// yield equivalent calls. Intended for use w/ non-streaming requests to an agent.
Request interface {
Call() *agent.Call
}
// RequestFunc is the functional adaptation of Request.
RequestFunc func() *agent.Call
// RequestStreaming generates a Call that's send to a Mesos agent. Subsequent invocations MAY generate
// different Call objects. No more Call objects are expected once a nil is returned to signal the end of
// of the request stream.
RequestStreaming interface {
Request
IsStreaming()
}
// RequestStreamingFunc is the functional adaptation of RequestStreaming.
RequestStreamingFunc func() *agent.Call
// Send issues a Request to a Mesos agent and properly manages Call-specific mechanics.
Sender interface {
Send(context.Context, Request) (mesos.Response, error)
}
// SenderFunc is the functional adaptation of the Sender interface
SenderFunc func(context.Context, Request) (mesos.Response, error)
)
func (f RequestFunc) Call() *agent.Call { return f() }
func (f RequestFunc) Marshaler() encoding.Marshaler {
// avoid returning (*agent.Call)(nil) for interface type
if call := f(); call != nil {
return call
}
return nil
}
func (f RequestStreamingFunc) Push(c ...*agent.Call) RequestStreamingFunc { return Push(f, c...) }
func (f RequestStreamingFunc) Marshaler() encoding.Marshaler {
// avoid returning (*agent.Call)(nil) for interface type
if call := f(); call != nil {
return call
}
return nil
}
func (f RequestStreamingFunc) IsStreaming() {}
func (f RequestStreamingFunc) Call() *agent.Call { return f() }
// Push prepends one or more calls onto a request stream. If no calls are given then the original stream is returned.
func Push(r RequestStreaming, c ...*agent.Call) RequestStreamingFunc {
return func() *agent.Call {
if len(c) == 0 {
return r.Call()
}
head := c[0]
c = c[1:]
return head
}
}
// Empty generates a stream that always returns nil.
func Empty() RequestStreamingFunc { return func() *agent.Call { return nil } }
var (
_ = Request(RequestFunc(nil))
_ = RequestStreaming(RequestStreamingFunc(nil))
_ = Sender(SenderFunc(nil))
)
// NonStreaming returns a RequestFunc that always generates the same Call.
func NonStreaming(c *agent.Call) RequestFunc { return func() *agent.Call { return c } }
// FromChan returns a streaming request that fetches calls from the given channel until it closes.
// If a nil chan is specified then the returned func will always generate nil.
func FromChan(ch <-chan *agent.Call) RequestStreamingFunc {
if ch == nil {
// avoid blocking forever if we're handed a nil chan
return func() *agent.Call { return nil }
}
return func() *agent.Call {
if m, ok := <-ch; ok {
return m
}
return nil
}
}
// Send implements the Sender interface for SenderFunc
func (f SenderFunc) Send(ctx context.Context, r Request) (mesos.Response, error) {
return f(ctx, r)
}
// IgnoreResponse generates a sender that closes any non-nil response received by Mesos.
func IgnoreResponse(s Sender) SenderFunc {
return func(ctx context.Context, r Request) (mesos.Response, error) {
resp, err := s.Send(ctx, r)
if resp != nil {
resp.Close()
}
return nil, err
}
}
// SendNoData is a convenience func that executes the given Call using the provided Sender
// and always drops the response data.
func SendNoData(ctx context.Context, sender Sender, r Request) (err error) {
_, err = IgnoreResponse(sender).Send(ctx, r)
return
}
package calls
//go:generate go run ../../extras/gen/sender.go ../../extras/gen/gen.go -import github.com/mesos/mesos-go/api/v1/lib/agent -type C:agent.Call
package mesos
// DEPRECATED in favor of github.com/mesos/mesos-go/api/v1/lib/client
import (
"io"
"github.com/mesos/mesos-go/api/v1/lib/encoding"
)
// A Client represents a Mesos API client which can send Calls and return
// a streaming Decoder from which callers can read Events from, an io.Closer to
// close the event stream on graceful termination and an error in case of failure.
type Client interface {
Do(encoding.Marshaler) (Response, error)
}
// ClientFunc is a functional adapter of the Client interface
type ClientFunc func(encoding.Marshaler) (Response, error)
// Do implements Client
func (cf ClientFunc) Do(m encoding.Marshaler) (Response, error) { return cf(m) }
// Response captures the output of a Mesos API operation. Callers are responsible for invoking
// Close when they're finished processing the response otherwise there may be connection leaks.
type Response interface {
io.Closer
encoding.Decoder
}
// ResponseDecorator optionally modifies the behavior of a Response
type ResponseDecorator interface {
Decorate(Response) Response
}
// ResponseDecoratorFunc is the functional adapter for ResponseDecorator
type ResponseDecoratorFunc func(Response) Response
func (f ResponseDecoratorFunc) Decorate(r Response) Response { return f(r) }
// CloseFunc is the functional adapter for io.Closer
type CloseFunc func() error
// Close implements io.Closer
func (f CloseFunc) Close() error { return f() }
// ResponseWrapper delegates to optional overrides for invocations of Response methods.
type ResponseWrapper struct {
Response Response
Closer io.Closer
Decoder encoding.Decoder
}
func (wrapper *ResponseWrapper) Close() error {
if wrapper.Closer != nil {
return wrapper.Closer.Close()
}
if wrapper.Response != nil {
return wrapper.Response.Close()
}
return nil
}
func (wrapper *ResponseWrapper) Decode(u encoding.Unmarshaler) error {
if wrapper.Decoder != nil {
return wrapper.Decoder.Decode(u)
}
return wrapper.Response.Decode(u)
}
var _ = Response(&ResponseWrapper{})
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["client.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/client",
importpath = "github.com/mesos/mesos-go/api/v1/lib/client",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package client
import (
"github.com/mesos/mesos-go/api/v1/lib/encoding"
)
type (
// ResponseClass indicates the kind of response that a caller is expecting from Mesos.
ResponseClass int
// Request is a non-streaming request from the client to the server.
// Marshaler always returns the same object; the object is sent once to the server and then
// a response is expected.
Request interface {
Marshaler() encoding.Marshaler
}
// RequestStreaming is a streaming request from the client to the server.
// Marshaler returns a new object for upon each invocation, nil when there are no more objects to send.
// Client implementations are expected to differentiate between Request and RequestStreaming either by
// type-switching or by attempting interface conversion.
RequestStreaming interface {
Request
IsStreaming()
}
RequestFunc func() encoding.Marshaler
RequestStreamingFunc func() encoding.Marshaler
)
var (
_ = Request(RequestFunc(nil))
_ = RequestStreaming(RequestStreamingFunc(nil))
)
func (f RequestFunc) Marshaler() encoding.Marshaler { return f() }
func (f RequestStreamingFunc) Marshaler() encoding.Marshaler { return f() }
func (f RequestStreamingFunc) IsStreaming() {}
// RequestSingleton generates a non-streaming Request that always returns the same marshaler
func RequestSingleton(m encoding.Marshaler) Request {
return RequestFunc(func() encoding.Marshaler { return m })
}
const (
ResponseClassSingleton ResponseClass = iota
ResponseClassStreaming
ResponseClassNoData
// ResponseClassAuto should be used with versions of Mesos prior to 1.2.x.
// Otherwise, this type is deprecated and callers should use ResponseClassSingleton
// or ResponseClassStreaming instead.
ResponseClassAuto
)
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["logger.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/debug",
importpath = "github.com/mesos/mesos-go/api/v1/lib/debug",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package debug
import "log"
type Logger bool
func (d Logger) Log(v ...interface{}) {
if d {
log.Print(v...)
}
}
func (d Logger) Logf(s string, v ...interface{}) {
if d {
log.Printf(s, v...)
}
}
// Package mesos presents common v1 HTTP API message types in addition to extension APIs that
// aim to simplify use of the machine-generated code.
package mesos
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["types.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/encoding",
importpath = "github.com/mesos/mesos-go/api/v1/lib/encoding",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/gogo/protobuf/proto:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/framing:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/codecs:all-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/framing:all-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/json:all-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/proto:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["codecs.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/codecs",
importpath = "github.com/mesos/mesos-go/api/v1/lib/encoding/codecs",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/json:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/proto:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package codecs
import (
"github.com/mesos/mesos-go/api/v1/lib/encoding"
"github.com/mesos/mesos-go/api/v1/lib/encoding/json"
"github.com/mesos/mesos-go/api/v1/lib/encoding/proto"
)
const (
// MediaTypeProtobuf is the Protobuf serialization format media type.
MediaTypeProtobuf = encoding.MediaType("application/x-protobuf")
// MediaTypeJSON is the JSON serialiation format media type.
MediaTypeJSON = encoding.MediaType("application/json")
NameProtobuf = "protobuf"
NameJSON = "json"
)
// ByMediaType are pre-configured default Codecs, ready to use OOTB
var ByMediaType = map[encoding.MediaType]encoding.Codec{
MediaTypeProtobuf: encoding.Codec{
Name: NameProtobuf,
Type: MediaTypeProtobuf,
NewEncoder: proto.NewEncoder,
NewDecoder: proto.NewDecoder,
},
MediaTypeJSON: encoding.Codec{
Name: NameJSON,
Type: MediaTypeJSON,
NewEncoder: json.NewEncoder,
NewDecoder: json.NewDecoder,
},
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"decoder.go",
"framing.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/framing",
importpath = "github.com/mesos/mesos-go/api/v1/lib/encoding/framing",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package framing
type (
// UnmarshalFunc translates bytes to objects
UnmarshalFunc func([]byte, interface{}) error
// Decoder reads and decodes Protobuf messages from an io.Reader.
Decoder interface {
// Decode reads the next encoded message from its input and stores it
// in the value pointed to by m. If m isn't a proto.Message, Decode will panic.
Decode(interface{}) error
}
// DecoderFunc is the functional adaptation of Decoder
DecoderFunc func(interface{}) error
)
func (f DecoderFunc) Decode(m interface{}) error { return f(m) }
var _ = Decoder(DecoderFunc(nil))
// NewDecoder returns a new Decoder that reads from the given frame Reader.
func NewDecoder(r Reader, uf UnmarshalFunc) DecoderFunc {
return func(m interface{}) error {
// Note: the buf returned by ReadFrame will change over time, it can't be sub-sliced
// and then those sub-slices retained. Examination of generated proto code seems to indicate
// that byte buffers are copied vs. referenced by sub-slice (gogo protoc).
frame, err := r.ReadFrame()
if err != nil {
return err
}
return uf(frame, m)
}
}
package framing
import (
"io"
"io/ioutil"
)
type Error string
func (err Error) Error() string { return string(err) }
const (
ErrorUnderrun = Error("frame underrun, unexpected EOF")
ErrorBadSize = Error("bad frame size")
ErrorOversizedFrame = Error("oversized frame, max size exceeded")
)
type (
// Reader generates data frames from some source, returning io.EOF when the end of the input stream is
// detected.
Reader interface {
ReadFrame() (frame []byte, err error)
}
// ReaderFunc is the functional adaptation of Reader.
ReaderFunc func() ([]byte, error)
// Writer sends whole frames to some endpoint; returns io.ErrShortWrite if the frame is only partially written.
Writer interface {
WriteFrame(frame []byte) error
}
// WriterFunc is the functional adaptation of Writer.
WriterFunc func([]byte) error
)
func (f ReaderFunc) ReadFrame() ([]byte, error) { return f() }
func (f WriterFunc) WriteFrame(b []byte) error { return f(b) }
var _ = Reader(ReaderFunc(nil))
var _ = Writer(WriterFunc(nil))
// EOFReaderFunc always returns nil, io.EOF; it implements the ReaderFunc API.
func EOFReaderFunc() ([]byte, error) { return nil, io.EOF }
var _ = ReaderFunc(EOFReaderFunc) // sanity check
// ReadAll returns a reader func that returns the complete contents of `r` in a single frame.
// A zero length frame is treated as an "end of stream" condition, returning io.EOF.
func ReadAll(r io.Reader) ReaderFunc {
return func() (b []byte, err error) {
b, err = ioutil.ReadAll(r)
if len(b) == 0 && err == nil {
err = io.EOF
}
return
}
}
// WriterFor adapts an io.Writer to the Writer interface. All buffers are written to `w` without decoration or
// modification.
func WriterFor(w io.Writer) WriterFunc {
return func(b []byte) error {
n, err := w.Write(b)
if err == nil && n != len(b) {
return io.ErrShortWrite
}
return err
}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["json.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/json",
importpath = "github.com/mesos/mesos-go/api/v1/lib/encoding/json",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/framing:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package json
import (
"encoding/json"
"github.com/mesos/mesos-go/api/v1/lib/encoding"
"github.com/mesos/mesos-go/api/v1/lib/encoding/framing"
)
// NewEncoder returns a new Encoder of Calls to JSON messages written to
// the given io.Writer.
func NewEncoder(s encoding.Sink) encoding.Encoder {
w := s()
return encoding.EncoderFunc(func(m encoding.Marshaler) error {
b, err := json.Marshal(m)
if err != nil {
return err
}
return w.WriteFrame(b)
})
}
// NewDecoder returns a new Decoder of JSON messages read from the given source.
func NewDecoder(s encoding.Source) encoding.Decoder {
r := s()
dec := framing.NewDecoder(r, json.Unmarshal)
return encoding.DecoderFunc(func(u encoding.Unmarshaler) error { return dec.Decode(u) })
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"encoding.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/proto",
importpath = "github.com/mesos/mesos-go/api/v1/lib/encoding/proto",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/gogo/protobuf/proto:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/framing:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package proto implements protobuf utilities such as functional options to
// construct complex structs and encoders and decoders composable with
// io.ReadWriters.
package proto
package proto
import (
"github.com/gogo/protobuf/proto"
"github.com/mesos/mesos-go/api/v1/lib/encoding"
"github.com/mesos/mesos-go/api/v1/lib/encoding/framing"
)
// NewEncoder returns a new Encoder of Calls to Protobuf messages written to
// the given io.Writer.
func NewEncoder(s encoding.Sink) encoding.Encoder {
w := s()
return encoding.EncoderFunc(func(m encoding.Marshaler) error {
b, err := proto.Marshal(m.(proto.Message))
if err != nil {
return err
}
return w.WriteFrame(b)
})
}
// NewDecoder returns a new Decoder of Protobuf messages read from the given Source.
func NewDecoder(s encoding.Source) encoding.Decoder {
r := s()
var (
uf = func(b []byte, m interface{}) error { return proto.Unmarshal(b, m.(proto.Message)) }
dec = framing.NewDecoder(r, uf)
)
return encoding.DecoderFunc(func(u encoding.Unmarshaler) error { return dec.Decode(u) })
}
package encoding
import (
"encoding/json"
"io"
pb "github.com/gogo/protobuf/proto"
"github.com/mesos/mesos-go/api/v1/lib/encoding/framing"
)
type MediaType string
// ContentType returns the HTTP Content-Type associated with the MediaType
func (m MediaType) ContentType() string { return string(m) }
type (
Source func() framing.Reader
Sink func() framing.Writer
// A Codec composes encoding and decoding of a serialization format.
Codec struct {
Name string
Type MediaType
NewEncoder func(Sink) Encoder
NewDecoder func(Source) Decoder
}
SourceFactory interface {
NewSource(r io.Reader) Source
}
SourceFactoryFunc func(r io.Reader) Source
SinkFactory interface {
NewSink(w io.Writer) Sink
}
SinkFactoryFunc func(w io.Writer) Sink
)
func (f SourceFactoryFunc) NewSource(r io.Reader) Source { return f(r) }
func (f SinkFactoryFunc) NewSink(w io.Writer) Sink { return f(w) }
var (
_ = SourceFactory(SourceFactoryFunc(nil))
_ = SinkFactory(SinkFactoryFunc(nil))
)
// SourceReader returns a Source that buffers all input from the given io.Reader
// and returns the contents in a single frame.
func SourceReader(r io.Reader) Source {
ch := make(chan framing.ReaderFunc, 1)
ch <- framing.ReadAll(r)
return func() framing.Reader {
select {
case f := <-ch:
return f
default:
return framing.ReaderFunc(framing.EOFReaderFunc)
}
}
}
// SinkWriter returns a Sink that sends a frame to an io.Writer with no decoration.
func SinkWriter(w io.Writer) Sink { return func() framing.Writer { return framing.WriterFor(w) } }
// String implements the fmt.Stringer interface.
func (c *Codec) String() string {
if c == nil {
return ""
}
return c.Name
}
type (
// Marshaler composes the supported marshaling formats.
Marshaler interface {
pb.Marshaler
json.Marshaler
}
// Unmarshaler composes the supporter unmarshaling formats.
Unmarshaler interface {
pb.Unmarshaler
json.Unmarshaler
}
// An Encoder encodes a given Marshaler or returns an error in case of failure.
Encoder interface {
Encode(Marshaler) error
}
// EncoderFunc is the functional adapter for Encoder
EncoderFunc func(Marshaler) error
// A Decoder decodes a given Unmarshaler or returns an error in case of failure.
Decoder interface {
Decode(Unmarshaler) error
}
// DecoderFunc is the functional adapter for Decoder
DecoderFunc func(Unmarshaler) error
)
// Decode implements the Decoder interface
func (f DecoderFunc) Decode(u Unmarshaler) error { return f(u) }
// Encode implements the Encoder interface
func (f EncoderFunc) Encode(m Marshaler) error { return f(m) }
var (
_ = Encoder(EncoderFunc(nil))
_ = Decoder(DecoderFunc(nil))
)
package mesos
import "time"
type FilterOpt func(*Filters)
func (f *Filters) With(opts ...FilterOpt) *Filters {
for _, o := range opts {
o(f)
}
return f
}
func RefuseSeconds(d time.Duration) FilterOpt {
return func(f *Filters) {
s := d.Seconds()
f.RefuseSeconds = &s
}
}
func OptionalFilters(fo ...FilterOpt) *Filters {
if len(fo) == 0 {
return nil
}
return (&Filters{}).With(fo...)
}
package mesos
// fixed point scalar math from mesos:src/common/values.cpp
// --
// We manipulate scalar values by converting them from floating point to a
// fixed point representation, doing a calculation, and then converting
// the result back to floating point. We deliberately only preserve three
// decimal digits of precision in the fixed point representation. This
// ensures that client applications see predictable numerical behavior, at
// the expense of sacrificing some precision.
import "math"
func convertToFloat64(f int64) float64 {
// NOTE: We do the conversion from fixed point via integer division
// and then modulus, rather than a single floating point division.
// This ensures that we only apply floating point division to inputs
// in the range [0,999], which is easier to check for correctness.
var (
quotient = float64(f / 1000)
remainder = float64(f%1000) / 1000.0
)
return quotient + remainder
}
func convertToFixed64(f float64) int64 {
return round64(f * 1000)
}
func round64(f float64) int64 {
if math.Abs(f) < 0.5 {
return 0
}
return int64(f + math.Copysign(0.5, f))
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"auth_basic.go",
"http.go",
"opts.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/httpcli",
importpath = "github.com/mesos/mesos-go/api/v1/lib/httpcli",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/mesos/mesos-go/api/v1/lib:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/client:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/debug:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/codecs:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/framing:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/httpcli/apierrors:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/recordio:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/httpcli/apierrors:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["apierrors.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/httpcli/apierrors",
importpath = "github.com/mesos/mesos-go/api/v1/lib/httpcli/apierrors",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package apierrors
import (
"io"
"io/ioutil"
"net/http"
)
// Code is a Mesos HTTP v1 API response status code
type Code int
const (
// MsgNotLeader is returned by Do calls that are sent to a non leading Mesos master.
MsgNotLeader = "call sent to a non-leading master"
// MsgAuth is returned by Do calls that are not successfully authenticated.
MsgAuth = "call not authenticated"
// MsgUnsubscribed is returned by Do calls that are sent before a subscription is established.
MsgUnsubscribed = "no subscription established"
// MsgVersion is returned by Do calls that are sent to an incompatible API version.
MsgVersion = "incompatible API version"
// MsgMalformed is returned by Do calls that are malformed.
MsgMalformed = "malformed request"
// MsgMediaType is returned by Do calls that are sent with an unsupported media type.
MsgMediaType = "unsupported media type"
// MsgRateLimit is returned by Do calls that are rate limited. This is a temporary condition
// that should clear.
MsgRateLimit = "rate limited"
// MsgUnavailable is returned by Do calls that are sent to a master or agent that's in recovery, or
// does not yet realize that it's the leader. This is a temporary condition that should clear.
MsgUnavailable = "mesos server unavailable"
// MsgNotFound could happen if the master or agent libprocess has not yet set up http routes.
MsgNotFound = "mesos http endpoint not found"
CodeNotLeader = Code(http.StatusTemporaryRedirect)
CodeNotAuthenticated = Code(http.StatusUnauthorized)
CodeUnsubscribed = Code(http.StatusForbidden)
CodeIncompatibleVersion = Code(http.StatusConflict)
CodeMalformedRequest = Code(http.StatusBadRequest)
CodeUnsupportedMediaType = Code(http.StatusNotAcceptable)
CodeRateLimitExceeded = Code(http.StatusTooManyRequests)
CodeMesosUnavailable = Code(http.StatusServiceUnavailable)
CodeNotFound = Code(http.StatusNotFound)
MaxSizeDetails = 4 * 1024 // MaxSizeDetails limits the length of the details message read from a response body
)
var (
// ErrorTable maps HTTP response codes to their respective Mesos v1 API error messages.
ErrorTable = map[Code]string{
CodeNotLeader: MsgNotLeader,
CodeMalformedRequest: MsgMalformed,
CodeIncompatibleVersion: MsgVersion,
CodeUnsubscribed: MsgUnsubscribed,
CodeNotAuthenticated: MsgAuth,
CodeUnsupportedMediaType: MsgMediaType,
CodeNotFound: MsgNotFound,
CodeMesosUnavailable: MsgUnavailable,
CodeRateLimitExceeded: MsgRateLimit,
}
)
// Error captures HTTP v1 API error codes and messages generated by Mesos.
type Error struct {
code Code // code is the HTTP response status code generated by Mesos
message string // message briefly summarizes the nature of the error, possibly includes details from Mesos
}
// IsError returns true for all HTTP status codes that are not considered informational or successful.
func (code Code) IsError() bool {
return code >= 300
}
// FromResponse returns an `*Error` for a response containing a status code that indicates an error condition.
// The response body (if any) is captured in the Error.Details field.
// Returns nil for nil responses and responses with non-error status codes.
// See IsErrorCode.
func FromResponse(res *http.Response) error {
if res == nil {
return nil
}
code := Code(res.StatusCode)
if !code.IsError() {
// non-error HTTP response codes don't generate errors
return nil
}
var details string
if res.Body != nil {
defer res.Body.Close()
buf, _ := ioutil.ReadAll(io.LimitReader(res.Body, MaxSizeDetails))
details = string(buf)
}
return code.Error(details)
}
// Error generates an error from the given status code and detail string.
func (code Code) Error(details string) error {
if !code.IsError() {
return nil
}
err := &Error{
code: code,
message: ErrorTable[code],
}
if details != "" {
err.message = err.message + ": " + details
}
return err
}
// Error implements error interface
func (e *Error) Error() string { return e.message }
// Temporary returns true if the error is a temporary condition that should eventually clear.
func (e *Error) Temporary() bool {
switch e.code {
// TODO(jdef): NotFound **could** be a temporary error because there's a race at mesos startup in which the
// HTTP server responds before the internal listeners have been initialized. But it could also be reported
// because the client is accessing an invalid endpoint; as of right now, a client cannot distinguish between
// these cases.
// https://issues.apache.org/jira/browse/MESOS-7697
case CodeRateLimitExceeded, CodeMesosUnavailable:
return true
default:
return false
}
}
// CodesIndicatingSubscriptionLoss is a set of apierror.Code entries which each indicate that
// the event subscription stream has been severed between the scheduler and mesos. It's respresented
// as a public map variable so that clients can program additional error codes (if such are discovered)
// without hacking the code of the mesos-go library directly.
var CodesIndicatingSubscriptionLoss = func(codes ...Code) map[Code]struct{} {
result := make(map[Code]struct{}, len(codes))
for _, code := range codes {
result[code] = struct{}{}
}
return result
}(
// expand this list as we discover other errors that guarantee we've lost our event subscription.
CodeUnsubscribed,
)
// SubscriptionLoss returns true if the error indicates that the event subscription stream has been severed
// between mesos and a mesos client.
func (e *Error) SubscriptionLoss() (result bool) {
_, result = CodesIndicatingSubscriptionLoss[e.code]
return
}
// Matches returns true if the given error is an API error with a matching error code
func (code Code) Matches(err error) bool {
if err == nil {
return !code.IsError()
}
apiErr, ok := err.(*Error)
return ok && apiErr.code == code
}
package httpcli
import (
"net/http"
)
// roundTripperFunc is the functional adaptation of http.RoundTripper
type roundTripperFunc func(*http.Request) (*http.Response, error)
// RoundTrip implements RoundTripper for roundTripperFunc
func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) }
// BasicAuth generates a functional config option that sets HTTP Basic authentication for a Client
func BasicAuth(username, passwd string) ConfigOpt {
// TODO(jdef) this could be more efficient. according to the stdlib we're not supposed to
// mutate the original Request, so we copy here (including headers). another approach would
// be to generate a functional RequestOpt that adds the right header.
return WrapRoundTripper(func(rt http.RoundTripper) http.RoundTripper {
return roundTripperFunc(func(req *http.Request) (*http.Response, error) {
var h http.Header
if req.Header != nil {
h = make(http.Header, len(req.Header))
for k, v := range req.Header {
h[k] = append(make([]string, 0, len(v)), v...)
}
}
clonedReq := *req
clonedReq.Header = h
clonedReq.SetBasicAuth(username, passwd)
return rt.RoundTrip(&clonedReq)
})
})
}
package httpcli
type (
// Opt defines a functional option for the HTTP client type. A functional option
// must return an Opt that acts as an "undo" if applied to the same Client.
Opt func(*Client) Opt
// Opts represents a series of functional options
Opts []Opt
)
// Apply is a nil-safe application of an Opt: if the receiver is nil then this func
// simply returns nil, otherwise it returns the result invoking the receiving Opt
// with the given Client.
func (o Opt) Apply(c *Client) (result Opt) {
if o != nil {
result = o(c)
}
return
}
// Merged generates a single Opt that applies all the functional options, in-order
func (opts Opts) Merged() Opt {
if len(opts) == 0 {
return nil
}
return func(c *Client) Opt {
var (
size = len(opts)
undo = make(Opts, size)
)
size-- // make this a zero-based offset
for i, opt := range opts {
if opt != nil {
undo[size-i] = opt(c)
}
}
return undo.Merged()
}
}
// And combines two functional options into a single Opt
func (o Opt) And(other Opt) Opt {
if o == nil {
if other == nil {
return nil
}
return other
}
if other == nil {
return o
}
return Opts{o, other}.Merged()
}
package mesos
import (
"bytes"
"io"
)
type labelList []Label // convenience type, for working with unwrapped Label slices
// Equivalent returns true if left and right have the same labels. Order is not important.
func (left *Labels) Equivalent(right *Labels) bool {
return labelList(left.GetLabels()).Equivalent(labelList(right.GetLabels()))
}
// Equivalent returns true if left and right have the same labels. Order is not important.
func (left labelList) Equivalent(right labelList) bool {
if len(left) != len(right) {
return false
} else {
for i := range left {
found := false
for j := range right {
if left[i].Equivalent(right[j]) {
found = true
break
}
}
if !found {
return false
}
}
return true
}
}
// Equivalent returns true if left and right represent the same Label.
func (left Label) Equivalent(right Label) bool {
if left.Key != right.Key {
return false
}
if left.Value == nil {
return right.Value == nil
} else {
return right.Value != nil && *left.Value == *right.Value
}
}
func (left Label) writeTo(w io.Writer) (n int64, err error) {
write := func(s string) {
if err != nil {
return
}
var n2 int
n2, err = io.WriteString(w, s)
n += int64(n2)
}
write(left.Key)
if s := left.GetValue(); s != "" {
write("=")
write(s)
}
return
}
func (left *Labels) writeTo(w io.Writer) (n int64, err error) {
var (
lab = left.GetLabels()
n2 int
n3 int64
)
for i := range lab {
if i > 0 {
n2, err = io.WriteString(w, ",")
n += int64(n2)
if err != nil {
break
}
}
n3, err = lab[i].writeTo(w)
n += n3
if err != nil {
break
}
}
return
}
func (left *Labels) Format() string {
if left == nil {
return ""
}
var b bytes.Buffer
left.writeTo(&b)
return b.String()
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
package mesos
import (
"sort"
)
// Ranges represents a list of Ranges.
type Ranges []Value_Range
// NewRanges returns squashed Ranges from the given numbers.
func NewRanges(ns ...uint64) Ranges {
xs := append(uint64s{}, ns...)
sort.Sort(xs)
rs := make(Ranges, len(xs))
for i := range xs {
rs[i].Begin, rs[i].End = xs[i], xs[i]
}
return rs.Squash()
}
// NewPortRanges returns Ranges from the "ports" resource in the
// given *Offer. If that resource isn't provided, nil will be returned.
//
// The returned Ranges are sorted and have all overlapping ranges merged from
// left to right. e.g. [[0, 5], [4, 3], [10, 7]] -> [[0, 5], [7, 10]]
func NewPortRanges(o *Offer) Ranges {
if o == nil {
return Ranges{}
}
var (
r Resource
found bool
)
for i := range o.Resources {
if o.Resources[i].GetName() == "ports" {
r = o.Resources[i]
found = true
break
}
}
if !found {
return Ranges{}
}
offered := r.GetRanges().GetRange()
rs := make(Ranges, len(offered))
for i, r := range offered {
if lo, hi := r.GetBegin(), r.GetEnd(); lo <= hi {
rs[i].Begin, rs[i].End = lo, hi
} else {
rs[i].Begin, rs[i].End = hi, lo
}
}
return rs.Sort().Squash()
}
// These three methods implement sort.Interface
func (rs Ranges) Len() int { return len(rs) }
func (rs Ranges) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] }
func (rs Ranges) Less(i, j int) bool {
return rs[i].Begin < rs[j].Begin || (rs[i].Begin == rs[j].Begin && rs[i].End < rs[j].End)
}
// Size returns the sum of the Size of all Ranges.
func (rs Ranges) Size() uint64 {
var sz uint64
for i := range rs {
sz += 1 + (rs[i].End - rs[i].Begin)
}
return sz
}
// Sort sorts the receiving Ranges and returns the result; convenience
func (rs Ranges) Sort() Ranges {
sort.Sort(rs)
return rs
}
// Squash merges overlapping and continuous Ranges. It assumes they're pre-sorted.
func (rs Ranges) Squash() Ranges {
if len(rs) < 2 {
return rs
}
squashed := Ranges{rs[0]}
for i := 1; i < len(rs); i++ {
switch max := squashed[len(squashed)-1].End; {
case 1+max < rs[i].Begin: // no overlap nor continuity: push
squashed = append(squashed, rs[i])
case max <= rs[i].End: // overlap or continuity: squash
squashed[len(squashed)-1].End = rs[i].End
}
}
return squashed
}
// Search performs a binary search for n returning the index of the Range it was
// found at or -1 if not found.
func (rs Ranges) Search(n uint64) int {
for lo, hi := 0, len(rs)-1; lo <= hi; {
switch m := lo + (hi-lo)/2; {
case n < rs[m].Begin:
hi = m - 1
case n > rs[m].End:
lo = m + 1
default:
return m
}
}
return -1
}
// Partition partitions Ranges around n. It returns the partitioned Ranges
// and a boolean indicating if n was found.
func (rs Ranges) Partition(n uint64) (Ranges, bool) {
i := rs.Search(n)
if i < 0 {
return rs, false
}
pn := make(Ranges, 0, len(rs)+1)
switch pn = append(pn, rs[:i]...); {
case rs[i].Begin == rs[i].End: // delete
case rs[i].Begin == n: // increment lower bound
pn = append(pn, Value_Range{rs[i].Begin + 1, rs[i].End})
case rs[i].End == n: // decrement upper bound
pn = append(pn, Value_Range{rs[i].Begin, rs[i].End - 1})
default: // split
pn = append(pn, Value_Range{rs[i].Begin, n - 1}, Value_Range{n + 1, rs[i].End})
}
return append(pn, rs[i+1:]...), true
}
// Remove removes a range from already coalesced ranges.
// The algorithms constructs a new vector of ranges which is then
// Squash'ed into a Ranges instance.
func (rs Ranges) Remove(removal Value_Range) Ranges {
ranges := make([]Value_Range, 0, len(rs))
for _, r := range rs {
// skip if the entire range is subsumed by removal
if r.Begin >= removal.Begin && r.End <= removal.End {
continue
}
// divide if the range subsumes the removal
if r.Begin < removal.Begin && r.End > removal.End {
ranges = append(ranges,
Value_Range{r.Begin, removal.Begin - 1},
Value_Range{removal.End + 1, r.End},
)
continue
}
// add the full range if there's no intersection
if r.End < removal.Begin || r.Begin > removal.End {
ranges = append(ranges, r)
continue
}
// trim if the range does intersect
if r.End > removal.End {
ranges = append(ranges, Value_Range{removal.End + 1, r.End})
} else {
if r.Begin >= removal.Begin {
// should never happen
panic("r.Begin >= removal.Begin")
}
ranges = append(ranges, Value_Range{r.Begin, removal.Begin - 1})
}
}
return Ranges(ranges).Squash()
}
// Compare assumes that both Ranges are already in sort-order.
// Returns 0 if rs and right are equivalent, -1 if rs is a subset of right, or else 1
func (rs Ranges) Compare(right Ranges) int {
x, y, result := rs.equiv(right)
if result {
return 0
}
for _, a := range x {
// make sure that this range is a subset of a range in y
matched := false
for _, b := range y {
if a.Begin >= b.Begin && a.End <= b.End {
matched = true
break
}
}
if !matched {
return 1
}
}
return -1
}
// Equivalent assumes that both Ranges are already in sort-order.
func (rs Ranges) Equivalent(right Ranges) (result bool) {
_, _, result = rs.equiv(right)
return
}
// Equivalent assumes that both Ranges are already in sort-order.
func (rs Ranges) equiv(right Ranges) (_, _ Ranges, _ bool) {
// we need to squash rs and right but don't want to change the originals
switch len(rs) {
case 0:
case 1:
rs = Ranges{rs[0]}
default:
rs = Ranges(append([]Value_Range{rs[0], rs[1]}, rs[2:]...)).Sort().Squash()
}
switch len(right) {
case 0:
case 1:
right = Ranges{right[0]}
default:
right = Ranges(append([]Value_Range{right[0], right[1]}, right[2:]...)).Sort().Squash()
}
return rs, right, (&Value_Ranges{Range: rs}).Equal(&Value_Ranges{Range: right})
}
func (rs Ranges) Clone() Ranges {
if len(rs) == 0 {
return nil
}
x := make(Ranges, len(rs))
copy(x, rs)
return x
}
// Min returns the minimum number in Ranges. It will panic on empty Ranges.
func (rs Ranges) Min() uint64 { return rs[0].Begin }
// Max returns the maximum number in Ranges. It will panic on empty Ranges.
func (rs Ranges) Max() uint64 { return rs[len(rs)-1].End }
// resource returns a *Resource with the given name and Ranges.
func (rs Ranges) resource(name string) Resource {
vr := make([]Value_Range, len(rs))
copy(vr, rs)
return Resource{
Name: name,
Type: RANGES.Enum(),
Ranges: &Value_Ranges{Range: vr},
}
}
// uint64s is an utility used to sort a slice of uint64s
type uint64s []uint64
// These three methods implement sort.Interface
func (ns uint64s) Len() int { return len(ns) }
func (ns uint64s) Less(i, j int) bool { return ns[i] < ns[j] }
func (ns uint64s) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] }
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"reader.go",
"strconv.go",
"writer.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/mesos/mesos-go/api/v1/lib/recordio",
importpath = "github.com/mesos/mesos-go/api/v1/lib/recordio",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/mesos/mesos-go/api/v1/lib/debug:go_default_library",
"//vendor/github.com/mesos/mesos-go/api/v1/lib/encoding/framing:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package recordio implements the Mesos variant of RecordIO framing, whereby
// each record is prefixed by a line that indicates the length of the record in
// decimal ASCII. The bytes of the record immediately follow the length-line.
// Zero-length records are allowed.
package recordio
package recordio
import (
"bufio"
"bytes"
"io"
logger "github.com/mesos/mesos-go/api/v1/lib/debug"
"github.com/mesos/mesos-go/api/v1/lib/encoding/framing"
)
const debug = logger.Logger(false)
type (
Opt func(*reader)
reader struct {
*bufio.Scanner
pend int
splitf func(data []byte, atEOF bool) (int, []byte, error)
maxf int // max frame size
}
)
// NewReader returns a reader that parses frames from a recordio stream.
func NewReader(read io.Reader, opt ...Opt) framing.Reader {
debug.Log("new frame reader")
r := &reader{Scanner: bufio.NewScanner(read)}
r.Split(func(data []byte, atEOF bool) (int, []byte, error) {
// Scanner panics if we invoke Split after scanning has started,
// use this proxy func as a work-around.
return r.splitf(data, atEOF)
})
buf := make([]byte, 16*1024)
r.Buffer(buf, 1<<22) // 1<<22 == max protobuf size
r.splitf = r.splitSize
// apply options
for _, f := range opt {
if f != nil {
f(r)
}
}
return r
}
// MaxMessageSize returns a functional option that configures the internal Scanner's buffer and max token (message)
// length, in bytes.
func MaxMessageSize(max int) Opt {
return func(r *reader) {
buf := make([]byte, max>>1)
r.Buffer(buf, max)
r.maxf = max
}
}
func (r *reader) splitSize(data []byte, atEOF bool) (int, []byte, error) {
const maxTokenLength = 20 // textual length of largest uint64 number
if atEOF {
x := len(data)
switch {
case x == 0:
debug.Log("EOF and empty frame, returning io.EOF")
return 0, nil, io.EOF
case x < 2: // min frame size
debug.Log("remaining data less than min total frame length")
return 0, nil, framing.ErrorUnderrun
}
// otherwise, we may have a valid frame...
}
debug.Log("len(data)=", len(data))
adv := 0
for {
i := 0
for ; i < maxTokenLength && i < len(data) && data[i] != '\n'; i++ {
}
debug.Log("i=", i)
if i == len(data) {
debug.Log("need more input")
return 0, nil, nil // need more input
}
if i == maxTokenLength && data[i] != '\n' {
debug.Log("frame size: max token length exceeded")
return 0, nil, framing.ErrorBadSize
}
n, err := ParseUintBytes(bytes.TrimSpace(data[:i]), 10, 64)
if err != nil {
debug.Log("failed to parse frame size field:", err)
return 0, nil, framing.ErrorBadSize
}
if r.maxf != 0 && int(n) > r.maxf {
debug.Log("frame size max length exceeded:", n)
return 0, nil, framing.ErrorOversizedFrame
}
if n == 0 {
// special case... don't invoke splitData, just parse the next size header
adv += i + 1
data = data[i+1:]
continue
}
r.pend = int(n)
r.splitf = r.splitFrame
debug.Logf("split next frame: %d, %d", n, adv+i+1)
return adv + i + 1, data[:0], nil // returning a nil token screws up the Scanner, so return empty
}
}
func (r *reader) splitFrame(data []byte, atEOF bool) (advance int, token []byte, err error) {
x := len(data)
debug.Log("splitFrame:x=", x, ",eof=", atEOF)
if atEOF {
if x < r.pend {
return 0, nil, framing.ErrorUnderrun
}
}
if r.pend == 0 {
panic("asked to read frame data, but no data left in frame")
}
if x < int(r.pend) {
// need more data
return 0, nil, nil
}
r.splitf = r.splitSize
adv := int(r.pend)
r.pend = 0
return adv, data[:adv], nil
}
// ReadFrame implements framing.Reader
func (r *reader) ReadFrame() (tok []byte, err error) {
for r.Scan() {
b := r.Bytes()
if len(b) == 0 {
continue
}
tok = b
debug.Log("len(tok)", len(tok))
break
}
// either scan failed, or it succeeded and we have a token...
err = r.Err()
if err == nil && len(tok) == 0 {
err = io.EOF
}
return
}
/*
Copyright 2013 The Camlistore 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 recordio
import (
"errors"
"strconv"
)
// ParseUintBytes is like strconv.ParseUint, but using a []byte.
func ParseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) {
var cutoff, maxVal uint64
if bitSize == 0 {
bitSize = int(strconv.IntSize)
}
s0 := s
switch {
case len(s) < 1:
err = strconv.ErrSyntax
goto Error
case 2 <= base && base <= 36:
// valid base; nothing to do
case base == 0:
// Look for octal, hex prefix.
switch {
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
base = 16
s = s[2:]
if len(s) < 1 {
err = strconv.ErrSyntax
goto Error
}
case s[0] == '0':
base = 8
default:
base = 10
}
default:
err = errors.New("invalid base " + strconv.Itoa(base))
goto Error
}
n = 0
cutoff = cutoff64(base)
maxVal = 1<<uint(bitSize) - 1
for i := 0; i < len(s); i++ {
var v byte
d := s[i]
switch {
case '0' <= d && d <= '9':
v = d - '0'
case 'a' <= d && d <= 'z':
v = d - 'a' + 10
case 'A' <= d && d <= 'Z':
v = d - 'A' + 10
default:
n = 0
err = strconv.ErrSyntax
goto Error
}
if int(v) >= base {
n = 0
err = strconv.ErrSyntax
goto Error
}
if n >= cutoff {
// n*base overflows
n = 1<<64 - 1
err = strconv.ErrRange
goto Error
}
n *= uint64(base)
n1 := n + uint64(v)
if n1 < n || n1 > maxVal {
// n+v overflows
n = 1<<64 - 1
err = strconv.ErrRange
goto Error
}
n = n1
}
return n, nil
Error:
return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err}
}
// Return the first number n such that n*base >= 1<<64.
func cutoff64(base int) uint64 {
if base < 2 {
return 0
}
return (1<<64-1)/uint64(base) + 1
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment