diff -Nru cadvisor-0.23.0+dfsg/api/versions.go cadvisor-0.25.0+dfsg/api/versions.go --- cadvisor-0.23.0+dfsg/api/versions.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/api/versions.go 2017-03-09 23:01:14.000000000 +0000 @@ -361,7 +361,10 @@ glog.V(4).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt) infos, err := m.GetRequestedContainersInfo(name, opt) if err != nil { - return err + if len(infos) == 0 { + return err + } + glog.Errorf("Error calling GetRequestedContainersInfo: %v", err) } contStats := make(map[string][]v2.DeprecatedContainerStats, 0) for name, cinfo := range infos { @@ -482,7 +485,10 @@ glog.V(4).Infof("Api - MachineStats(%v)", request) cont, err := m.GetRequestedContainersInfo("/", opt) if err != nil { - return err + if len(cont) == 0 { + return err + } + glog.Errorf("Error calling GetRequestedContainersInfo: %v", err) } return writeResult(v2.MachineStatsFromV1(cont["/"]), w) case statsApi: @@ -490,7 +496,10 @@ glog.V(4).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt) conts, err := m.GetRequestedContainersInfo(name, opt) if err != nil { - return err + if len(conts) == 0 { + return err + } + glog.Errorf("Error calling GetRequestedContainersInfo: %v", err) } contStats := make(map[string]v2.ContainerInfo, len(conts)) for name, cont := range conts { @@ -500,7 +509,7 @@ } contStats[name] = v2.ContainerInfo{ Spec: v2.ContainerSpecFromV1(&cont.Spec, cont.Aliases, cont.Namespace), - Stats: v2.ContainerStatsFromV1(&cont.Spec, cont.Stats), + Stats: v2.ContainerStatsFromV1(name, &cont.Spec, cont.Stats), } } return writeResult(contStats, w) diff -Nru cadvisor-0.23.0+dfsg/build/assets.sh cadvisor-0.25.0+dfsg/build/assets.sh --- cadvisor-0.23.0+dfsg/build/assets.sh 1970-01-01 00:00:00.000000000 +0000 +++ cadvisor-0.25.0+dfsg/build/assets.sh 2017-03-09 23:01:14.000000000 +0000 @@ -0,0 +1,62 @@ +#!/bin/bash + +# Copyright 2015 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. + +set -e + +GIT_ROOT=$(dirname "${BASH_SOURCE}")/.. + +ASSETS_INPUT_DIRS="$GIT_ROOT/pages/assets/js/... $GIT_ROOT/pages/assets/styles/..." +ASSETS_OUTPUT_PATH="$GIT_ROOT/pages/static/assets.go" +ASSETS_PACKAGE="static" + +TEMPLATES_INPUT_DIRS="$GIT_ROOT/pages/assets/html/..." +TEMPLATES_OUTPUT_PATH="$GIT_ROOT/pages/templates.go" +TEMPLATES_PACKAGE="pages" + +FORCE="${FORCE:-}" # Force assets to be rebuilt if FORCE=true + +go get -u github.com/jteeuwen/go-bindata/... + +build_asset () { + local package=$1 + local output_path=$2 + local input_dirs=${@:3} + local tmp_output=$(mktemp) + local year=$(date +%Y) + + go-bindata -nometadata -o $output_path -pkg $package $input_dirs + cat build/boilerplate/boilerplate.go.txt | sed "s/YEAR/$year/" > "${tmp_output}" + echo -e "// generated by build/assets.sh; DO NOT EDIT\n" >> "${tmp_output}" + cat "${output_path}" >> "${tmp_output}" + gofmt -w -s "${tmp_output}" + mv "${tmp_output}" "${output_path}" +} + +for f in $GIT_ROOT/pages/assets/js/* $GIT_ROOT/pages/assets/styles/*; do + if [ "$FORCE" == "true" ] || [ "$f" -nt $ASSETS_OUTPUT_PATH -o ! -e $ASSETS_OUTPUT_PATH ]; then + build_asset "$ASSETS_PACKAGE" "$ASSETS_OUTPUT_PATH" "$ASSETS_INPUT_DIRS" + break; + fi +done + +for f in $GIT_ROOT/pages/assets/html/*; do + if [ "$FORCE" == "true" ] || [ "$f" -nt $TEMPLATES_OUTPUT_PATH -o ! -e $TEMPLATES_OUTPUT_PATH ]; then + build_asset "$TEMPLATES_PACKAGE" "$TEMPLATES_OUTPUT_PATH" "$TEMPLATES_INPUT_DIRS" + break; + fi +done + +exit 0 diff -Nru cadvisor-0.23.0+dfsg/build/boilerplate/boilerplate.py cadvisor-0.25.0+dfsg/build/boilerplate/boilerplate.py --- cadvisor-0.23.0+dfsg/build/boilerplate/boilerplate.py 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/build/boilerplate/boilerplate.py 2017-03-09 23:01:14.000000000 +0000 @@ -95,7 +95,7 @@ def file_extension(filename): return os.path.splitext(filename)[1].split(".")[-1].lower() -skipped_dirs = ['Godeps', 'third_party', '_gopath', '_output', '.git'] +skipped_dirs = ['Godeps', 'vendor', 'third_party', '_gopath', '_output', '.git'] def normalize_files(files): newfiles = [] for pathname in files: @@ -138,7 +138,7 @@ # Search for "YEAR" which exists in the boilerplate, but shouldn't in the real thing regexs["year"] = re.compile( 'YEAR' ) # dates can be 2014, 2015 or 2016, company holder names can be anything - regexs["date"] = re.compile( '(2014|2015|2016)' ) + regexs["date"] = re.compile( '(2014|2015|2016|2017|2018|2019|2020)' ) # strip // +build \n\n build constraints regexs["go_build_constraints"] = re.compile(r"^(// \+build.*\n)+\n", re.MULTILINE) # strip #!.* from shell scripts diff -Nru cadvisor-0.23.0+dfsg/build/build.sh cadvisor-0.25.0+dfsg/build/build.sh --- cadvisor-0.23.0+dfsg/build/build.sh 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/build/build.sh 2017-03-09 23:01:14.000000000 +0000 @@ -16,18 +16,19 @@ set -e +GO_FLAGS=${GO_FLAGS:-} # Extra go flags to use in the build. +GO_CMD=${GO_CMD:-"install"} +BUILD_USER=${BUILD_USER:-"${USER}@${HOSTNAME}"} +BUILD_DATE=${BUILD_DATE:-$( date +%Y%m%d-%H:%M:%S )} +VERBOSE=${VERBOSE:-} + repo_path="github.com/google/cadvisor" -version=$( cat version/VERSION ) +version=$( git describe --tags --dirty --abbrev=14 | sed -E 's/-([0-9]+)-g/.\1+/' ) revision=$( git rev-parse --short HEAD 2> /dev/null || echo 'unknown' ) branch=$( git rev-parse --abbrev-ref HEAD 2> /dev/null || echo 'unknown' ) -host=$( hostname -f ) -build_date=$( date +%Y%m%d-%H:%M:%S ) go_version=$( go version | sed -e 's/^[^0-9.]*\([0-9.]*\).*/\1/' ) -if [ "$(go env GOOS)" = "windows" ]; then - ext=".exe" -fi # go 1.4 requires ldflags format to be "-X key value", not "-X key=value" ldseparator="=" @@ -36,14 +37,20 @@ fi ldflags=" + -extldflags '-static' -X ${repo_path}/version.Version${ldseparator}${version} -X ${repo_path}/version.Revision${ldseparator}${revision} -X ${repo_path}/version.Branch${ldseparator}${branch} - -X ${repo_path}/version.BuildUser${ldseparator}${USER}@${host} - -X ${repo_path}/version.BuildDate${ldseparator}${build_date} + -X ${repo_path}/version.BuildUser${ldseparator}${BUILD_USER} + -X ${repo_path}/version.BuildDate${ldseparator}${BUILD_DATE} -X ${repo_path}/version.GoVersion${ldseparator}${go_version}" -echo " > cadvisor" -godep go build -ldflags "${ldflags}" -o cadvisor${ext} ${repo_path} +echo ">> building cadvisor" + +if [ -n "$VERBOSE" ]; then + echo "Building with -ldflags $ldflags" +fi + +GOBIN=$PWD go "$GO_CMD" ${GO_FLAGS} -ldflags "${ldflags}" "${repo_path}" exit 0 diff -Nru cadvisor-0.23.0+dfsg/build/check_errorf.sh cadvisor-0.25.0+dfsg/build/check_errorf.sh --- cadvisor-0.23.0+dfsg/build/check_errorf.sh 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/build/check_errorf.sh 2017-03-09 23:01:14.000000000 +0000 @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -GO_FILES=$(find . -not -wholename "*Godeps*" -name "*.go") +GO_FILES=$(find . -not -wholename "*Godeps*" -not -wholename "*vendor*" -name "*.go") for FILE in ${GO_FILES}; do ERRS=`grep 'fmt.Errorf("[A-Z]' ${FILE}` diff -Nru cadvisor-0.23.0+dfsg/build/check_gofmt.sh cadvisor-0.25.0+dfsg/build/check_gofmt.sh --- cadvisor-0.23.0+dfsg/build/check_gofmt.sh 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/build/check_gofmt.sh 2017-03-09 23:01:14.000000000 +0000 @@ -14,14 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Check usage. -if [ $# -ne 1 ]; then - echo "USAGE: check_gofmt " - exit 1 -fi - # Check formatting on non Godep'd code. -GOFMT_PATHS=$(find . -not -wholename "*.git*" -not -wholename "*Godeps*" -not -name "." -type d) +GOFMT_PATHS=$(find . -not -wholename "*.git*" -not -wholename "*Godeps*" -not -wholename "*vendor*" -not -name "." -type d) # Find any files with gofmt problems BAD_FILES=$(gofmt -s -l $GOFMT_PATHS) diff -Nru cadvisor-0.23.0+dfsg/build/integration.sh cadvisor-0.25.0+dfsg/build/integration.sh --- cadvisor-0.23.0+dfsg/build/integration.sh 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/build/integration.sh 2017-03-09 23:01:14.000000000 +0000 @@ -18,17 +18,39 @@ exec ./build/jenkins_e2e.sh fi -sudo -v || exit 1 +set -e -echo ">> starting cAdvisor locally" -sudo ./cadvisor --docker_env_metadata_whitelist=TEST_VAR & +# Build the test binary. +GO_FLAGS="-race" ./build/build.sh -readonly TIMEOUT=120 # Timeout to wait for cAdvisor, in seconds. +TEST_PID=$$ +sudo printf "" # Refresh sudo credentials if necessary. +function start { + set +e # We want to handle errors if cAdvisor crashes. + echo ">> starting cAdvisor locally" + GORACE="halt_on_error=1" sudo -E ./cadvisor --docker_env_metadata_whitelist=TEST_VAR + if [ $? != 0 ]; then + echo "!! cAdvisor exited unexpectedly with Exit $?" + kill $TEST_PID # cAdvisor crashed: abort testing. + fi +} +start & +RUNNER_PID=$! + +function cleanup { + if pgrep cadvisor > /dev/null; then + echo ">> stopping cAdvisor" + sudo pkill -SIGINT cadvisor + wait $RUNNER_PID + fi +} +trap cleanup EXIT + +readonly TIMEOUT=30 # Timeout to wait for cAdvisor, in seconds. START=$(date +%s) while [ "$(curl -Gs http://localhost:8080/healthz)" != "ok" ]; do if (( $(date +%s) - $START > $TIMEOUT )); then echo "Timed out waiting for cAdvisor to start" - sudo pkill -9 cadvisor exit 1 fi echo "Waiting for cAdvisor to start ..." @@ -36,12 +58,4 @@ done echo ">> running integration tests against local cAdvisor" -godep go test github.com/google/cadvisor/integration/tests/... --vmodule=*=2 -STATUS=$? -if [ $STATUS -ne 0 ]; then - echo "Integration tests failed" -fi -echo ">> stopping cAdvisor" -sudo pkill -9 cadvisor - -exit $STATUS +go test github.com/google/cadvisor/integration/tests/... --vmodule=*=2 diff -Nru cadvisor-0.23.0+dfsg/build/jenkins_e2e.sh cadvisor-0.25.0+dfsg/build/jenkins_e2e.sh --- cadvisor-0.23.0+dfsg/build/jenkins_e2e.sh 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/build/jenkins_e2e.sh 2017-03-09 23:01:14.000000000 +0000 @@ -17,36 +17,51 @@ set -e set -x -godep go build github.com/google/cadvisor/integration/runner +BUILDER=${BUILDER:-false} # Whether this is running a PR builder job. -# Host Notes -# e2e-cadvisor-ubuntu-trusty-docker110 -# - ubuntu 14.04 -# - docker 1.10 -# e2e-cadvisor-container-vm-v20160127-docker18 -# - docker 1.8.3 -# e2e-cadvisor-container-vm-v20151215-docker18 -# - docker 1.8.3 -# e2e-cadvisor-ubuntu-trusty-docker19 -# - ubunty 14.04 -# - docker 1.9.1 -# e2e-cadvisor-coreos-beta-docker19 -# - docker 1.9.1 -# e2e-cadvisor-rhel-7-docker19 -# - red hat 7 -# - docker 1.9.1 -# e2e-cadvisor-centos-v7 -# - docker 1.9.1 +export GO_FLAGS="-race" +export GORACE="halt_on_error=1" + +# Check whether assets need to be rebuilt. +FORCE=true build/assets.sh +if [[ ! -z "$(git diff --name-only pages)" ]]; then + echo "Found changes to UI assets:" + git diff --name-only pages + echo "Run: `make assets FORCE=true`" + exit 1 +fi + +# Build & test with go 1.7 +docker run --rm \ + -w "/go/src/github.com/google/cadvisor" \ + -v "${GOPATH}/src/github.com/google/cadvisor:/go/src/github.com/google/cadvisor" \ + golang:1.7 make all test-runner # Nodes that are currently stable. When tests fail on a specific node, and the failure is not remedied within a week, that node will be removed from this list. -golden_nodes="e2e-cadvisor-ubuntu-trusty-docker19 e2e-cadvisor-coreos-beta-docker19 e2e-cadvisor-container-vm-v20151215-docker18 e2e-cadvisor-container-vm-v20160127-docker18 e2e-cadvisor-rhel-7-docker19 -" +golden_nodes=( + e2e-cadvisor-ubuntu-trusty + e2e-cadvisor-container-vm-v20151215 + e2e-cadvisor-container-vm-v20160127 + e2e-cadvisor-rhel-7 +) + +# TODO: Add test on GCI + +# TODO: Add test for kubernetes default image +# e2e-cadvisor-container-vm-v20160321 + +# TODO: Temporarily disabled for #1344 +# e2e-cadvisor-coreos-beta + # TODO: enable when docker 1.10 is working # e2e-cadvisor-ubuntu-trusty-docker110 -# Always fails with "Network tx and rx bytes should not be equal" -failing_nodes="e2e-cadvisor-centos-v7" +# TODO: Always fails with "Network tx and rx bytes should not be equal" +# e2e-cadvisor-centos-v7 max_retries=8 -./runner --logtostderr --test-retry-count=$max_retries --test-retry-whitelist=integration/runner/retrywhitelist.txt $golden_nodes +./runner --logtostderr --test-retry-count=$max_retries \ + --test-retry-whitelist=integration/runner/retrywhitelist.txt \ + --ssh-options "-i /var/lib/jenkins/gce_keys/google_compute_engine -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o CheckHostIP=no -o StrictHostKeyChecking=no" \ + ${golden_nodes[*]} diff -Nru cadvisor-0.23.0+dfsg/build/presubmit.sh cadvisor-0.25.0+dfsg/build/presubmit.sh --- cadvisor-0.23.0+dfsg/build/presubmit.sh 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/build/presubmit.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -#!/bin/bash - -# Copyright 2015 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. - -set -e -set -x - -./build/check_gofmt.sh . -./build/check_boilerplate.sh -go vet github.com/google/cadvisor/... -godep go test -tags test -v -race -test.short github.com/google/cadvisor/... -godep go build github.com/google/cadvisor diff -Nru cadvisor-0.23.0+dfsg/build/release.sh cadvisor-0.25.0+dfsg/build/release.sh --- cadvisor-0.23.0+dfsg/build/release.sh 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/build/release.sh 2017-03-09 23:01:14.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2015 Google Inc. All rights reserved. # @@ -14,10 +14,42 @@ # See the License for the specific language governing permissions and # limitations under the License. -version=$( cat version/VERSION ) -branch=$( git rev-parse --abbrev-ref HEAD 2> /dev/null || echo 'unknown' ) +set -e -rm -rf release && mkdir release -cp cadvisor release/cadvisor -go get -u github.com/progrium/gh-release -gh-release create google/cadvisor ${version} ${branch} ${version} +VERSION=$( git describe --tags --dirty --abbrev=14 | sed -E 's/-([0-9]+)-g/.\1+/' ) +# Only allow releases of tagged versions. +TAGGED='^v[0-9]+\.[0-9]+\.[0-9]+(-(alpha|beta)[0-9]*)?$' +if [[ ! "$VERSION" =~ $TAGGED ]]; then + echo "Error: Only tagged versions are allowed for releases" >&2 + echo "Found: $VERSION" >&2 + exit 1 +fi + +# Don't include hostname with release builds +if ! git_user="$(git config --get user.email)"; then + echo "Error: git user not set, use:" + echo "git config user.email " + exit 1 +fi + +# Build the release. +export BUILD_USER="$git_user" +export BUILD_DATE=$( date +%Y%m%d ) # Release date is only to day-granularity +export GO_CMD="build" # Don't use cached build objects for releases. +export VERBOSE=true +build/build.sh + +# Build the docker image +echo ">> building cadvisor docker image" +docker_tag="google/cadvisor:$VERSION" +gcr_tag="gcr.io/google_containers/cadvisor:$VERSION" +docker build -t $docker_tag -t $gcr_tag -f deploy/Dockerfile . + +echo +echo "Release info:" +echo "VERSION=$VERSION" +sha256sum --tag cadvisor +echo "docker image: $docker_tag" +echo "gcr.io image: $gcr_tag" + +exit 0 diff -Nru cadvisor-0.23.0+dfsg/cadvisor.go cadvisor-0.25.0+dfsg/cadvisor.go --- cadvisor-0.23.0+dfsg/cadvisor.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/cadvisor.go 2017-03-09 23:01:14.000000000 +0000 @@ -32,6 +32,7 @@ "github.com/google/cadvisor/utils/sysfs" "github.com/google/cadvisor/version" + "crypto/tls" "github.com/golang/glog" ) @@ -39,7 +40,6 @@ var argPort = flag.Int("port", 8080, "port to listen") var maxProcs = flag.Int("max_procs", 0, "max number of CPUs that can be used simultaneously. Less than 1 for default (number of cores).") -var argDbDriver = flag.String("storage_driver", "", "storage driver to use. Data is always cached shortly in memory, this controls where data is pushed besides the local cache. Empty means none. Options are: (default), bigquery, influxdb, and kafka") var versionFlag = flag.Bool("version", false, "print cAdvisor version and exit") var httpAuthFile = flag.String("http_auth_file", "", "HTTP auth file for the web UI") @@ -54,6 +54,9 @@ var enableProfiling = flag.Bool("profiling", false, "Enable profiling via web interface host:port/debug/pprof/") +var collectorCert = flag.String("collector_cert", "", "Collector's certificate, exposed to endpoints for certificate based authentication.") +var collectorKey = flag.String("collector_key", "", "Key for the collector's certificate") + var ( // Metrics to be ignored. // Tcp metrics are ignored by default. @@ -72,11 +75,15 @@ } func (ml *metricSetValue) String() string { - return fmt.Sprint(*ml) + var values []string + for metric, _ := range ml.MetricSet { + values = append(values, string(metric)) + } + return strings.Join(values, ",") } func (ml *metricSetValue) Set(value string) error { - ignoreMetrics = metricSetValue{} + ml.MetricSet = container.MetricSet{} if value == "" { return nil } @@ -91,7 +98,7 @@ } func init() { - flag.Var(&ignoreMetrics, "disable_metrics", "comma-separated list of metrics to be disabled. Options are `disk`, `network`, `tcp`. Note: tcp is disabled by default due to high CPU usage.") + flag.Var(&ignoreMetrics, "disable_metrics", "comma-separated list of `metrics` to be disabled. Options are 'disk', 'network', 'tcp'. Note: tcp is disabled by default due to high CPU usage.") } func main() { @@ -105,9 +112,9 @@ setMaxProcs() - memoryStorage, err := NewMemoryStorage(*argDbDriver) + memoryStorage, err := NewMemoryStorage() if err != nil { - glog.Fatalf("Failed to connect to database: %s", err) + glog.Fatalf("Failed to initialize storage driver: %s", err) } sysFs, err := sysfs.NewRealSysFs() @@ -115,7 +122,9 @@ glog.Fatalf("Failed to create a system interface: %s", err) } - containerManager, err := manager.New(memoryStorage, sysFs, *maxHousekeepingInterval, *allowDynamicHousekeeping, ignoreMetrics.MetricSet) + collectorHttpClient := createCollectorHttpClient(*collectorCert, *collectorKey) + + containerManager, err := manager.New(memoryStorage, sysFs, *maxHousekeepingInterval, *allowDynamicHousekeeping, ignoreMetrics.MetricSet, &collectorHttpClient) if err != nil { glog.Fatalf("Failed to create a Container Manager: %s", err) } @@ -183,3 +192,29 @@ os.Exit(0) }() } + +func createCollectorHttpClient(collectorCert, collectorKey string) http.Client { + //Enable accessing insecure endpoints. We should be able to access metrics from any endpoint + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + } + + if collectorCert != "" { + if collectorKey == "" { + glog.Fatal("The collector_key value must be specified if the collector_cert value is set.") + } + cert, err := tls.LoadX509KeyPair(collectorCert, collectorKey) + if err != nil { + glog.Fatalf("Failed to use the collector certificate and key: %s", err) + } + + tlsConfig.Certificates = []tls.Certificate{cert} + tlsConfig.BuildNameToCertificate() + } + + transport := &http.Transport{ + TLSClientConfig: tlsConfig, + } + + return http.Client{Transport: transport} +} diff -Nru cadvisor-0.23.0+dfsg/cadvisor_test.go cadvisor-0.25.0+dfsg/cadvisor_test.go --- cadvisor-0.23.0+dfsg/cadvisor_test.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/cadvisor_test.go 2017-03-09 23:01:14.000000000 +0000 @@ -28,8 +28,22 @@ assert.True(t, ignoreMetrics.Has(container.NetworkTcpUsageMetrics)) } -func TestTcpMetricsAreEnabledOnDemand(t *testing.T) { - assert.True(t, ignoreMetrics.Has(container.NetworkTcpUsageMetrics)) - ignoreMetrics.Set("") - assert.False(t, ignoreMetrics.Has(container.NetworkTcpUsageMetrics)) +func TestIgnoreMetrics(t *testing.T) { + tests := []struct { + value string + expected []container.MetricKind + }{ + {"", []container.MetricKind{}}, + {"disk", []container.MetricKind{container.DiskUsageMetrics}}, + {"disk,tcp,network", []container.MetricKind{container.DiskUsageMetrics, container.NetworkTcpUsageMetrics, container.NetworkUsageMetrics}}, + } + + for _, test := range tests { + assert.NoError(t, ignoreMetrics.Set(test.value)) + + assert.Equal(t, len(test.expected), len(ignoreMetrics.MetricSet)) + for _, expected := range test.expected { + assert.True(t, ignoreMetrics.Has(expected), "Missing %s", expected) + } + } } diff -Nru cadvisor-0.23.0+dfsg/CHANGELOG.md cadvisor-0.25.0+dfsg/CHANGELOG.md --- cadvisor-0.23.0+dfsg/CHANGELOG.md 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/CHANGELOG.md 2017-03-09 23:01:14.000000000 +0000 @@ -1,5 +1,81 @@ # Changelog +### 0.25.0 (2017-03-09) +- Disable thin_ls due to excessive iops +- Ignore .mount cgroups, fixing dissappearing stats +- Fix wc goroutine leak +- Update aws-sdk-go dependency to 1.6.10 +- Update to go 1.7 for releases + +### 0.24.1 (2016-10-10) + +- Fix issue with running cAdvisor in a container on some distributions. + +### 0.24.0 (2016-09-19) + +- Added host-level inode stats (total & available) +- Improved robustness to partial failures +- Metrics collector improvements + - Added ability to directly use endpoints from the container itself + - Allow SSL endpoint access + - Ability to provide a certificate which is exposed to custom endpoints +- Lots of bug fixes, including: + - Devicemapper thin_ls fixes + - Prometheus metrics fixes + - Fixes for missing stats (memory reservation, FS usage, etc.) + +### 0.23.9 (2016-08-09) + +- Cherry-pick release: + - Ensure minimum kernel version for thin_ls + +### 0.23.8 (2016-08-02) + +- Cherry-pick release: + - Prefix Docker labels & env vars in Prometheus metrics to prevent conflicts + +### 0.23.7 (2016-07-18) + +- Cherry-pick release: + - Modify working set memory stats calculation + +### 0.23.6 (2016-06-23) + +- Cherry-pick release: + - Updating inotify to fix memory leak v0.23 cherrypick + +### 0.23.5 (2016-06-22) + +- Cherry-pick release: + - support LVM based device mapper storage drivers + +### 0.23.4 (2016-06-16) +- Cherry-pick release: + - Check for thin_is binary in path for devicemapper when using ThinPoolWatcher + - Fix uint64 overflow issue for CPU stats + +### 0.23.3 (2016-06-08) +- Cherry-pick release: + - Cap the maximum consecutive du commands + - Fix a panic when a prometheus endpoint ends with a newline + +### 0.23.2 (2016-05-18) +- Handle kernel log rotation +- More rkt support: poll rkt service for new containers +- Better handling of partial failures when fetching subcontainers +- Devicemapper thin_ls support (requires Device Mapper kernel module and supporting utilities) + +### 0.23.1 (2016-05-11) +- Add multi-container charts to the UI +- Add TLS options for Kafka storage driver +- Switch to official Docker client +- Systemd: + - Ignore .mount cgroups on systemd + - Better OOM monitoring +- Bug: Fix broken -disable_metrics flag +- Bug: Fix openstack identified as AWS +- Bug: Fix EventStore when limit is 0 + ### 0.23.0 (2016-04-21) - Docker v1.11 support - Preliminary rkt support @@ -79,7 +155,7 @@ - Enabled CPU load tracking (experimental). ## 0.11.0 (2015-03-27) -- Export all stats as [Prometheus](http://prometheus.io/) metrics. +- Export all stats as [Prometheus](https://prometheus.io/) metrics. - Initial support for [events](docs/api.md): creation, deletion, and OOM. - Adding machine UUID information. - Beta release of the cAdvisor [2.0 API](docs/api_v2.md). diff -Nru cadvisor-0.23.0+dfsg/collector/config/sample_config_endpoint_config.json cadvisor-0.25.0+dfsg/collector/config/sample_config_endpoint_config.json --- cadvisor-0.23.0+dfsg/collector/config/sample_config_endpoint_config.json 1970-01-01 00:00:00.000000000 +0000 +++ cadvisor-0.25.0+dfsg/collector/config/sample_config_endpoint_config.json 2017-03-09 23:01:14.000000000 +0000 @@ -0,0 +1,38 @@ +{ + "endpoint" : { + "protocol": "https", + "port": 8000, + "path": "/nginx_status" + }, + "metrics_config" : [ + { "name" : "activeConnections", + "metric_type" : "gauge", + "units" : "number of active connections", + "data_type" : "int", + "polling_frequency" : 10, + "regex" : "Active connections: ([0-9]+)" + }, + { "name" : "reading", + "metric_type" : "gauge", + "units" : "number of reading connections", + "data_type" : "int", + "polling_frequency" : 10, + "regex" : "Reading: ([0-9]+) .*" + }, + { "name" : "writing", + "metric_type" : "gauge", + "data_type" : "int", + "units" : "number of writing connections", + "polling_frequency" : 10, + "regex" : ".*Writing: ([0-9]+).*" + }, + { "name" : "waiting", + "metric_type" : "gauge", + "units" : "number of waiting connections", + "data_type" : "int", + "polling_frequency" : 10, + "regex" : ".*Waiting: ([0-9]+)" + } + ] + +} diff -Nru cadvisor-0.23.0+dfsg/collector/config/sample_config_prometheus_endpoint_config.json cadvisor-0.25.0+dfsg/collector/config/sample_config_prometheus_endpoint_config.json --- cadvisor-0.23.0+dfsg/collector/config/sample_config_prometheus_endpoint_config.json 1970-01-01 00:00:00.000000000 +0000 +++ cadvisor-0.25.0+dfsg/collector/config/sample_config_prometheus_endpoint_config.json 2017-03-09 23:01:14.000000000 +0000 @@ -0,0 +1,10 @@ +{ + "endpoint" : { + "protocol": "http", + "port": 8081, + "path": "/METRICS" + }, + "polling_frequency" : 10, + "metrics_config" : [ + ] +} \ No newline at end of file diff -Nru cadvisor-0.23.0+dfsg/collector/config.go cadvisor-0.25.0+dfsg/collector/config.go --- cadvisor-0.23.0+dfsg/collector/config.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/collector/config.go 2017-03-09 23:01:14.000000000 +0000 @@ -17,46 +17,84 @@ import ( "time" + "encoding/json" "github.com/google/cadvisor/info/v1" ) type Config struct { - //the endpoint to hit to scrape metrics - Endpoint string `json:"endpoint"` + // the endpoint to hit to scrape metrics + Endpoint EndpointConfig `json:"endpoint"` - //holds information about different metrics that can be collected + // holds information about different metrics that can be collected MetricsConfig []MetricConfig `json:"metrics_config"` } // metricConfig holds information extracted from the config file about a metric type MetricConfig struct { - //the name of the metric + // the name of the metric Name string `json:"name"` - //enum type for the metric type + // enum type for the metric type MetricType v1.MetricType `json:"metric_type"` // metric units to display on UI and in storage (eg: MB, cores) // this is only used for display. Units string `json:"units"` - //data type of the metric (eg: int, float) + // data type of the metric (eg: int, float) DataType v1.DataType `json:"data_type"` - //the frequency at which the metric should be collected + // the frequency at which the metric should be collected PollingFrequency time.Duration `json:"polling_frequency"` - //the regular expression that can be used to extract the metric + // the regular expression that can be used to extract the metric Regex string `json:"regex"` } type Prometheus struct { - //the endpoint to hit to scrape metrics - Endpoint string `json:"endpoint"` + // the endpoint to hit to scrape metrics + Endpoint EndpointConfig `json:"endpoint"` - //the frequency at which metrics should be collected + // the frequency at which metrics should be collected PollingFrequency time.Duration `json:"polling_frequency"` - //holds names of different metrics that can be collected + // holds names of different metrics that can be collected MetricsConfig []string `json:"metrics_config"` } + +type EndpointConfig struct { + // The full URL of the endpoint to reach + URL string + // A configuration in which an actual URL is constructed from, using the container's ip address + URLConfig URLConfig +} + +type URLConfig struct { + // the protocol to use for connecting to the endpoint. Eg 'http' or 'https' + Protocol string `json:"protocol"` + + // the port to use for connecting to the endpoint. Eg '8778' + Port json.Number `json:"port"` + + // the path to use for the endpoint. Eg '/metrics' + Path string `json:"path"` +} + +func (ec *EndpointConfig) UnmarshalJSON(b []byte) error { + url := "" + config := URLConfig{ + Protocol: "http", + Port: "8000", + } + + if err := json.Unmarshal(b, &url); err == nil { + ec.URL = url + return nil + } + err := json.Unmarshal(b, &config) + if err == nil { + ec.URLConfig = config + return nil + } + return err +} diff -Nru cadvisor-0.23.0+dfsg/collector/generic_collector.go cadvisor-0.25.0+dfsg/collector/generic_collector.go --- cadvisor-0.23.0+dfsg/collector/generic_collector.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/collector/generic_collector.go 2017-03-09 23:01:14.000000000 +0000 @@ -24,25 +24,29 @@ "strings" "time" + "github.com/google/cadvisor/container" "github.com/google/cadvisor/info/v1" ) type GenericCollector struct { - //name of the collector + // name of the collector name string - //holds information extracted from the config file for a collector + // holds information extracted from the config file for a collector configFile Config - //holds information necessary to extract metrics + // holds information necessary to extract metrics info *collectorInfo + + // The Http client to use when connecting to metric endpoints + httpClient *http.Client } type collectorInfo struct { - //minimum polling frequency among all metrics + // minimum polling frequency among all metrics minPollingFrequency time.Duration - //regular expresssions for all metrics + // regular expresssions for all metrics regexps []*regexp.Regexp // Limit for the number of srcaped metrics. If the count is higher, @@ -50,15 +54,17 @@ metricCountLimit int } -//Returns a new collector using the information extracted from the configfile -func NewCollector(collectorName string, configFile []byte, metricCountLimit int) (*GenericCollector, error) { +// Returns a new collector using the information extracted from the configfile +func NewCollector(collectorName string, configFile []byte, metricCountLimit int, containerHandler container.ContainerHandler, httpClient *http.Client) (*GenericCollector, error) { var configInJSON Config err := json.Unmarshal(configFile, &configInJSON) if err != nil { return nil, err } - //TODO : Add checks for validity of config file (eg : Accurate JSON fields) + configInJSON.Endpoint.configure(containerHandler) + + // TODO : Add checks for validity of config file (eg : Accurate JSON fields) if len(configInJSON.MetricsConfig) == 0 { return nil, fmt.Errorf("No metrics provided in config") @@ -99,10 +105,11 @@ regexps: regexprs, metricCountLimit: metricCountLimit, }, + httpClient: httpClient, }, nil } -//Returns name of the collector +// Returns name of the collector func (collector *GenericCollector) Name() string { return collector.name } @@ -125,13 +132,13 @@ return specs } -//Returns collected metrics and the next collection time of the collector +// Returns collected metrics and the next collection time of the collector func (collector *GenericCollector) Collect(metrics map[string][]v1.MetricVal) (time.Time, map[string][]v1.MetricVal, error) { currentTime := time.Now() nextCollectionTime := currentTime.Add(time.Duration(collector.info.minPollingFrequency)) - uri := collector.configFile.Endpoint - response, err := http.Get(uri) + uri := collector.configFile.Endpoint.URL + response, err := collector.httpClient.Get(uri) if err != nil { return nextCollectionTime, nil, err } diff -Nru cadvisor-0.23.0+dfsg/collector/generic_collector_test.go cadvisor-0.25.0+dfsg/collector/generic_collector_test.go --- cadvisor-0.23.0+dfsg/collector/generic_collector_test.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/collector/generic_collector_test.go 2017-03-09 23:01:14.000000000 +0000 @@ -24,6 +24,7 @@ "github.com/google/cadvisor/info/v1" + containertest "github.com/google/cadvisor/container/testing" "github.com/stretchr/testify/assert" ) @@ -38,13 +39,14 @@ } ` - //Create a temporary config file 'temp.json' with invalid json format + // Create a temporary config file 'temp.json' with invalid json format assert.NoError(ioutil.WriteFile("temp.json", []byte(emptyConfig), 0777)) configFile, err := ioutil.ReadFile("temp.json") assert.NoError(err) - _, err = NewCollector("tempCollector", configFile, 100) + containerHandler := containertest.NewMockContainerHandler("mockContainer") + _, err = NewCollector("tempCollector", configFile, 100, containerHandler, http.DefaultClient) assert.Error(err) assert.NoError(os.Remove("temp.json")) @@ -53,7 +55,7 @@ func TestConfigWithErrors(t *testing.T) { assert := assert.New(t) - //Syntax error: Missed '"' after activeConnections + // Syntax error: Missed '"' after activeConnections invalid := ` { "endpoint" : "http://localhost:8000/nginx_status", @@ -69,12 +71,13 @@ } ` - //Create a temporary config file 'temp.json' with invalid json format + // Create a temporary config file 'temp.json' with invalid json format assert.NoError(ioutil.WriteFile("temp.json", []byte(invalid), 0777)) configFile, err := ioutil.ReadFile("temp.json") assert.NoError(err) - _, err = NewCollector("tempCollector", configFile, 100) + containerHandler := containertest.NewMockContainerHandler("mockContainer") + _, err = NewCollector("tempCollector", configFile, 100, containerHandler, http.DefaultClient) assert.Error(err) assert.NoError(os.Remove("temp.json")) @@ -83,7 +86,7 @@ func TestConfigWithRegexErrors(t *testing.T) { assert := assert.New(t) - //Error: Missed operand for '+' in activeConnections regex + // Error: Missed operand for '+' in activeConnections regex invalid := ` { "endpoint" : "host:port/nginx_status", @@ -106,13 +109,14 @@ } ` - //Create a temporary config file 'temp.json' + // Create a temporary config file 'temp.json' assert.NoError(ioutil.WriteFile("temp.json", []byte(invalid), 0777)) configFile, err := ioutil.ReadFile("temp.json") assert.NoError(err) - _, err = NewCollector("tempCollector", configFile, 100) + containerHandler := containertest.NewMockContainerHandler("mockContainer") + _, err = NewCollector("tempCollector", configFile, 100, containerHandler, http.DefaultClient) assert.Error(err) assert.NoError(os.Remove("temp.json")) @@ -121,25 +125,44 @@ func TestConfig(t *testing.T) { assert := assert.New(t) - //Create an nginx collector using the config file 'sample_config.json' + // Create an nginx collector using the config file 'sample_config.json' configFile, err := ioutil.ReadFile("config/sample_config.json") assert.NoError(err) - collector, err := NewCollector("nginx", configFile, 100) + containerHandler := containertest.NewMockContainerHandler("mockContainer") + collector, err := NewCollector("nginx", configFile, 100, containerHandler, http.DefaultClient) assert.NoError(err) assert.Equal(collector.name, "nginx") - assert.Equal(collector.configFile.Endpoint, "http://localhost:8000/nginx_status") + assert.Equal(collector.configFile.Endpoint.URL, "http://localhost:8000/nginx_status") + assert.Equal(collector.configFile.MetricsConfig[0].Name, "activeConnections") +} + +func TestEndpointConfig(t *testing.T) { + assert := assert.New(t) + configFile, err := ioutil.ReadFile("config/sample_config_endpoint_config.json") + assert.NoError(err) + + containerHandler := containertest.NewMockContainerHandler("mockContainer") + containerHandler.On("GetContainerIPAddress").Return( + "111.111.111.111", + ) + + collector, err := NewCollector("nginx", configFile, 100, containerHandler, http.DefaultClient) + assert.NoError(err) + assert.Equal(collector.name, "nginx") + assert.Equal(collector.configFile.Endpoint.URL, "https://111.111.111.111:8000/nginx_status") assert.Equal(collector.configFile.MetricsConfig[0].Name, "activeConnections") } func TestMetricCollection(t *testing.T) { assert := assert.New(t) - //Collect nginx metrics from a fake nginx endpoint + // Collect nginx metrics from a fake nginx endpoint configFile, err := ioutil.ReadFile("config/sample_config.json") assert.NoError(err) - fakeCollector, err := NewCollector("nginx", configFile, 100) + containerHandler := containertest.NewMockContainerHandler("mockContainer") + fakeCollector, err := NewCollector("nginx", configFile, 100, containerHandler, http.DefaultClient) assert.NoError(err) tempServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -147,7 +170,7 @@ fmt.Fprintln(w, "5 5 32\nReading: 0 Writing: 1 Waiting: 2") })) defer tempServer.Close() - fakeCollector.configFile.Endpoint = tempServer.URL + fakeCollector.configFile.Endpoint.URL = tempServer.URL metrics := map[string][]v1.MetricVal{} _, metrics, errMetric := fakeCollector.Collect(metrics) @@ -170,10 +193,11 @@ func TestMetricCollectionLimit(t *testing.T) { assert := assert.New(t) - //Collect nginx metrics from a fake nginx endpoint + // Collect nginx metrics from a fake nginx endpoint configFile, err := ioutil.ReadFile("config/sample_config.json") assert.NoError(err) - _, err = NewCollector("nginx", configFile, 1) + containerHandler := containertest.NewMockContainerHandler("mockContainer") + _, err = NewCollector("nginx", configFile, 1, containerHandler, http.DefaultClient) assert.Error(err) } diff -Nru cadvisor-0.23.0+dfsg/collector/prometheus_collector.go cadvisor-0.25.0+dfsg/collector/prometheus_collector.go --- cadvisor-0.23.0+dfsg/collector/prometheus_collector.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/collector/prometheus_collector.go 2017-03-09 23:01:14.000000000 +0000 @@ -15,26 +15,30 @@ package collector import ( + "bytes" "encoding/json" "fmt" - "io/ioutil" - "math" + "io" "net/http" - "strconv" - "strings" + "sort" "time" + rawmodel "github.com/prometheus/client_model/go" + "github.com/prometheus/common/expfmt" + "github.com/prometheus/common/model" + + "github.com/google/cadvisor/container" "github.com/google/cadvisor/info/v1" ) type PrometheusCollector struct { - //name of the collector + // name of the collector name string - //rate at which metrics are collected + // rate at which metrics are collected pollingFrequency time.Duration - //holds information extracted from the config file for a collector + // holds information extracted from the config file for a collector configFile Prometheus // the metrics to gather (uses a map as a set) @@ -43,16 +47,21 @@ // Limit for the number of scaped metrics. If the count is higher, // no metrics will be returned. metricCountLimit int + + // The Http client to use when connecting to metric endpoints + httpClient *http.Client } -//Returns a new collector using the information extracted from the configfile -func NewPrometheusCollector(collectorName string, configFile []byte, metricCountLimit int) (*PrometheusCollector, error) { +// Returns a new collector using the information extracted from the configfile +func NewPrometheusCollector(collectorName string, configFile []byte, metricCountLimit int, containerHandler container.ContainerHandler, httpClient *http.Client) (*PrometheusCollector, error) { var configInJSON Prometheus err := json.Unmarshal(configFile, &configInJSON) if err != nil { return nil, err } + configInJSON.Endpoint.configure(containerHandler) + minPollingFrequency := configInJSON.PollingFrequency // Minimum supported frequency is 1s @@ -78,135 +87,187 @@ return nil, fmt.Errorf("Too many metrics defined: %d limit %d", len(configInJSON.MetricsConfig), metricCountLimit) } - //TODO : Add checks for validity of config file (eg : Accurate JSON fields) + // TODO : Add checks for validity of config file (eg : Accurate JSON fields) return &PrometheusCollector{ name: collectorName, pollingFrequency: minPollingFrequency, configFile: configInJSON, metricsSet: metricsSet, metricCountLimit: metricCountLimit, + httpClient: httpClient, }, nil } -//Returns name of the collector +// Returns name of the collector func (collector *PrometheusCollector) Name() string { return collector.name } -func getMetricData(line string) string { - fields := strings.Fields(line) - data := fields[3] - if len(fields) > 4 { - for i := range fields { - if i > 3 { - data = data + "_" + fields[i] - } - } - } - return strings.TrimSpace(data) -} - func (collector *PrometheusCollector) GetSpec() []v1.MetricSpec { - specs := []v1.MetricSpec{} - response, err := http.Get(collector.configFile.Endpoint) + + response, err := collector.httpClient.Get(collector.configFile.Endpoint.URL) if err != nil { - return specs + return nil } defer response.Body.Close() - pageContent, err := ioutil.ReadAll(response.Body) - if err != nil { - return specs + if response.StatusCode != http.StatusOK { + return nil } - lines := strings.Split(string(pageContent), "\n") - for i, line := range lines { - if strings.HasPrefix(line, "# HELP") { - stopIndex := strings.Index(lines[i+2], "{") - if stopIndex == -1 { - stopIndex = strings.Index(lines[i+2], " ") - } - name := strings.TrimSpace(lines[i+2][0:stopIndex]) - if _, ok := collector.metricsSet[name]; collector.metricsSet != nil && !ok { - continue - } - spec := v1.MetricSpec{ - Name: name, - Type: v1.MetricType(getMetricData(lines[i+1])), - Format: "float", - Units: getMetricData(lines[i]), - } - specs = append(specs, spec) + dec := expfmt.NewDecoder(response.Body, expfmt.ResponseFormat(response.Header)) + + var specs []v1.MetricSpec + + for { + d := rawmodel.MetricFamily{} + if err = dec.Decode(&d); err != nil { + break + } + name := d.GetName() + if len(name) == 0 { + continue + } + // If metrics to collect is specified, skip any metrics not in the list to collect. + if _, ok := collector.metricsSet[name]; collector.metricsSet != nil && !ok { + continue + } + + spec := v1.MetricSpec{ + Name: name, + Type: metricType(d.GetType()), + Format: v1.FloatType, } + specs = append(specs, spec) } + + if err != nil && err != io.EOF { + return nil + } + return specs } -//Returns collected metrics and the next collection time of the collector +// metricType converts Prometheus metric type to cadvisor metric type. +// If there is no mapping then just return the name of the Prometheus metric type. +func metricType(t rawmodel.MetricType) v1.MetricType { + switch t { + case rawmodel.MetricType_COUNTER: + return v1.MetricCumulative + case rawmodel.MetricType_GAUGE: + return v1.MetricGauge + default: + return v1.MetricType(t.String()) + } +} + +type prometheusLabels []*rawmodel.LabelPair + +func labelSetToLabelPairs(labels model.Metric) prometheusLabels { + var promLabels prometheusLabels + for k, v := range labels { + name := string(k) + value := string(v) + promLabels = append(promLabels, &rawmodel.LabelPair{Name: &name, Value: &value}) + } + return promLabels +} + +func (s prometheusLabels) Len() int { return len(s) } +func (s prometheusLabels) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// ByName implements sort.Interface by providing Less and using the Len and +// Swap methods of the embedded PrometheusLabels value. +type byName struct{ prometheusLabels } + +func (s byName) Less(i, j int) bool { + return s.prometheusLabels[i].GetName() < s.prometheusLabels[j].GetName() +} + +func prometheusLabelSetToCadvisorLabel(promLabels model.Metric) string { + labels := labelSetToLabelPairs(promLabels) + sort.Sort(byName{labels}) + var b bytes.Buffer + + for i, l := range labels { + if i > 0 { + b.WriteString("\xff") + } + b.WriteString(l.GetName()) + b.WriteString("=") + b.WriteString(l.GetValue()) + } + + return string(b.Bytes()) +} + +// Returns collected metrics and the next collection time of the collector func (collector *PrometheusCollector) Collect(metrics map[string][]v1.MetricVal) (time.Time, map[string][]v1.MetricVal, error) { currentTime := time.Now() nextCollectionTime := currentTime.Add(time.Duration(collector.pollingFrequency)) - uri := collector.configFile.Endpoint - response, err := http.Get(uri) + uri := collector.configFile.Endpoint.URL + response, err := collector.httpClient.Get(uri) if err != nil { return nextCollectionTime, nil, err } defer response.Body.Close() - pageContent, err := ioutil.ReadAll(response.Body) - if err != nil { - return nextCollectionTime, nil, err + if response.StatusCode != http.StatusOK { + return nextCollectionTime, nil, fmt.Errorf("server returned HTTP status %s", response.Status) } - var errorSlice []error - lines := strings.Split(string(pageContent), "\n") - - newMetrics := make(map[string][]v1.MetricVal) - - for _, line := range lines { - if line == "" { + sdec := expfmt.SampleDecoder{ + Dec: expfmt.NewDecoder(response.Body, expfmt.ResponseFormat(response.Header)), + Opts: &expfmt.DecodeOptions{ + Timestamp: model.TimeFromUnixNano(currentTime.UnixNano()), + }, + } + + var ( + // 50 is chosen as a reasonable guesstimate at a number of metrics we can + // expect from virtually any endpoint to try to save allocations. + decSamples = make(model.Vector, 0, 50) + newMetrics = make(map[string][]v1.MetricVal) + ) + for { + if err = sdec.Decode(&decSamples); err != nil { break } - if !strings.HasPrefix(line, "# HELP") && !strings.HasPrefix(line, "# TYPE") { - var metLabel string - startLabelIndex := strings.Index(line, "{") - spaceIndex := strings.Index(line, " ") - if startLabelIndex == -1 { - startLabelIndex = spaceIndex - } - metName := strings.TrimSpace(line[0:startLabelIndex]) - if _, ok := collector.metricsSet[metName]; collector.metricsSet != nil && !ok { + for _, sample := range decSamples { + metName := string(sample.Metric[model.MetricNameLabel]) + if len(metName) == 0 { continue } - - if startLabelIndex+1 <= spaceIndex-1 { - metLabel = strings.TrimSpace(line[(startLabelIndex + 1):(spaceIndex - 1)]) - } - - metVal, err := strconv.ParseFloat(line[spaceIndex+1:], 64) - if err != nil { - errorSlice = append(errorSlice, err) - } - if math.IsNaN(metVal) { - metVal = 0 + // If metrics to collect is specified, skip any metrics not in the list to collect. + if _, ok := collector.metricsSet[metName]; collector.metricsSet != nil && !ok { + continue } + // TODO Handle multiple labels nicer. Prometheus metrics can have multiple + // labels, cadvisor only accepts a single string for the metric label. + label := prometheusLabelSetToCadvisorLabel(sample.Metric) metric := v1.MetricVal{ - Label: metLabel, - FloatValue: metVal, - Timestamp: currentTime, + FloatValue: float64(sample.Value), + Timestamp: sample.Timestamp.Time(), + Label: label, } newMetrics[metName] = append(newMetrics[metName], metric) if len(newMetrics) > collector.metricCountLimit { return nextCollectionTime, nil, fmt.Errorf("too many metrics to collect") } } + decSamples = decSamples[:0] } + + if err != nil && err != io.EOF { + return nextCollectionTime, nil, err + } + for key, val := range newMetrics { metrics[key] = append(metrics[key], val...) } - return nextCollectionTime, metrics, compileErrors(errorSlice) + return nextCollectionTime, metrics, nil } diff -Nru cadvisor-0.23.0+dfsg/collector/prometheus_collector_test.go cadvisor-0.25.0+dfsg/collector/prometheus_collector_test.go --- cadvisor-0.23.0+dfsg/collector/prometheus_collector_test.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/collector/prometheus_collector_test.go 2017-03-09 23:01:14.000000000 +0000 @@ -23,56 +23,143 @@ "github.com/google/cadvisor/info/v1" + containertest "github.com/google/cadvisor/container/testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestPrometheus(t *testing.T) { assert := assert.New(t) - //Create a prometheus collector using the config file 'sample_config_prometheus.json' + // Create a prometheus collector using the config file 'sample_config_prometheus.json' configFile, err := ioutil.ReadFile("config/sample_config_prometheus.json") - collector, err := NewPrometheusCollector("Prometheus", configFile, 100) + containerHandler := containertest.NewMockContainerHandler("mockContainer") + collector, err := NewPrometheusCollector("Prometheus", configFile, 100, containerHandler, http.DefaultClient) assert.NoError(err) - assert.Equal(collector.name, "Prometheus") - assert.Equal(collector.configFile.Endpoint, "http://localhost:8080/metrics") + assert.Equal("Prometheus", collector.name) + assert.Equal("http://localhost:8080/metrics", collector.configFile.Endpoint.URL) tempServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - text := "# HELP go_gc_duration_seconds A summary of the GC invocation durations.\n" - text += "# TYPE go_gc_duration_seconds summary\n" - text += "go_gc_duration_seconds{quantile=\"0\"} 5.8348000000000004e-05\n" - text += "go_gc_duration_seconds{quantile=\"1\"} 0.000499764\n" - text += "# HELP go_goroutines Number of goroutines that currently exist.\n" - text += "# TYPE go_goroutines gauge\n" - text += "go_goroutines 16" + text := `# HELP go_gc_duration_seconds A summary of the GC invocation durations. +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{quantile="0"} 5.8348000000000004e-05 +go_gc_duration_seconds{quantile="1"} 0.000499764 +go_gc_duration_seconds_sum 1.7560473e+07 +go_gc_duration_seconds_count 2693 +# HELP go_goroutines Number of goroutines that currently exist. +# TYPE go_goroutines gauge +go_goroutines 16 +# HELP empty_metric A metric without any values +# TYPE empty_metric counter +# HELP metric_with_spaces_in_label A metric with spaces in a label. +# TYPE metric_with_spaces_in_label gauge +metric_with_spaces_in_label{name="Network Agent"} 72 +# HELP metric_with_multiple_labels A metric with multiple labels. +# TYPE metric_with_multiple_labels gauge +metric_with_multiple_labels{label1="One", label2="Two", label3="Three"} 81 +` fmt.Fprintln(w, text) })) defer tempServer.Close() - collector.configFile.Endpoint = tempServer.URL + collector.configFile.Endpoint.URL = tempServer.URL + + var spec []v1.MetricSpec + require.NotPanics(t, func() { spec = collector.GetSpec() }) + assert.Len(spec, 4) + specNames := make(map[string]struct{}, 3) + for _, s := range spec { + specNames[s.Name] = struct{}{} + } + expectedSpecNames := map[string]struct{}{ + "go_gc_duration_seconds": {}, + "go_goroutines": {}, + "metric_with_spaces_in_label": {}, + "metric_with_multiple_labels": {}, + } + assert.Equal(expectedSpecNames, specNames) + metrics := map[string][]v1.MetricVal{} _, metrics, errMetric := collector.Collect(metrics) assert.NoError(errMetric) go_gc_duration := metrics["go_gc_duration_seconds"] - assert.Equal(go_gc_duration[0].FloatValue, 5.8348000000000004e-05) - assert.Equal(go_gc_duration[1].FloatValue, 0.000499764) + assert.Equal(5.8348000000000004e-05, go_gc_duration[0].FloatValue) + assert.Equal("__name__=go_gc_duration_seconds\xffquantile=0", go_gc_duration[0].Label) + assert.Equal(0.000499764, go_gc_duration[1].FloatValue) + assert.Equal("__name__=go_gc_duration_seconds\xffquantile=1", go_gc_duration[1].Label) + go_gc_duration_sum := metrics["go_gc_duration_seconds_sum"] + assert.Equal(1.7560473e+07, go_gc_duration_sum[0].FloatValue) + assert.Equal("__name__=go_gc_duration_seconds_sum", go_gc_duration_sum[0].Label) + go_gc_duration_count := metrics["go_gc_duration_seconds_count"] + assert.Equal(2693, go_gc_duration_count[0].FloatValue) + assert.Equal("__name__=go_gc_duration_seconds_count", go_gc_duration_count[0].Label) goRoutines := metrics["go_goroutines"] - assert.Equal(goRoutines[0].FloatValue, 16) + assert.Equal(16, goRoutines[0].FloatValue) + assert.Equal("__name__=go_goroutines", goRoutines[0].Label) + + metricWithSpaces := metrics["metric_with_spaces_in_label"] + assert.Equal(72, metricWithSpaces[0].FloatValue) + assert.Equal("__name__=metric_with_spaces_in_label\xffname=Network Agent", metricWithSpaces[0].Label) + + metricWithMultipleLabels := metrics["metric_with_multiple_labels"] + assert.Equal(81, metricWithMultipleLabels[0].FloatValue) + assert.Equal("__name__=metric_with_multiple_labels\xfflabel1=One\xfflabel2=Two\xfflabel3=Three", metricWithMultipleLabels[0].Label) } -func TestPrometheusMetricCountLimit(t *testing.T) { +func TestPrometheusEndpointConfig(t *testing.T) { assert := assert.New(t) //Create a prometheus collector using the config file 'sample_config_prometheus.json' + configFile, err := ioutil.ReadFile("config/sample_config_prometheus_endpoint_config.json") + containerHandler := containertest.NewMockContainerHandler("mockContainer") + containerHandler.On("GetContainerIPAddress").Return( + "222.222.222.222", + ) + + collector, err := NewPrometheusCollector("Prometheus", configFile, 100, containerHandler, http.DefaultClient) + assert.NoError(err) + assert.Equal(collector.name, "Prometheus") + assert.Equal(collector.configFile.Endpoint.URL, "http://222.222.222.222:8081/METRICS") +} + +func TestPrometheusShortResponse(t *testing.T) { + assert := assert.New(t) + + // Create a prometheus collector using the config file 'sample_config_prometheus.json' + configFile, err := ioutil.ReadFile("config/sample_config_prometheus.json") + containerHandler := containertest.NewMockContainerHandler("mockContainer") + collector, err := NewPrometheusCollector("Prometheus", configFile, 100, containerHandler, http.DefaultClient) + assert.NoError(err) + assert.Equal(collector.name, "Prometheus") + assert.Equal(collector.configFile.Endpoint.URL, "http://localhost:8080/metrics") + + tempServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + text := "# HELP empty_metric A metric without any values" + fmt.Fprint(w, text) + })) + + defer tempServer.Close() + + collector.configFile.Endpoint.URL = tempServer.URL + + assert.NotPanics(func() { collector.GetSpec() }) +} + +func TestPrometheusMetricCountLimit(t *testing.T) { + assert := assert.New(t) + + // Create a prometheus collector using the config file 'sample_config_prometheus.json' configFile, err := ioutil.ReadFile("config/sample_config_prometheus.json") - collector, err := NewPrometheusCollector("Prometheus", configFile, 10) + containerHandler := containertest.NewMockContainerHandler("mockContainer") + collector, err := NewPrometheusCollector("Prometheus", configFile, 10, containerHandler, http.DefaultClient) assert.NoError(err) assert.Equal(collector.name, "Prometheus") - assert.Equal(collector.configFile.Endpoint, "http://localhost:8080/metrics") + assert.Equal(collector.configFile.Endpoint.URL, "http://localhost:8080/metrics") tempServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { for i := 0; i < 30; i++ { @@ -83,7 +170,7 @@ })) defer tempServer.Close() - collector.configFile.Endpoint = tempServer.URL + collector.configFile.Endpoint.URL = tempServer.URL metrics := map[string][]v1.MetricVal{} _, result, errMetric := collector.Collect(metrics) @@ -95,28 +182,32 @@ func TestPrometheusFiltersMetrics(t *testing.T) { assert := assert.New(t) - //Create a prometheus collector using the config file 'sample_config_prometheus_filtered.json' + // Create a prometheus collector using the config file 'sample_config_prometheus_filtered.json' configFile, err := ioutil.ReadFile("config/sample_config_prometheus_filtered.json") - collector, err := NewPrometheusCollector("Prometheus", configFile, 100) + containerHandler := containertest.NewMockContainerHandler("mockContainer") + collector, err := NewPrometheusCollector("Prometheus", configFile, 100, containerHandler, http.DefaultClient) assert.NoError(err) assert.Equal(collector.name, "Prometheus") - assert.Equal(collector.configFile.Endpoint, "http://localhost:8080/metrics") + assert.Equal(collector.configFile.Endpoint.URL, "http://localhost:8080/metrics") tempServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - text := "# HELP go_gc_duration_seconds A summary of the GC invocation durations.\n" - text += "# TYPE go_gc_duration_seconds summary\n" - text += "go_gc_duration_seconds{quantile=\"0\"} 5.8348000000000004e-05\n" - text += "go_gc_duration_seconds{quantile=\"1\"} 0.000499764\n" - text += "# HELP go_goroutines Number of goroutines that currently exist.\n" - text += "# TYPE go_goroutines gauge\n" - text += "go_goroutines 16" + text := `# HELP go_gc_duration_seconds A summary of the GC invocation durations. +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{quantile="0"} 5.8348000000000004e-05 +go_gc_duration_seconds{quantile="1"} 0.000499764 +go_gc_duration_seconds_sum 1.7560473e+07 +go_gc_duration_seconds_count 2693 +# HELP go_goroutines Number of goroutines that currently exist. +# TYPE go_goroutines gauge +go_goroutines 16 +` fmt.Fprintln(w, text) })) defer tempServer.Close() - collector.configFile.Endpoint = tempServer.URL + collector.configFile.Endpoint.URL = tempServer.URL metrics := map[string][]v1.MetricVal{} _, metrics, errMetric := collector.Collect(metrics) @@ -130,8 +221,9 @@ func TestPrometheusFiltersMetricsCountLimit(t *testing.T) { assert := assert.New(t) - //Create a prometheus collector using the config file 'sample_config_prometheus_filtered.json' + // Create a prometheus collector using the config file 'sample_config_prometheus_filtered.json' configFile, err := ioutil.ReadFile("config/sample_config_prometheus_filtered.json") - _, err = NewPrometheusCollector("Prometheus", configFile, 1) + containerHandler := containertest.NewMockContainerHandler("mockContainer") + _, err = NewPrometheusCollector("Prometheus", configFile, 1, containerHandler, http.DefaultClient) assert.Error(err) } diff -Nru cadvisor-0.23.0+dfsg/collector/util.go cadvisor-0.25.0+dfsg/collector/util.go --- cadvisor-0.23.0+dfsg/collector/util.go 1970-01-01 00:00:00.000000000 +0000 +++ cadvisor-0.25.0+dfsg/collector/util.go 2017-03-09 23:01:14.000000000 +0000 @@ -0,0 +1,26 @@ +// Copyright 2016 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 collector + +import "github.com/google/cadvisor/container" + +func (endpointConfig *EndpointConfig) configure(containerHandler container.ContainerHandler) { + //If the exact URL was not specified, generate it based on the ip address of the container. + endpoint := endpointConfig + if endpoint.URL == "" { + ipAddress := containerHandler.GetContainerIPAddress() + endpointConfig.URL = endpoint.URLConfig.Protocol + "://" + ipAddress + ":" + endpoint.URLConfig.Port.String() + endpoint.URLConfig.Path + } +} diff -Nru cadvisor-0.23.0+dfsg/container/common/fsHandler.go cadvisor-0.25.0+dfsg/container/common/fsHandler.go --- cadvisor-0.23.0+dfsg/container/common/fsHandler.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/common/fsHandler.go 2017-03-09 23:01:14.000000000 +0000 @@ -16,6 +16,7 @@ package common import ( + "fmt" "sync" "time" @@ -26,71 +27,85 @@ type FsHandler interface { Start() - Usage() (baseUsageBytes uint64, totalUsageBytes uint64) + Usage() FsUsage Stop() } +type FsUsage struct { + BaseUsageBytes uint64 + TotalUsageBytes uint64 + InodeUsage uint64 +} + type realFsHandler struct { sync.RWMutex - lastUpdate time.Time - usageBytes uint64 - baseUsageBytes uint64 - period time.Duration - minPeriod time.Duration - rootfs string - extraDir string - fsInfo fs.FsInfo + lastUpdate time.Time + usage FsUsage + period time.Duration + minPeriod time.Duration + rootfs string + extraDir string + fsInfo fs.FsInfo // Tells the container to stop. stopChan chan struct{} } const ( - longDu = time.Second - duTimeout = time.Minute - maxDuBackoffFactor = 20 + longOp = time.Second + timeout = 2 * time.Minute + maxBackoffFactor = 20 ) +const DefaultPeriod = time.Minute + var _ FsHandler = &realFsHandler{} func NewFsHandler(period time.Duration, rootfs, extraDir string, fsInfo fs.FsInfo) FsHandler { return &realFsHandler{ - lastUpdate: time.Time{}, - usageBytes: 0, - baseUsageBytes: 0, - period: period, - minPeriod: period, - rootfs: rootfs, - extraDir: extraDir, - fsInfo: fsInfo, - stopChan: make(chan struct{}, 1), + lastUpdate: time.Time{}, + usage: FsUsage{}, + period: period, + minPeriod: period, + rootfs: rootfs, + extraDir: extraDir, + fsInfo: fsInfo, + stopChan: make(chan struct{}, 1), } } func (fh *realFsHandler) update() error { var ( - baseUsage, extraDirUsage uint64 - err error + baseUsage, extraDirUsage, inodeUsage uint64 + rootDiskErr, rootInodeErr, extraDiskErr error ) // TODO(vishh): Add support for external mounts. if fh.rootfs != "" { - baseUsage, err = fh.fsInfo.GetDirUsage(fh.rootfs, duTimeout) - if err != nil { - return err - } + baseUsage, rootDiskErr = fh.fsInfo.GetDirDiskUsage(fh.rootfs, timeout) + inodeUsage, rootInodeErr = fh.fsInfo.GetDirInodeUsage(fh.rootfs, timeout) } if fh.extraDir != "" { - extraDirUsage, err = fh.fsInfo.GetDirUsage(fh.extraDir, duTimeout) - if err != nil { - return err - } + extraDirUsage, extraDiskErr = fh.fsInfo.GetDirDiskUsage(fh.extraDir, timeout) } + // Wait to handle errors until after all operartions are run. + // An error in one will not cause an early return, skipping others fh.Lock() defer fh.Unlock() fh.lastUpdate = time.Now() - fh.usageBytes = baseUsage + extraDirUsage - fh.baseUsageBytes = baseUsage + if rootDiskErr == nil && fh.rootfs != "" { + fh.usage.InodeUsage = inodeUsage + } + if rootInodeErr == nil && fh.rootfs != "" { + fh.usage.TotalUsageBytes = baseUsage + extraDirUsage + } + if extraDiskErr == nil && fh.extraDir != "" { + fh.usage.BaseUsageBytes = baseUsage + } + // Combine errors into a single error to return + if rootDiskErr != nil || rootInodeErr != nil || extraDiskErr != nil { + return fmt.Errorf("rootDiskErr: %v, rootInodeErr: %v, extraDiskErr: %v", rootDiskErr, rootInodeErr, extraDiskErr) + } return nil } @@ -105,15 +120,15 @@ if err := fh.update(); err != nil { glog.Errorf("failed to collect filesystem stats - %v", err) fh.period = fh.period * 2 - if fh.period > maxDuBackoffFactor*fh.minPeriod { - fh.period = maxDuBackoffFactor * fh.minPeriod + if fh.period > maxBackoffFactor*fh.minPeriod { + fh.period = maxBackoffFactor * fh.minPeriod } } else { fh.period = fh.minPeriod } duration := time.Since(start) - if duration > longDu { - glog.V(2).Infof("`du` on following dirs took %v: %v", duration, []string{fh.rootfs, fh.extraDir}) + if duration > longOp { + glog.V(2).Infof("du and find on following dirs took %v: %v", duration, []string{fh.rootfs, fh.extraDir}) } } } @@ -127,8 +142,8 @@ close(fh.stopChan) } -func (fh *realFsHandler) Usage() (baseUsageBytes, totalUsageBytes uint64) { +func (fh *realFsHandler) Usage() FsUsage { fh.RLock() defer fh.RUnlock() - return fh.baseUsageBytes, fh.usageBytes + return fh.usage } diff -Nru cadvisor-0.23.0+dfsg/container/common/helpers.go cadvisor-0.25.0+dfsg/container/common/helpers.go --- cadvisor-0.23.0+dfsg/container/common/helpers.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/common/helpers.go 2017-03-09 23:01:14.000000000 +0000 @@ -23,6 +23,7 @@ "strings" "time" + "github.com/google/cadvisor/container" info "github.com/google/cadvisor/info/v1" "github.com/google/cadvisor/utils" @@ -109,6 +110,7 @@ spec.HasMemory = true spec.Memory.Limit = readUInt64(memoryRoot, "memory.limit_in_bytes") spec.Memory.SwapLimit = readUInt64(memoryRoot, "memory.memsw.limit_in_bytes") + spec.Memory.Reservation = readUInt64(memoryRoot, "memory.soft_limit_in_bytes") } } @@ -201,3 +203,23 @@ } return false } + +func ListContainers(name string, cgroupPaths map[string]string, listType container.ListType) ([]info.ContainerReference, error) { + containers := make(map[string]struct{}) + for _, cgroupPath := range cgroupPaths { + err := ListDirectories(cgroupPath, name, listType == container.ListRecursive, containers) + if err != nil { + return nil, err + } + } + + // Make into container references. + ret := make([]info.ContainerReference, 0, len(containers)) + for cont := range containers { + ret = append(ret, info.ContainerReference{ + Name: cont, + }) + } + + return ret, nil +} diff -Nru cadvisor-0.23.0+dfsg/container/container.go cadvisor-0.25.0+dfsg/container/container.go --- cadvisor-0.23.0+dfsg/container/container.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/container.go 2017-03-09 23:01:14.000000000 +0000 @@ -27,23 +27,15 @@ ListRecursive ) -// SubcontainerEventType indicates an addition or deletion event. -type SubcontainerEventType int +type ContainerType int const ( - SubcontainerAdd SubcontainerEventType = iota - SubcontainerDelete + ContainerTypeRaw ContainerType = iota + ContainerTypeDocker + ContainerTypeRkt + ContainerTypeSystemd ) -// SubcontainerEvent represents a -type SubcontainerEvent struct { - // The type of event that occurred. - EventType SubcontainerEventType - - // The full container name of the container where the event occurred. - Name string -} - // Interface for container operation handlers. type ContainerHandler interface { // Returns the ContainerReference @@ -58,24 +50,18 @@ // Returns the subcontainers of this container. ListContainers(listType ListType) ([]info.ContainerReference, error) - // Returns the threads inside this container. - ListThreads(listType ListType) ([]int, error) - // Returns the processes inside this container. ListProcesses(listType ListType) ([]int, error) - // Registers a channel to listen for events affecting subcontainers (recursively). - WatchSubcontainers(events chan SubcontainerEvent) error - - // Stops watching for subcontainer changes. - StopWatchingSubcontainers() error - // Returns absolute cgroup path for the requested resource. GetCgroupPath(resource string) (string, error) // Returns container labels, if available. GetContainerLabels() map[string]string + // Returns the container's ip address, if available + GetContainerIPAddress() string + // Returns whether the container still exists. Exists() bool @@ -85,4 +71,7 @@ // Start starts any necessary background goroutines - must be cleaned up in Cleanup(). // It is expected that most implementations will be a no-op. Start() + + // Type of handler + Type() ContainerType } diff -Nru cadvisor-0.23.0+dfsg/container/docker/client.go cadvisor-0.25.0+dfsg/container/docker/client.go --- cadvisor-0.23.0+dfsg/container/docker/client.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/docker/client.go 2017-03-09 23:01:14.000000000 +0000 @@ -20,18 +20,18 @@ import ( "sync" - dclient "github.com/fsouza/go-dockerclient" + dclient "github.com/docker/engine-api/client" ) var ( - dockerClient *dclient.Client - dockerClientErr error - once sync.Once + dockerClient *dclient.Client + dockerClientErr error + dockerClientOnce sync.Once ) func Client() (*dclient.Client, error) { - once.Do(func() { - dockerClient, dockerClientErr = dclient.NewClient(*ArgDockerEndpoint) + dockerClientOnce.Do(func() { + dockerClient, dockerClientErr = dclient.NewClient(*ArgDockerEndpoint, "", nil, nil) }) return dockerClient, dockerClientErr } diff -Nru cadvisor-0.23.0+dfsg/container/docker/docker.go cadvisor-0.25.0+dfsg/container/docker/docker.go --- cadvisor-0.23.0+dfsg/container/docker/docker.go 1970-01-01 00:00:00.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/docker/docker.go 2017-03-09 23:01:14.000000000 +0000 @@ -0,0 +1,163 @@ +// Copyright 2016 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. + +// Provides global docker information. +package docker + +import ( + "fmt" + "strconv" + "strings" + + dockertypes "github.com/docker/engine-api/types" + "golang.org/x/net/context" + + "github.com/google/cadvisor/info/v1" + "github.com/google/cadvisor/machine" +) + +func Status() (v1.DockerStatus, error) { + client, err := Client() + if err != nil { + return v1.DockerStatus{}, fmt.Errorf("unable to communicate with docker daemon: %v", err) + } + dockerInfo, err := client.Info(context.Background()) + if err != nil { + return v1.DockerStatus{}, err + } + + out := v1.DockerStatus{} + out.Version = VersionString() + out.KernelVersion = machine.KernelVersion() + out.OS = dockerInfo.OperatingSystem + out.Hostname = dockerInfo.Name + out.RootDir = dockerInfo.DockerRootDir + out.Driver = dockerInfo.Driver + out.ExecDriver = dockerInfo.ExecutionDriver + out.NumImages = dockerInfo.Images + out.NumContainers = dockerInfo.Containers + out.DriverStatus = make(map[string]string, len(dockerInfo.DriverStatus)) + for _, v := range dockerInfo.DriverStatus { + out.DriverStatus[v[0]] = v[1] + } + return out, nil +} + +func Images() ([]v1.DockerImage, error) { + client, err := Client() + if err != nil { + return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) + } + images, err := client.ImageList(context.Background(), dockertypes.ImageListOptions{All: false}) + if err != nil { + return nil, err + } + + out := []v1.DockerImage{} + const unknownTag = ":" + for _, image := range images { + if len(image.RepoTags) == 1 && image.RepoTags[0] == unknownTag { + // images with repo or tags are uninteresting. + continue + } + di := v1.DockerImage{ + ID: image.ID, + RepoTags: image.RepoTags, + Created: image.Created, + VirtualSize: image.VirtualSize, + Size: image.Size, + } + out = append(out, di) + } + return out, nil + +} + +// Checks whether the dockerInfo reflects a valid docker setup, and returns it if it does, or an +// error otherwise. +func ValidateInfo() (*dockertypes.Info, error) { + client, err := Client() + if err != nil { + return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) + } + + dockerInfo, err := client.Info(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed to detect Docker info: %v", err) + } + + // Fall back to version API if ServerVersion is not set in info. + if dockerInfo.ServerVersion == "" { + version, err := client.ServerVersion(context.Background()) + if err != nil { + return nil, fmt.Errorf("unable to get docker version: %v", err) + } + dockerInfo.ServerVersion = version.Version + } + version, err := parseDockerVersion(dockerInfo.ServerVersion) + if err != nil { + return nil, err + } + + if version[0] < 1 { + return nil, fmt.Errorf("cAdvisor requires docker version %v or above but we have found version %v reported as %q", []int{1, 0, 0}, version, dockerInfo.ServerVersion) + } + + // Check that the libcontainer execdriver is used if the version is < 1.11 + // (execution drivers are no longer supported as of 1.11). + if version[0] <= 1 && version[1] <= 10 && + !strings.HasPrefix(dockerInfo.ExecutionDriver, "native") { + return nil, fmt.Errorf("docker found, but not using native exec driver") + } + + if dockerInfo.Driver == "" { + return nil, fmt.Errorf("failed to find docker storage driver") + } + + return &dockerInfo, nil +} + +func Version() ([]int, error) { + return parseDockerVersion(VersionString()) +} + +func VersionString() string { + docker_version := "Unknown" + client, err := Client() + if err == nil { + version, err := client.ServerVersion(context.Background()) + if err == nil { + docker_version = version.Version + } + } + return docker_version +} + +// TODO: switch to a semantic versioning library. +func parseDockerVersion(full_version_string string) ([]int, error) { + matches := version_re.FindAllStringSubmatch(full_version_string, -1) + if len(matches) != 1 { + return nil, fmt.Errorf("version string \"%v\" doesn't match expected regular expression: \"%v\"", full_version_string, version_regexp_string) + } + version_string_array := matches[0][1:] + version_array := make([]int, 3) + for index, version_string := range version_string_array { + version, err := strconv.Atoi(version_string) + if err != nil { + return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\"", version_string, full_version_string) + } + version_array[index] = version + } + return version_array, nil +} diff -Nru cadvisor-0.23.0+dfsg/container/docker/factory.go cadvisor-0.25.0+dfsg/container/docker/factory.go --- cadvisor-0.23.0+dfsg/container/docker/factory.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/docker/factory.go 2017-03-09 23:01:14.000000000 +0000 @@ -21,25 +21,28 @@ "regexp" "strconv" "strings" + "sync" + "github.com/blang/semver" + dockertypes "github.com/docker/engine-api/types" "github.com/google/cadvisor/container" "github.com/google/cadvisor/container/libcontainer" + "github.com/google/cadvisor/devicemapper" "github.com/google/cadvisor/fs" info "github.com/google/cadvisor/info/v1" + "github.com/google/cadvisor/machine" + "github.com/google/cadvisor/manager/watcher" + dockerutil "github.com/google/cadvisor/utils/docker" - docker "github.com/fsouza/go-dockerclient" + docker "github.com/docker/engine-api/client" "github.com/golang/glog" + "golang.org/x/net/context" ) var ArgDockerEndpoint = flag.String("docker", "unix:///var/run/docker.sock", "docker endpoint") // The namespace under which Docker aliases are unique. -var DockerNamespace = "docker" - -// Basepath to all container specific information that libcontainer stores. -// TODO: Deprecate this flag -var dockerRootDir = flag.String("docker_root", "/var/lib/docker", "Absolute path to the Docker state root directory (default: /var/lib/docker)") -var dockerRunDir = flag.String("docker_run", "/var/run/docker", "Absolute path to the Docker run directory (default: /var/run/docker)") +const DockerNamespace = "docker" // Regexp that identifies docker cgroups, containers started with // --cgroup-parent have another prefix than 'docker' @@ -47,24 +50,36 @@ var dockerEnvWhitelist = flag.String("docker_env_metadata_whitelist", "", "a comma-separated list of environment variable keys that needs to be collected for docker containers") -// TODO(vmarmol): Export run dir too for newer Dockers. -// Directory holding Docker container state information. -func DockerStateDir() string { - return libcontainer.DockerStateDir(*dockerRootDir) -} +var ( + // Basepath to all container specific information that libcontainer stores. + dockerRootDir string -const ( - dockerRootDirKey = "Root Dir" + dockerRootDirFlag = flag.String("docker_root", "/var/lib/docker", "DEPRECATED: docker root is read from docker info (this is a fallback, default: /var/lib/docker)") + + dockerRootDirOnce sync.Once + + // flag that controls globally disabling thin_ls pending future enhancements. + // in production, it has been found that thin_ls makes excessive use of iops. + // in an iops restricted environment, usage of thin_ls must be controlled via blkio. + // pending that enhancement, disable its usage. + disableThinLs = true ) func RootDir() string { - return *dockerRootDir + dockerRootDirOnce.Do(func() { + status, err := Status() + if err == nil && status.RootDir != "" { + dockerRootDir = status.RootDir + } else { + dockerRootDir = *dockerRootDirFlag + } + }) + return dockerRootDir } type storageDriver string const ( - // TODO: Add support for devicemapper storage usage. devicemapperStorageDriver storageDriver = "devicemapper" aufsStorageDriver storageDriver = "aufs" overlayStorageDriver storageDriver = "overlay" @@ -88,6 +103,8 @@ dockerVersion []int ignoreMetrics container.MetricSet + + thinPoolWatcher *devicemapper.ThinPoolWatcher } func (self *dockerFactory) String() string { @@ -114,6 +131,7 @@ metadataEnvs, self.dockerVersion, self.ignoreMetrics, + self.thinPoolWatcher, ) return } @@ -129,29 +147,33 @@ return id } +// isContainerName returns true if the cgroup with associated name +// corresponds to a docker container. func isContainerName(name string) bool { + // always ignore .mount cgroup even if associated with docker and delegate to systemd + if strings.HasSuffix(name, ".mount") { + return false + } return dockerCgroupRegexp.MatchString(path.Base(name)) } // Docker handles all containers under /docker func (self *dockerFactory) CanHandleAndAccept(name string) (bool, bool, error) { - // docker factory accepts all containers it can handle. - canAccept := true - + // if the container is not associated with docker, we can't handle it or accept it. if !isContainerName(name) { - return false, canAccept, fmt.Errorf("invalid container name") + return false, false, nil } // Check if the container is known to docker and it is active. id := ContainerNameToDockerId(name) // We assume that if Inspect fails then the container is not known to docker. - ctnr, err := self.client.InspectContainer(id) + ctnr, err := self.client.ContainerInspect(context.Background(), id) if err != nil || !ctnr.State.Running { - return false, canAccept, fmt.Errorf("error inspecting container: %v", err) + return false, true, fmt.Errorf("error inspecting container: %v", err) } - return true, canAccept, nil + return true, true, nil } func (self *dockerFactory) DebugInfo() map[string][]string { @@ -163,22 +185,97 @@ version_re = regexp.MustCompile(version_regexp_string) ) -// TODO: switch to a semantic versioning library. -func parseDockerVersion(full_version_string string) ([]int, error) { - matches := version_re.FindAllStringSubmatch(full_version_string, -1) - if len(matches) != 1 { - return nil, fmt.Errorf("version string \"%v\" doesn't match expected regular expression: \"%v\"", full_version_string, version_regexp_string) - } - version_string_array := matches[0][1:] - version_array := make([]int, 3) - for index, version_string := range version_string_array { - version, err := strconv.Atoi(version_string) +func startThinPoolWatcher(dockerInfo *dockertypes.Info) (*devicemapper.ThinPoolWatcher, error) { + _, err := devicemapper.ThinLsBinaryPresent() + if err != nil { + return nil, err + } + + if err := ensureThinLsKernelVersion(machine.KernelVersion()); err != nil { + return nil, err + } + + if disableThinLs { + return nil, fmt.Errorf("usage of thin_ls is disabled to preserve iops") + } + + dockerThinPoolName, err := dockerutil.DockerThinPoolName(*dockerInfo) + if err != nil { + return nil, err + } + + dockerMetadataDevice, err := dockerutil.DockerMetadataDevice(*dockerInfo) + if err != nil { + return nil, err + } + + thinPoolWatcher, err := devicemapper.NewThinPoolWatcher(dockerThinPoolName, dockerMetadataDevice) + if err != nil { + return nil, err + } + + go thinPoolWatcher.Start() + return thinPoolWatcher, nil +} + +func ensureThinLsKernelVersion(kernelVersion string) error { + // kernel 4.4.0 has the proper bug fixes to allow thin_ls to work without corrupting the thin pool + minKernelVersion := semver.MustParse("4.4.0") + // RHEL 7 kernel 3.10.0 release >= 366 has the proper bug fixes backported from 4.4.0 to allow + // thin_ls to work without corrupting the thin pool + minRhel7KernelVersion := semver.MustParse("3.10.0") + + matches := version_re.FindStringSubmatch(kernelVersion) + if len(matches) < 4 { + return fmt.Errorf("error parsing kernel version: %q is not a semver", kernelVersion) + } + + sem, err := semver.Make(matches[0]) + if err != nil { + return err + } + + if sem.GTE(minKernelVersion) { + // kernel 4.4+ - good + return nil + } + + // Certain RHEL/Centos 7.x kernels have a backport to fix the corruption bug + if !strings.Contains(kernelVersion, ".el7") { + // not a RHEL 7.x kernel - won't work + return fmt.Errorf("kernel version 4.4.0 or later is required to use thin_ls - you have %q", kernelVersion) + } + + // RHEL/Centos 7.x from here on + if sem.Major != 3 { + // only 3.x kernels *may* work correctly + return fmt.Errorf("RHEL/Centos 7.x kernel version 3.10.0-366 or later is required to use thin_ls - you have %q", kernelVersion) + } + + if sem.GT(minRhel7KernelVersion) { + // 3.10.1+ - good + return nil + } + + if sem.EQ(minRhel7KernelVersion) { + // need to check release + releaseRE := regexp.MustCompile(`^[^-]+-([0-9]+)\.`) + releaseMatches := releaseRE.FindStringSubmatch(kernelVersion) + if len(releaseMatches) != 2 { + return fmt.Errorf("unable to determine RHEL/Centos 7.x kernel release from %q", kernelVersion) + } + + release, err := strconv.Atoi(releaseMatches[1]) if err != nil { - return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\"", version_string, full_version_string) + return fmt.Errorf("error parsing release %q: %v", releaseMatches[1], err) + } + + if release >= 366 { + return nil } - version_array[index] = version } - return version_array, nil + + return fmt.Errorf("RHEL/Centos 7.x kernel version 3.10.0-366 or later is required to use thin_ls - you have %q", kernelVersion) } // Register root container before running this function! @@ -196,15 +293,19 @@ // Version already validated above, assume no error here. dockerVersion, _ := parseDockerVersion(dockerInfo.ServerVersion) - storageDir := dockerInfo.DockerRootDir - if storageDir == "" { - storageDir = *dockerRootDir - } cgroupSubsystems, err := libcontainer.GetCgroupSubsystems() if err != nil { return fmt.Errorf("failed to get cgroup subsystems: %v", err) } + var thinPoolWatcher *devicemapper.ThinPoolWatcher + if storageDriver(dockerInfo.Driver) == devicemapperStorageDriver { + thinPoolWatcher, err = startThinPoolWatcher(dockerInfo) + if err != nil { + glog.Errorf("devicemapper filesystem stats will not be reported: %v", err) + } + } + glog.Infof("Registering Docker factory") f := &dockerFactory{ cgroupSubsystems: cgroupSubsystems, @@ -213,10 +314,11 @@ fsInfo: fsInfo, machineInfoFactory: factory, storageDriver: storageDriver(dockerInfo.Driver), - storageDir: storageDir, + storageDir: RootDir(), ignoreMetrics: ignoreMetrics, + thinPoolWatcher: thinPoolWatcher, } - container.RegisterContainerHandlerFactory(f) + container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw}) return nil } diff -Nru cadvisor-0.23.0+dfsg/container/docker/factory_test.go cadvisor-0.25.0+dfsg/container/docker/factory_test.go --- cadvisor-0.23.0+dfsg/container/docker/factory_test.go 1970-01-01 00:00:00.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/docker/factory_test.go 2017-03-09 23:01:14.000000000 +0000 @@ -0,0 +1,72 @@ +// Copyright 2016 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 docker + +import "testing" + +func TestEnsureThinLsKernelVersion(t *testing.T) { + tests := []struct { + version string + expectedError string + }{ + {"4.4.0-31-generic", ""}, + {"4.4.1", ""}, + {"4.6.4-301.fc24.x86_64", ""}, + {"3.10.0-327.22.2.el7.x86_64", `RHEL/Centos 7.x kernel version 3.10.0-366 or later is required to use thin_ls - you have "3.10.0-327.22.2.el7.x86_64"`}, + {"3.10.0-366.el7.x86_64", ""}, + {"3.10.0-366.el7_3.x86_64", ""}, + {"3.10.0.el7.abc", `unable to determine RHEL/Centos 7.x kernel release from "3.10.0.el7.abc"`}, + {"3.10.0-abc.el7.blarg", `unable to determine RHEL/Centos 7.x kernel release from "3.10.0-abc.el7.blarg"`}, + {"3.10.0-367.el7.x86_64", ""}, + {"3.10.0-366.x86_64", `kernel version 4.4.0 or later is required to use thin_ls - you have "3.10.0-366.x86_64"`}, + {"3.10.1-1.el7.x86_64", ""}, + {"2.0.36", `kernel version 4.4.0 or later is required to use thin_ls - you have "2.0.36"`}, + {"2.1", `error parsing kernel version: "2.1" is not a semver`}, + } + + for _, test := range tests { + err := ensureThinLsKernelVersion(test.version) + if err != nil { + if len(test.expectedError) == 0 { + t.Errorf("%s: expected no error, got %v", test.version, err) + } else if err.Error() != test.expectedError { + t.Errorf("%s: expected error %v, got %v", test.version, test.expectedError, err) + } + } else if err == nil && len(test.expectedError) > 0 { + t.Errorf("%s: expected error %v", test.version, test.expectedError) + } + } +} + +func TestIsContainerName(t *testing.T) { + tests := []struct { + name string + expected bool + }{ + { + name: "/system.slice/var-lib-docker-overlay-9f086b233ab7c786bf8b40b164680b658a8f00e94323868e288d6ce20bc92193-merged.mount", + expected: false, + }, + { + name: "/system.slice/docker-72e5a5ff5eef3c4222a6551b992b9360a99122f77d2229783f0ee0946dfd800e.scope", + expected: true, + }, + } + for _, test := range tests { + if actual := isContainerName(test.name); actual != test.expected { + t.Errorf("%s: expected: %v, actual: %v", test.name, test.expected, actual) + } + } +} diff -Nru cadvisor-0.23.0+dfsg/container/docker/handler.go cadvisor-0.25.0+dfsg/container/docker/handler.go --- cadvisor-0.23.0+dfsg/container/docker/handler.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/docker/handler.go 2017-03-09 23:01:14.000000000 +0000 @@ -25,13 +25,18 @@ "github.com/google/cadvisor/container" "github.com/google/cadvisor/container/common" containerlibcontainer "github.com/google/cadvisor/container/libcontainer" + "github.com/google/cadvisor/devicemapper" "github.com/google/cadvisor/fs" info "github.com/google/cadvisor/info/v1" + dockerutil "github.com/google/cadvisor/utils/docker" - docker "github.com/fsouza/go-dockerclient" + docker "github.com/docker/engine-api/client" + dockercontainer "github.com/docker/engine-api/types/container" + "github.com/golang/glog" "github.com/opencontainers/runc/libcontainer/cgroups" cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs" libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs" + "golang.org/x/net/context" ) const ( @@ -55,10 +60,18 @@ // Manager of this container's cgroups. cgroupManager cgroups.Manager + // the docker storage driver storageDriver storageDriver fsInfo fs.FsInfo rootfsStorageDir string + // devicemapper state + + // the devicemapper poolname + poolName string + // the devicemapper device id for the container + deviceID string + // Time at which this container was created. creationTime time.Time @@ -76,14 +89,22 @@ rootFs string // The network mode of the container - networkMode string + networkMode dockercontainer.NetworkMode // Filesystem handler. fsHandler common.FsHandler + // The IP address of the container + ipAddress string + ignoreMetrics container.MetricSet + + // thin pool watcher + thinPoolWatcher *devicemapper.ThinPoolWatcher } +var _ container.ContainerHandler = &dockerContainerHandler{} + func getRwLayerID(containerID, storageDir string, sd storageDriver, dockerVersion []int) (string, error) { const ( // Docker version >=1.10.0 have a randomized ID for the root fs of a container. @@ -101,6 +122,7 @@ return string(bytes), err } +// newDockerContainerHandler returns a new container.ContainerHandler func newDockerContainerHandler( client *docker.Client, name string, @@ -113,6 +135,7 @@ metadataEnvs []string, dockerVersion []int, ignoreMetrics container.MetricSet, + thinPoolWatcher *devicemapper.ThinPoolWatcher, ) (container.ContainerHandler, error) { // Create the cgroup paths. cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints)) @@ -144,14 +167,27 @@ if err != nil { return nil, err } - var rootfsStorageDir string + + // Determine the rootfs storage dir OR the pool name to determine the device + var ( + rootfsStorageDir string + poolName string + ) switch storageDriver { case aufsStorageDriver: rootfsStorageDir = path.Join(storageDir, string(aufsStorageDriver), aufsRWLayer, rwLayerID) case overlayStorageDriver: rootfsStorageDir = path.Join(storageDir, string(overlayStorageDriver), rwLayerID) + case devicemapperStorageDriver: + status, err := Status() + if err != nil { + return nil, fmt.Errorf("unable to determine docker status: %v", err) + } + + poolName = status.DriverStatus[dockerutil.DriverStatusPoolName] } + // TODO: extract object mother method handler := &dockerContainerHandler{ id: id, client: client, @@ -162,21 +198,24 @@ storageDriver: storageDriver, fsInfo: fsInfo, rootFs: rootFs, + poolName: poolName, rootfsStorageDir: rootfsStorageDir, envs: make(map[string]string), ignoreMetrics: ignoreMetrics, - } - - if !ignoreMetrics.Has(container.DiskUsageMetrics) { - handler.fsHandler = common.NewFsHandler(time.Minute, rootfsStorageDir, otherStorageDir, fsInfo) + thinPoolWatcher: thinPoolWatcher, } // We assume that if Inspect fails then the container is not known to docker. - ctnr, err := client.InspectContainer(id) + ctnr, err := client.ContainerInspect(context.Background(), id) if err != nil { return nil, fmt.Errorf("failed to inspect container %q: %v", id, err) } - handler.creationTime = ctnr.Created + // Timestamp returned by Docker is in time.RFC3339Nano format. + handler.creationTime, err = time.Parse(time.RFC3339Nano, ctnr.Created) + if err != nil { + // 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) + } handler.pid = ctnr.State.Pid // Add the name and bare ID as aliases of the container. @@ -184,13 +223,40 @@ handler.labels = ctnr.Config.Labels handler.image = ctnr.Config.Image handler.networkMode = ctnr.HostConfig.NetworkMode + handler.deviceID = ctnr.GraphDriver.Data["DeviceId"] + + // Obtain the IP address for the contianer. + // If the NetworkMode starts with 'container:' then we need to use the IP address of the container specified. + // This happens in cases such as kubernetes where the containers doesn't have an IP address itself and we need to use the pod's address + ipAddress := ctnr.NetworkSettings.IPAddress + networkMode := string(ctnr.HostConfig.NetworkMode) + if ipAddress == "" && strings.HasPrefix(networkMode, "container:") { + containerId := strings.TrimPrefix(networkMode, "container:") + c, err := client.ContainerInspect(context.Background(), containerId) + if err != nil { + return nil, fmt.Errorf("failed to inspect container %q: %v", id, err) + } + ipAddress = c.NetworkSettings.IPAddress + } + + handler.ipAddress = ipAddress + + if !ignoreMetrics.Has(container.DiskUsageMetrics) { + handler.fsHandler = &dockerFsHandler{ + fsHandler: common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, otherStorageDir, fsInfo), + thinPoolWatcher: thinPoolWatcher, + deviceID: handler.deviceID, + } + } // split env vars to get metadata map. for _, exposedEnv := range metadataEnvs { for _, envVar := range ctnr.Config.Env { - splits := strings.SplitN(envVar, "=", 2) - if splits[0] == exposedEnv { - handler.envs[strings.ToLower(exposedEnv)] = splits[1] + if envVar != "" { + splits := strings.SplitN(envVar, "=", 2) + if len(splits) == 2 && splits[0] == exposedEnv { + handler.envs[strings.ToLower(exposedEnv)] = splits[1] + } } } } @@ -198,6 +264,51 @@ return handler, nil } +// dockerFsHandler is a composite FsHandler implementation the incorporates +// the common fs handler and a devicemapper ThinPoolWatcher. +type dockerFsHandler struct { + fsHandler common.FsHandler + + // thinPoolWatcher is the devicemapper thin pool watcher + thinPoolWatcher *devicemapper.ThinPoolWatcher + // deviceID is the id of the container's fs device + deviceID string +} + +var _ common.FsHandler = &dockerFsHandler{} + +func (h *dockerFsHandler) Start() { + h.fsHandler.Start() +} + +func (h *dockerFsHandler) Stop() { + h.fsHandler.Stop() +} + +func (h *dockerFsHandler) Usage() common.FsUsage { + usage := h.fsHandler.Usage() + + // When devicemapper is the storage driver, the base usage of the container comes from the thin pool. + // We still need the result of the fsHandler for any extra storage associated with the container. + // To correctly factor in the thin pool usage, we should: + // * Usage the thin pool usage as the base usage + // * Calculate the overall usage by adding the overall usage from the fs handler to the thin pool usage + if h.thinPoolWatcher != nil { + thinPoolUsage, err := h.thinPoolWatcher.GetUsage(h.deviceID) + if err != nil { + // TODO: ideally we should keep track of how many times we failed to get the usage for this + // device vs how many refreshes of the cache there have been, and display an error e.g. if we've + // had at least 1 refresh and we still can't find the device. + glog.V(5).Infof("unable to get fs usage from thin pool for device %s: %v", h.deviceID, err) + } else { + usage.BaseUsageBytes = thinPoolUsage + usage.TotalUsageBytes += thinPoolUsage + } + } + + return usage +} + func (self *dockerContainerHandler) Start() { if self.fsHandler != nil { self.fsHandler.Start() @@ -222,7 +333,7 @@ func (self *dockerContainerHandler) needNet() bool { if !self.ignoreMetrics.Has(container.NetworkUsageMetrics) { - return !strings.HasPrefix(self.networkMode, "container:") + return !self.networkMode.IsContainer() } return false } @@ -242,17 +353,22 @@ if self.ignoreMetrics.Has(container.DiskUsageMetrics) { return nil } + var device string switch self.storageDriver { + case devicemapperStorageDriver: + // Device has to be the pool name to correlate with the device name as + // set in the machine info filesystems. + device = self.poolName case aufsStorageDriver, overlayStorageDriver, zfsStorageDriver: + deviceInfo, err := self.fsInfo.GetDirFsDevice(self.rootfsStorageDir) + if err != nil { + return fmt.Errorf("unable to determine device info for dir: %v: %v", self.rootfsStorageDir, err) + } + device = deviceInfo.Device default: return nil } - deviceInfo, err := self.fsInfo.GetDirFsDevice(self.rootfsStorageDir) - if err != nil { - return err - } - mi, err := self.machineInfoFactory.GetMachineInfo() if err != nil { return err @@ -265,16 +381,19 @@ // Docker does not impose any filesystem limits for containers. So use capacity as limit. for _, fs := range mi.Filesystems { - if fs.Device == deviceInfo.Device { + if fs.Device == device { limit = fs.Capacity fsType = fs.Type break } } - fsStat := info.FsStats{Device: deviceInfo.Device, Type: fsType, Limit: limit} + fsStat := info.FsStats{Device: device, Type: fsType, Limit: limit} + usage := self.fsHandler.Usage() + fsStat.BaseUsage = usage.BaseUsageBytes + fsStat.Usage = usage.TotalUsageBytes + fsStat.Inodes = usage.InodeUsage - fsStat.BaseUsage, fsStat.Usage = self.fsHandler.Usage() stats.Filesystem = append(stats.Filesystem, fsStat) return nil @@ -316,96 +435,22 @@ return path, nil } -func (self *dockerContainerHandler) ListThreads(listType container.ListType) ([]int, error) { - // TODO(vmarmol): Implement. - return nil, nil -} - func (self *dockerContainerHandler) GetContainerLabels() map[string]string { return self.labels } -func (self *dockerContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { - return containerlibcontainer.GetProcesses(self.cgroupManager) +func (self *dockerContainerHandler) GetContainerIPAddress() string { + return self.ipAddress } -func (self *dockerContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error { - return fmt.Errorf("watch is unimplemented in the Docker container driver") -} - -func (self *dockerContainerHandler) StopWatchingSubcontainers() error { - // No-op for Docker driver. - return nil +func (self *dockerContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { + return containerlibcontainer.GetProcesses(self.cgroupManager) } func (self *dockerContainerHandler) Exists() bool { return common.CgroupExists(self.cgroupPaths) } -func DockerInfo() (docker.DockerInfo, error) { - client, err := Client() - if err != nil { - return docker.DockerInfo{}, fmt.Errorf("unable to communicate with docker daemon: %v", err) - } - info, err := client.Info() - if err != nil { - return docker.DockerInfo{}, err - } - return *info, nil -} - -func DockerImages() ([]docker.APIImages, error) { - client, err := Client() - if err != nil { - return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) - } - images, err := client.ListImages(docker.ListImagesOptions{All: false}) - if err != nil { - return nil, err - } - return images, nil -} - -// Checks whether the dockerInfo reflects a valid docker setup, and returns it if it does, or an -// error otherwise. -func ValidateInfo() (*docker.DockerInfo, error) { - client, err := Client() - if err != nil { - return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) - } - - dockerInfo, err := client.Info() - if err != nil { - return nil, fmt.Errorf("failed to detect Docker info: %v", err) - } - - // Fall back to version API if ServerVersion is not set in info. - if dockerInfo.ServerVersion == "" { - version, err := client.Version() - if err != nil { - return nil, fmt.Errorf("unable to get docker version: %v", err) - } - dockerInfo.ServerVersion = version.Get("Version") - } - version, err := parseDockerVersion(dockerInfo.ServerVersion) - if err != nil { - return nil, err - } - - if version[0] < 1 { - return nil, fmt.Errorf("cAdvisor requires docker version %v or above but we have found version %v reported as %q", []int{1, 0, 0}, version, dockerInfo.ServerVersion) - } - - // Check that the libcontainer execdriver is used if the version is < 1.11 - // (execution drivers are no longer supported as of 1.11). - if version[0] <= 1 && version[1] <= 10 && - !strings.HasPrefix(dockerInfo.ExecutionDriver, "native") { - return nil, fmt.Errorf("docker found, but not using native exec driver") - } - - if dockerInfo.Driver == "" { - return nil, fmt.Errorf("failed to find docker storage driver") - } - - return dockerInfo, nil +func (self *dockerContainerHandler) Type() container.ContainerType { + return container.ContainerTypeDocker } diff -Nru cadvisor-0.23.0+dfsg/container/factory.go cadvisor-0.25.0+dfsg/container/factory.go --- cadvisor-0.23.0+dfsg/container/factory.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/factory.go 2017-03-09 23:01:14.000000000 +0000 @@ -18,6 +18,8 @@ "fmt" "sync" + "github.com/google/cadvisor/manager/watcher" + "github.com/golang/glog" ) @@ -67,17 +69,19 @@ // TODO(vmarmol): Consider not making this global. // Global list of factories. var ( - factories []ContainerHandlerFactory + factories = map[watcher.ContainerWatchSource][]ContainerHandlerFactory{} factoriesLock sync.RWMutex ) // Register a ContainerHandlerFactory. These should be registered from least general to most general // as they will be asked in order whether they can handle a particular container. -func RegisterContainerHandlerFactory(factory ContainerHandlerFactory) { +func RegisterContainerHandlerFactory(factory ContainerHandlerFactory, watchTypes []watcher.ContainerWatchSource) { factoriesLock.Lock() defer factoriesLock.Unlock() - factories = append(factories, factory) + for _, watchType := range watchTypes { + factories[watchType] = append(factories[watchType], factory) + } } // Returns whether there are any container handler factories registered. @@ -89,12 +93,12 @@ } // Create a new ContainerHandler for the specified container. -func NewContainerHandler(name string, inHostNamespace bool) (ContainerHandler, bool, error) { +func NewContainerHandler(name string, watchType watcher.ContainerWatchSource, inHostNamespace bool) (ContainerHandler, bool, error) { factoriesLock.RLock() defer factoriesLock.RUnlock() // Create the ContainerHandler with the first factory that supports it. - for _, factory := range factories { + for _, factory := range factories[watchType] { canHandle, canAccept, err := factory.CanHandleAndAccept(name) if err != nil { glog.V(4).Infof("Error trying to work out if we can handle %s: %v", name, err) @@ -120,7 +124,7 @@ factoriesLock.Lock() defer factoriesLock.Unlock() - factories = make([]ContainerHandlerFactory, 0, 4) + factories = map[watcher.ContainerWatchSource][]ContainerHandlerFactory{} } func DebugInfo() map[string][]string { @@ -129,9 +133,11 @@ // Get debug information for all factories. out := make(map[string][]string) - for _, factory := range factories { - for k, v := range factory.DebugInfo() { - out[k] = v + for _, factoriesSlice := range factories { + for _, factory := range factoriesSlice { + for k, v := range factory.DebugInfo() { + out[k] = v + } } } return out diff -Nru cadvisor-0.23.0+dfsg/container/factory_test.go cadvisor-0.25.0+dfsg/container/factory_test.go --- cadvisor-0.23.0+dfsg/container/factory_test.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/factory_test.go 2017-03-09 23:01:14.000000000 +0000 @@ -12,11 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -package container +package container_test import ( "testing" + "github.com/google/cadvisor/container" + containertest "github.com/google/cadvisor/container/testing" + "github.com/google/cadvisor/manager/watcher" + "github.com/stretchr/testify/mock" ) @@ -39,17 +43,17 @@ return self.CanHandleValue, self.CanAcceptValue, nil } -func (self *mockContainerHandlerFactory) NewContainerHandler(name string, isHostNamespace bool) (ContainerHandler, error) { +func (self *mockContainerHandlerFactory) NewContainerHandler(name string, isHostNamespace bool) (container.ContainerHandler, error) { args := self.Called(name) - return args.Get(0).(ContainerHandler), args.Error(1) + return args.Get(0).(container.ContainerHandler), args.Error(1) } const testContainerName = "/test" -var mockFactory FactoryForMockContainerHandler +var mockFactory containertest.FactoryForMockContainerHandler func TestNewContainerHandler_FirstMatches(t *testing.T) { - ClearContainerHandlerFactories() + container.ClearContainerHandlerFactories() // Register one allways yes factory. allwaysYes := &mockContainerHandlerFactory{ @@ -57,7 +61,7 @@ CanHandleValue: true, CanAcceptValue: true, } - RegisterContainerHandlerFactory(allwaysYes) + container.RegisterContainerHandlerFactory(allwaysYes, []watcher.ContainerWatchSource{watcher.Raw}) // The yes factory should be asked to create the ContainerHandler. mockContainer, err := mockFactory.NewContainerHandler(testContainerName, true) @@ -66,7 +70,7 @@ } allwaysYes.On("NewContainerHandler", testContainerName).Return(mockContainer, nil) - cont, _, err := NewContainerHandler(testContainerName, true) + cont, _, err := container.NewContainerHandler(testContainerName, watcher.Raw, true) if err != nil { t.Error(err) } @@ -76,7 +80,7 @@ } func TestNewContainerHandler_SecondMatches(t *testing.T) { - ClearContainerHandlerFactories() + container.ClearContainerHandlerFactories() // Register one allways no and one always yes factory. allwaysNo := &mockContainerHandlerFactory{ @@ -84,13 +88,13 @@ CanHandleValue: false, CanAcceptValue: true, } - RegisterContainerHandlerFactory(allwaysNo) + container.RegisterContainerHandlerFactory(allwaysNo, []watcher.ContainerWatchSource{watcher.Raw}) allwaysYes := &mockContainerHandlerFactory{ Name: "yes", CanHandleValue: true, CanAcceptValue: true, } - RegisterContainerHandlerFactory(allwaysYes) + container.RegisterContainerHandlerFactory(allwaysYes, []watcher.ContainerWatchSource{watcher.Raw}) // The yes factory should be asked to create the ContainerHandler. mockContainer, err := mockFactory.NewContainerHandler(testContainerName, true) @@ -99,7 +103,7 @@ } allwaysYes.On("NewContainerHandler", testContainerName).Return(mockContainer, nil) - cont, _, err := NewContainerHandler(testContainerName, true) + cont, _, err := container.NewContainerHandler(testContainerName, watcher.Raw, true) if err != nil { t.Error(err) } @@ -109,7 +113,7 @@ } func TestNewContainerHandler_NoneMatch(t *testing.T) { - ClearContainerHandlerFactories() + container.ClearContainerHandlerFactories() // Register two allways no factories. allwaysNo1 := &mockContainerHandlerFactory{ @@ -117,22 +121,22 @@ CanHandleValue: false, CanAcceptValue: true, } - RegisterContainerHandlerFactory(allwaysNo1) + container.RegisterContainerHandlerFactory(allwaysNo1, []watcher.ContainerWatchSource{watcher.Raw}) allwaysNo2 := &mockContainerHandlerFactory{ Name: "no", CanHandleValue: false, CanAcceptValue: true, } - RegisterContainerHandlerFactory(allwaysNo2) + container.RegisterContainerHandlerFactory(allwaysNo2, []watcher.ContainerWatchSource{watcher.Raw}) - _, _, err := NewContainerHandler(testContainerName, true) + _, _, err := container.NewContainerHandler(testContainerName, watcher.Raw, true) if err == nil { t.Error("Expected NewContainerHandler to fail") } } func TestNewContainerHandler_Accept(t *testing.T) { - ClearContainerHandlerFactories() + container.ClearContainerHandlerFactories() // Register handler that can handle the container, but can't accept it. cannotHandle := &mockContainerHandlerFactory{ @@ -140,15 +144,15 @@ CanHandleValue: false, CanAcceptValue: true, } - RegisterContainerHandlerFactory(cannotHandle) + container.RegisterContainerHandlerFactory(cannotHandle, []watcher.ContainerWatchSource{watcher.Raw}) cannotAccept := &mockContainerHandlerFactory{ Name: "no", CanHandleValue: true, CanAcceptValue: false, } - RegisterContainerHandlerFactory(cannotAccept) + container.RegisterContainerHandlerFactory(cannotAccept, []watcher.ContainerWatchSource{watcher.Raw}) - _, accept, err := NewContainerHandler(testContainerName, true) + _, accept, err := container.NewContainerHandler(testContainerName, watcher.Raw, true) if err != nil { t.Error("Expected NewContainerHandler to succeed") } diff -Nru cadvisor-0.23.0+dfsg/container/libcontainer/helpers.go cadvisor-0.25.0+dfsg/container/libcontainer/helpers.go --- cadvisor-0.23.0+dfsg/container/libcontainer/helpers.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/libcontainer/helpers.go 2017-03-09 23:01:14.000000000 +0000 @@ -45,7 +45,7 @@ // Get information about the cgroup subsystems. func GetCgroupSubsystems() (CgroupSubsystems, error) { // Get all cgroup mounts. - allCgroups, err := cgroups.GetCgroupMounts() + allCgroups, err := cgroups.GetCgroupMounts(true) if err != nil { return CgroupSubsystems{}, err } @@ -89,7 +89,7 @@ libcontainerStats := &libcontainer.Stats{ CgroupStats: cgroupStats, } - stats := toContainerStats(libcontainerStats) + stats := newContainerStats(libcontainerStats) // If we know the pid then get network stats from /proc//net/dev if pid == 0 { @@ -299,10 +299,6 @@ return pids, nil } -func DockerStateDir(dockerRoot string) string { - return path.Join(dockerRoot, "containers") -} - func DiskStatsCopy0(major, minor uint64) *info.PerDiskStats { disk := info.PerDiskStats{ Major: major, @@ -354,7 +350,7 @@ } // Convert libcontainer stats to info.ContainerStats. -func toContainerStats0(s *cgroups.Stats, ret *info.ContainerStats) { +func setCpuStats(s *cgroups.Stats, ret *info.ContainerStats) { ret.Cpu.Usage.User = s.CpuStats.CpuUsage.UsageInUsermode ret.Cpu.Usage.System = s.CpuStats.CpuUsage.UsageInKernelmode n := len(s.CpuStats.CpuUsage.PercpuUsage) @@ -365,9 +361,13 @@ ret.Cpu.Usage.PerCpu[i] = s.CpuStats.CpuUsage.PercpuUsage[i] ret.Cpu.Usage.Total += s.CpuStats.CpuUsage.PercpuUsage[i] } + + ret.Cpu.CFS.Periods = s.CpuStats.ThrottlingData.Periods + ret.Cpu.CFS.ThrottledPeriods = s.CpuStats.ThrottlingData.ThrottledPeriods + ret.Cpu.CFS.ThrottledTime = s.CpuStats.ThrottlingData.ThrottledTime } -func toContainerStats1(s *cgroups.Stats, ret *info.ContainerStats) { +func setDiskIoStats(s *cgroups.Stats, ret *info.ContainerStats) { ret.DiskIo.IoServiceBytes = DiskStatsCopy(s.BlkioStats.IoServiceBytesRecursive) ret.DiskIo.IoServiced = DiskStatsCopy(s.BlkioStats.IoServicedRecursive) ret.DiskIo.IoQueued = DiskStatsCopy(s.BlkioStats.IoQueuedRecursive) @@ -378,11 +378,12 @@ ret.DiskIo.IoTime = DiskStatsCopy(s.BlkioStats.IoTimeRecursive) } -func toContainerStats2(s *cgroups.Stats, ret *info.ContainerStats) { +func setMemoryStats(s *cgroups.Stats, ret *info.ContainerStats) { ret.Memory.Usage = s.MemoryStats.Usage.Usage ret.Memory.Failcnt = s.MemoryStats.Usage.Failcnt ret.Memory.Cache = s.MemoryStats.Stats["cache"] ret.Memory.RSS = s.MemoryStats.Stats["rss"] + ret.Memory.Swap = s.MemoryStats.Stats["swap"] if v, ok := s.MemoryStats.Stats["pgfault"]; ok { ret.Memory.ContainerData.Pgfault = v ret.Memory.HierarchicalData.Pgfault = v @@ -391,26 +392,19 @@ ret.Memory.ContainerData.Pgmajfault = v ret.Memory.HierarchicalData.Pgmajfault = v } - if v, ok := s.MemoryStats.Stats["total_inactive_anon"]; ok { - workingSet := ret.Memory.Usage + + workingSet := ret.Memory.Usage + if v, ok := s.MemoryStats.Stats["total_inactive_file"]; ok { if workingSet < v { workingSet = 0 } else { workingSet -= v } - - if v, ok := s.MemoryStats.Stats["total_inactive_file"]; ok { - if workingSet < v { - workingSet = 0 - } else { - workingSet -= v - } - } - ret.Memory.WorkingSet = workingSet } + ret.Memory.WorkingSet = workingSet } -func toContainerStats3(libcontainerStats *libcontainer.Stats, ret *info.ContainerStats) { +func setNetworkStats(libcontainerStats *libcontainer.Stats, ret *info.ContainerStats) { ret.Network.Interfaces = make([]info.InterfaceStats, len(libcontainerStats.Interfaces)) for i := range libcontainerStats.Interfaces { ret.Network.Interfaces[i] = info.InterfaceStats{ @@ -432,18 +426,18 @@ } } -func toContainerStats(libcontainerStats *libcontainer.Stats) *info.ContainerStats { - s := libcontainerStats.CgroupStats - ret := new(info.ContainerStats) - ret.Timestamp = time.Now() - - if s != nil { - toContainerStats0(s, ret) - toContainerStats1(s, ret) - toContainerStats2(s, ret) +func newContainerStats(libcontainerStats *libcontainer.Stats) *info.ContainerStats { + ret := &info.ContainerStats{ + Timestamp: time.Now(), + } + + if s := libcontainerStats.CgroupStats; s != nil { + setCpuStats(s, ret) + setDiskIoStats(s, ret) + setMemoryStats(s, ret) } if len(libcontainerStats.Interfaces) > 0 { - toContainerStats3(libcontainerStats, ret) + setNetworkStats(libcontainerStats, ret) } return ret } diff -Nru cadvisor-0.23.0+dfsg/container/mock.go cadvisor-0.25.0+dfsg/container/mock.go --- cadvisor-0.23.0+dfsg/container/mock.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/mock.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,129 +0,0 @@ -// Copyright 2014 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. - -// +build test - -package container - -import ( - info "github.com/google/cadvisor/info/v1" - - "github.com/stretchr/testify/mock" -) - -// This struct mocks a container handler. -type MockContainerHandler struct { - mock.Mock - Name string - Aliases []string -} - -func NewMockContainerHandler(containerName string) *MockContainerHandler { - return &MockContainerHandler{ - Name: containerName, - } -} - -// If self.Name is not empty, then ContainerReference() will return self.Name and self.Aliases. -// Otherwise, it will use the value provided by .On().Return(). -func (self *MockContainerHandler) ContainerReference() (info.ContainerReference, error) { - if len(self.Name) > 0 { - var aliases []string - if len(self.Aliases) > 0 { - aliases = make([]string, len(self.Aliases)) - copy(aliases, self.Aliases) - } - return info.ContainerReference{ - Name: self.Name, - Aliases: aliases, - }, nil - } - args := self.Called() - return args.Get(0).(info.ContainerReference), args.Error(1) -} - -func (self *MockContainerHandler) Start() {} - -func (self *MockContainerHandler) Cleanup() {} - -func (self *MockContainerHandler) GetSpec() (info.ContainerSpec, error) { - args := self.Called() - return args.Get(0).(info.ContainerSpec), args.Error(1) -} - -func (self *MockContainerHandler) GetStats() (*info.ContainerStats, error) { - args := self.Called() - return args.Get(0).(*info.ContainerStats), args.Error(1) -} - -func (self *MockContainerHandler) ListContainers(listType ListType) ([]info.ContainerReference, error) { - args := self.Called(listType) - return args.Get(0).([]info.ContainerReference), args.Error(1) -} - -func (self *MockContainerHandler) ListThreads(listType ListType) ([]int, error) { - args := self.Called(listType) - return args.Get(0).([]int), args.Error(1) -} - -func (self *MockContainerHandler) ListProcesses(listType ListType) ([]int, error) { - args := self.Called(listType) - return args.Get(0).([]int), args.Error(1) -} - -func (self *MockContainerHandler) WatchSubcontainers(events chan SubcontainerEvent) error { - args := self.Called(events) - return args.Error(0) -} - -func (self *MockContainerHandler) StopWatchingSubcontainers() error { - args := self.Called() - return args.Error(0) -} - -func (self *MockContainerHandler) Exists() bool { - args := self.Called() - return args.Get(0).(bool) -} - -func (self *MockContainerHandler) GetCgroupPath(path string) (string, error) { - args := self.Called(path) - return args.Get(0).(string), args.Error(1) -} - -func (self *MockContainerHandler) GetContainerLabels() map[string]string { - args := self.Called() - return args.Get(0).(map[string]string) -} - -type FactoryForMockContainerHandler struct { - Name string - PrepareContainerHandlerFunc func(name string, handler *MockContainerHandler) -} - -func (self *FactoryForMockContainerHandler) String() string { - return self.Name -} - -func (self *FactoryForMockContainerHandler) NewContainerHandler(name string, inHostNamespace bool) (ContainerHandler, error) { - handler := &MockContainerHandler{} - if self.PrepareContainerHandlerFunc != nil { - self.PrepareContainerHandlerFunc(name, handler) - } - return handler, nil -} - -func (self *FactoryForMockContainerHandler) CanHandle(name string) bool { - return true -} diff -Nru cadvisor-0.23.0+dfsg/container/raw/factory.go cadvisor-0.25.0+dfsg/container/raw/factory.go --- cadvisor-0.23.0+dfsg/container/raw/factory.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/raw/factory.go 2017-03-09 23:01:14.000000000 +0000 @@ -23,6 +23,7 @@ "github.com/google/cadvisor/container/libcontainer" "github.com/google/cadvisor/fs" info "github.com/google/cadvisor/info/v1" + watch "github.com/google/cadvisor/manager/watcher" "github.com/golang/glog" ) @@ -90,6 +91,6 @@ watcher: watcher, ignoreMetrics: ignoreMetrics, } - container.RegisterContainerHandlerFactory(factory) + container.RegisterContainerHandlerFactory(factory, []watch.ContainerWatchSource{watch.Raw}) return nil } diff -Nru cadvisor-0.23.0+dfsg/container/raw/handler.go cadvisor-0.25.0+dfsg/container/raw/handler.go --- cadvisor-0.23.0+dfsg/container/raw/handler.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/raw/handler.go 2017-03-09 23:01:14.000000000 +0000 @@ -17,22 +17,18 @@ import ( "fmt" - "io/ioutil" - "path" - "strings" "github.com/google/cadvisor/container" "github.com/google/cadvisor/container/common" "github.com/google/cadvisor/container/libcontainer" "github.com/google/cadvisor/fs" info "github.com/google/cadvisor/info/v1" - "github.com/google/cadvisor/utils/machine" + "github.com/google/cadvisor/machine" "github.com/golang/glog" "github.com/opencontainers/runc/libcontainer/cgroups" cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs" "github.com/opencontainers/runc/libcontainer/configs" - "golang.org/x/exp/inotify" ) type rawContainerHandler struct { @@ -41,12 +37,6 @@ cgroupSubsystems *libcontainer.CgroupSubsystems machineInfoFactory info.MachineInfoFactory - // Inotify event watcher. - watcher *common.InotifyWatcher - - // Signal for watcher thread to stop. - stopWatcher chan error - // Absolute path to the cgroup hierarchies of this container. // (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test") cgroupPaths map[string]string @@ -102,12 +92,10 @@ name: name, cgroupSubsystems: cgroupSubsystems, machineInfoFactory: machineInfoFactory, - stopWatcher: make(chan error), cgroupPaths: cgroupPaths, cgroupManager: cgroupManager, fsInfo: fsInfo, externalMounts: externalMounts, - watcher: watcher, rootFs: rootFs, ignoreMetrics: ignoreMetrics, pid: pid, @@ -177,6 +165,37 @@ return spec, nil } +func fsToFsStats(fs *fs.Fs) info.FsStats { + inodes := uint64(0) + inodesFree := uint64(0) + hasInodes := fs.InodesFree != nil + if hasInodes { + inodes = *fs.Inodes + inodesFree = *fs.InodesFree + } + return info.FsStats{ + Device: fs.Device, + Type: fs.Type.String(), + Limit: fs.Capacity, + Usage: fs.Capacity - fs.Free, + HasInodes: hasInodes, + Inodes: inodes, + InodesFree: inodesFree, + Available: fs.Available, + ReadsCompleted: fs.DiskStats.ReadsCompleted, + ReadsMerged: fs.DiskStats.ReadsMerged, + SectorsRead: fs.DiskStats.SectorsRead, + ReadTime: fs.DiskStats.ReadTime, + WritesCompleted: fs.DiskStats.WritesCompleted, + WritesMerged: fs.DiskStats.WritesMerged, + SectorsWritten: fs.DiskStats.SectorsWritten, + WriteTime: fs.DiskStats.WriteTime, + IoInProgress: fs.DiskStats.IoInProgress, + IoTime: fs.DiskStats.IoTime, + WeightedIoTime: fs.DiskStats.WeightedIoTime, + } +} + func (self *rawContainerHandler) getFsStats(stats *info.ContainerStats) error { // Get Filesystem information only for the root cgroup. if isRootCgroup(self.name) { @@ -184,27 +203,9 @@ if err != nil { return err } - for _, fs := range filesystems { - stats.Filesystem = append(stats.Filesystem, - info.FsStats{ - Device: fs.Device, - Type: fs.Type.String(), - Limit: fs.Capacity, - Usage: fs.Capacity - fs.Free, - Available: fs.Available, - InodesFree: fs.InodesFree, - ReadsCompleted: fs.DiskStats.ReadsCompleted, - ReadsMerged: fs.DiskStats.ReadsMerged, - SectorsRead: fs.DiskStats.SectorsRead, - ReadTime: fs.DiskStats.ReadTime, - WritesCompleted: fs.DiskStats.WritesCompleted, - WritesMerged: fs.DiskStats.WritesMerged, - SectorsWritten: fs.DiskStats.SectorsWritten, - WriteTime: fs.DiskStats.WriteTime, - IoInProgress: fs.DiskStats.IoInProgress, - IoTime: fs.DiskStats.IoTime, - WeightedIoTime: fs.DiskStats.WeightedIoTime, - }) + for i := range filesystems { + fs := filesystems[i] + stats.Filesystem = append(stats.Filesystem, fsToFsStats(&fs)) } } else if len(self.externalMounts) > 0 { var mountSet map[string]struct{} @@ -216,26 +217,9 @@ if err != nil { return err } - for _, fs := range filesystems { - stats.Filesystem = append(stats.Filesystem, - info.FsStats{ - Device: fs.Device, - Type: fs.Type.String(), - Limit: fs.Capacity, - Usage: fs.Capacity - fs.Free, - InodesFree: fs.InodesFree, - ReadsCompleted: fs.DiskStats.ReadsCompleted, - ReadsMerged: fs.DiskStats.ReadsMerged, - SectorsRead: fs.DiskStats.SectorsRead, - ReadTime: fs.DiskStats.ReadTime, - WritesCompleted: fs.DiskStats.WritesCompleted, - WritesMerged: fs.DiskStats.WritesMerged, - SectorsWritten: fs.DiskStats.SectorsWritten, - WriteTime: fs.DiskStats.WriteTime, - IoInProgress: fs.DiskStats.IoInProgress, - IoTime: fs.DiskStats.IoTime, - WeightedIoTime: fs.DiskStats.WeightedIoTime, - }) + for i := range filesystems { + fs := filesystems[i] + stats.Filesystem = append(stats.Filesystem, fsToFsStats(&fs)) } } return nil @@ -268,180 +252,23 @@ return map[string]string{} } -func (self *rawContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) { - containers := make(map[string]struct{}) - for _, cgroupPath := range self.cgroupPaths { - err := common.ListDirectories(cgroupPath, self.name, listType == container.ListRecursive, containers) - if err != nil { - return nil, err - } - } - - // Make into container references. - ret := make([]info.ContainerReference, 0, len(containers)) - for cont := range containers { - ret = append(ret, info.ContainerReference{ - Name: cont, - }) - } - - return ret, nil +func (self *rawContainerHandler) GetContainerIPAddress() string { + // the IP address for the raw container corresponds to the system ip address. + return "127.0.0.1" } -func (self *rawContainerHandler) ListThreads(listType container.ListType) ([]int, error) { - // TODO(vmarmol): Implement - return nil, nil +func (self *rawContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) { + return common.ListContainers(self.name, self.cgroupPaths, listType) } func (self *rawContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { return libcontainer.GetProcesses(self.cgroupManager) } -// Watches the specified directory and all subdirectories. Returns whether the path was -// already being watched and an error (if any). -func (self *rawContainerHandler) watchDirectory(dir string, containerName string) (bool, error) { - alreadyWatching, err := self.watcher.AddWatch(containerName, dir) - if err != nil { - return alreadyWatching, err - } - - // Remove the watch if further operations failed. - cleanup := true - defer func() { - if cleanup { - _, err := self.watcher.RemoveWatch(containerName, dir) - if err != nil { - glog.Warningf("Failed to remove inotify watch for %q: %v", dir, err) - } - } - }() - - // TODO(vmarmol): We should re-do this once we're done to ensure directories were not added in the meantime. - // Watch subdirectories as well. - entries, err := ioutil.ReadDir(dir) - if err != nil { - return alreadyWatching, err - } - for _, entry := range entries { - if entry.IsDir() { - // TODO(vmarmol): We don't have to fail here, maybe we can recover and try to get as many registrations as we can. - _, err = self.watchDirectory(path.Join(dir, entry.Name()), path.Join(containerName, entry.Name())) - if err != nil { - return alreadyWatching, err - } - } - } - - cleanup = false - return alreadyWatching, nil -} - -func (self *rawContainerHandler) processEvent(event *inotify.Event, events chan container.SubcontainerEvent) error { - // Convert the inotify event type to a container create or delete. - var eventType container.SubcontainerEventType - switch { - case (event.Mask & inotify.IN_CREATE) > 0: - eventType = container.SubcontainerAdd - case (event.Mask & inotify.IN_DELETE) > 0: - eventType = container.SubcontainerDelete - case (event.Mask & inotify.IN_MOVED_FROM) > 0: - eventType = container.SubcontainerDelete - case (event.Mask & inotify.IN_MOVED_TO) > 0: - eventType = container.SubcontainerAdd - default: - // Ignore other events. - return nil - } - - // Derive the container name from the path name. - var containerName string - for _, mount := range self.cgroupSubsystems.Mounts { - mountLocation := path.Clean(mount.Mountpoint) + "/" - if strings.HasPrefix(event.Name, mountLocation) { - containerName = event.Name[len(mountLocation)-1:] - break - } - } - if containerName == "" { - return fmt.Errorf("unable to detect container from watch event on directory %q", event.Name) - } - - // Maintain the watch for the new or deleted container. - switch { - case eventType == container.SubcontainerAdd: - // New container was created, watch it. - alreadyWatched, err := self.watchDirectory(event.Name, containerName) - if err != nil { - return err - } - - // Only report container creation once. - if alreadyWatched { - return nil - } - case eventType == container.SubcontainerDelete: - // Container was deleted, stop watching for it. - lastWatched, err := self.watcher.RemoveWatch(containerName, event.Name) - if err != nil { - return err - } - - // Only report container deletion once. - if !lastWatched { - return nil - } - default: - return fmt.Errorf("unknown event type %v", eventType) - } - - // Deliver the event. - events <- container.SubcontainerEvent{ - EventType: eventType, - Name: containerName, - } - - return nil -} - -func (self *rawContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error { - // Watch this container (all its cgroups) and all subdirectories. - for _, cgroupPath := range self.cgroupPaths { - _, err := self.watchDirectory(cgroupPath, self.name) - if err != nil { - return err - } - } - - // Process the events received from the kernel. - go func() { - for { - select { - case event := <-self.watcher.Event(): - err := self.processEvent(event, events) - if err != nil { - glog.Warningf("Error while processing event (%+v): %v", event, err) - } - case err := <-self.watcher.Error(): - glog.Warningf("Error while watching %q:", self.name, err) - case <-self.stopWatcher: - err := self.watcher.Close() - if err == nil { - self.stopWatcher <- err - return - } - } - } - }() - - return nil -} - -func (self *rawContainerHandler) StopWatchingSubcontainers() error { - // Rendezvous with the watcher thread. - self.stopWatcher <- nil - return <-self.stopWatcher -} - func (self *rawContainerHandler) Exists() bool { return common.CgroupExists(self.cgroupPaths) } + +func (self *rawContainerHandler) Type() container.ContainerType { + return container.ContainerTypeRaw +} diff -Nru cadvisor-0.23.0+dfsg/container/raw/handler_test.go cadvisor-0.25.0+dfsg/container/raw/handler_test.go --- cadvisor-0.23.0+dfsg/container/raw/handler_test.go 1970-01-01 00:00:00.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/raw/handler_test.go 2017-03-09 23:01:14.000000000 +0000 @@ -0,0 +1,126 @@ +// Copyright 2016 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 "raw" containers. +package raw + +import ( + "reflect" + "testing" + + "github.com/google/cadvisor/fs" + info "github.com/google/cadvisor/info/v1" +) + +func TestFsToFsStats(t *testing.T) { + inodes := uint64(100) + inodesFree := uint64(50) + testCases := map[string]struct { + fs *fs.Fs + expected info.FsStats + }{ + "has_inodes": { + fs: &fs.Fs{ + DeviceInfo: fs.DeviceInfo{Device: "123"}, + Type: fs.VFS, + Capacity: uint64(1024 * 1024), + Free: uint64(1024), + Available: uint64(1024), + Inodes: &inodes, + InodesFree: &inodesFree, + DiskStats: fs.DiskStats{ + ReadsCompleted: uint64(100), + ReadsMerged: uint64(100), + SectorsRead: uint64(100), + ReadTime: uint64(100), + WritesCompleted: uint64(100), + WritesMerged: uint64(100), + SectorsWritten: uint64(100), + WriteTime: uint64(100), + IoInProgress: uint64(100), + IoTime: uint64(100), + WeightedIoTime: uint64(100), + }, + }, + expected: info.FsStats{ + Device: "123", + Type: fs.VFS.String(), + Limit: uint64(1024 * 1024), + Usage: uint64(1024*1024) - uint64(1024), + HasInodes: true, + Inodes: inodes, + InodesFree: inodesFree, + Available: uint64(1024), + ReadsCompleted: uint64(100), + ReadsMerged: uint64(100), + SectorsRead: uint64(100), + ReadTime: uint64(100), + WritesCompleted: uint64(100), + WritesMerged: uint64(100), + SectorsWritten: uint64(100), + WriteTime: uint64(100), + IoInProgress: uint64(100), + IoTime: uint64(100), + WeightedIoTime: uint64(100), + }, + }, + "has_no_inodes": { + fs: &fs.Fs{ + DeviceInfo: fs.DeviceInfo{Device: "123"}, + Type: fs.DeviceMapper, + Capacity: uint64(1024 * 1024), + Free: uint64(1024), + Available: uint64(1024), + DiskStats: fs.DiskStats{ + ReadsCompleted: uint64(100), + ReadsMerged: uint64(100), + SectorsRead: uint64(100), + ReadTime: uint64(100), + WritesCompleted: uint64(100), + WritesMerged: uint64(100), + SectorsWritten: uint64(100), + WriteTime: uint64(100), + IoInProgress: uint64(100), + IoTime: uint64(100), + WeightedIoTime: uint64(100), + }, + }, + expected: info.FsStats{ + Device: "123", + Type: fs.DeviceMapper.String(), + Limit: uint64(1024 * 1024), + Usage: uint64(1024*1024) - uint64(1024), + HasInodes: false, + Available: uint64(1024), + ReadsCompleted: uint64(100), + ReadsMerged: uint64(100), + SectorsRead: uint64(100), + ReadTime: uint64(100), + WritesCompleted: uint64(100), + WritesMerged: uint64(100), + SectorsWritten: uint64(100), + WriteTime: uint64(100), + IoInProgress: uint64(100), + IoTime: uint64(100), + WeightedIoTime: uint64(100), + }, + }, + } + for testName, testCase := range testCases { + actual := fsToFsStats(testCase.fs) + if !reflect.DeepEqual(testCase.expected, actual) { + t.Errorf("test case=%v, expected=%v, actual=%v", testName, testCase.expected, actual) + } + } +} diff -Nru cadvisor-0.23.0+dfsg/container/rkt/client.go cadvisor-0.25.0+dfsg/container/rkt/client.go --- cadvisor-0.23.0+dfsg/container/rkt/client.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/rkt/client.go 2017-03-09 23:01:14.000000000 +0000 @@ -20,6 +20,7 @@ "sync" "time" + "github.com/blang/semver" rktapi "github.com/coreos/rkt/api/v1alpha" "golang.org/x/net/context" @@ -29,6 +30,7 @@ const ( defaultRktAPIServiceAddr = "localhost:15441" timeout = 2 * time.Second + minimumRktBinVersion = "1.6.0" ) var ( @@ -55,7 +57,25 @@ return } - rktClient = rktapi.NewPublicAPIClient(apisvcConn) + apisvc := rktapi.NewPublicAPIClient(apisvcConn) + + resp, err := apisvc.GetInfo(context.Background(), &rktapi.GetInfoRequest{}) + if err != nil { + rktClientErr = fmt.Errorf("rkt: GetInfo() failed: %v", err) + return + } + + binVersion, err := semver.Make(resp.Info.RktVersion) + if err != nil { + rktClientErr = fmt.Errorf("rkt: couldn't parse RtVersion: %v", err) + return + } + if binVersion.LT(semver.MustParse(minimumRktBinVersion)) { + rktClientErr = fmt.Errorf("rkt: binary version is too old(%v), requires at least %v", resp.Info.RktVersion, minimumRktBinVersion) + return + } + + rktClient = apisvc }) return rktClient, rktClientErr diff -Nru cadvisor-0.23.0+dfsg/container/rkt/client_test.go cadvisor-0.25.0+dfsg/container/rkt/client_test.go --- cadvisor-0.23.0+dfsg/container/rkt/client_test.go 1970-01-01 00:00:00.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/rkt/client_test.go 2017-03-09 23:01:14.000000000 +0000 @@ -0,0 +1,28 @@ +// Copyright 2016 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 rkt + +import ( + "testing" + + "github.com/blang/semver" +) + +func TestMinParse(t *testing.T) { + _, err := semver.Make(minimumRktBinVersion) + if err != nil { + t.Errorf("Couldn't parse the minimumRktBinVersion(%v): %v", minimumRktBinVersion, err) + } +} diff -Nru cadvisor-0.23.0+dfsg/container/rkt/factory.go cadvisor-0.25.0+dfsg/container/rkt/factory.go --- cadvisor-0.23.0+dfsg/container/rkt/factory.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/rkt/factory.go 2017-03-09 23:01:14.000000000 +0000 @@ -16,12 +16,12 @@ import ( "fmt" - "strings" "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" "github.com/golang/glog" ) @@ -58,14 +58,9 @@ } func (self *rktFactory) CanHandleAndAccept(name string) (bool, bool, error) { - // will ignore all cgroup names that don't either correspond to the machine.slice that is the pod or the containers that belong to the pod - // only works for machined rkt pods at the moment + accept, err := verifyPod(name) - if strings.HasPrefix(name, "/machine.slice/machine-rkt\\x2d") { - accept, err := verifyName(name) - return true, accept, err - } - return false, false, fmt.Errorf("%s not handled by rkt handler", name) + return accept, accept, err } func (self *rktFactory) DebugInfo() map[string][]string { @@ -99,6 +94,6 @@ ignoreMetrics: ignoreMetrics, rktPath: rktPath, } - container.RegisterContainerHandlerFactory(factory) + container.RegisterContainerHandlerFactory(factory, []watcher.ContainerWatchSource{watcher.Rkt}) return nil } diff -Nru cadvisor-0.23.0+dfsg/container/rkt/handler.go cadvisor-0.25.0+dfsg/container/rkt/handler.go --- cadvisor-0.23.0+dfsg/container/rkt/handler.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/rkt/handler.go 2017-03-09 23:01:14.000000000 +0000 @@ -18,8 +18,6 @@ import ( "fmt" "os" - "path" - "time" rktapi "github.com/coreos/rkt/api/v1alpha" "github.com/google/cadvisor/container" @@ -85,7 +83,7 @@ return nil, fmt.Errorf("this should be impossible!, new handler failing, but factory allowed, name = %s", name) } - //rktnetes uses containerID: rkt://fff40827-b994-4e3a-8f88-6427c2c8a5ac:nginx + // rktnetes uses containerID: rkt://fff40827-b994-4e3a-8f88-6427c2c8a5ac:nginx if parsed.Container == "" { isPod = true aliases = append(aliases, "rkt://"+parsed.Pod) @@ -100,20 +98,19 @@ }) if err != nil { return nil, err - } else { - var annotations []*rktapi.KeyValue - if parsed.Container == "" { - pid = int(resp.Pod.Pid) - apiPod = resp.Pod - annotations = resp.Pod.Annotations + } + annotations := resp.Pod.Annotations + if parsed.Container != "" { // As not empty string, an App container + if contAnnotations, ok := findAnnotations(resp.Pod.Apps, parsed.Container); !ok { + glog.Warningf("couldn't find app %v in pod", parsed.Container) } else { - var ok bool - if annotations, ok = findAnnotations(resp.Pod.Apps, parsed.Container); !ok { - glog.Warningf("couldn't find application in Pod matching %v", parsed.Container) - } + annotations = append(annotations, contAnnotations...) } - labels = createLabels(annotations) + } else { // The Pod container + pid = int(resp.Pod.Pid) + apiPod = resp.Pod } + labels = createLabels(annotations) cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name) @@ -152,7 +149,7 @@ } if !ignoreMetrics.Has(container.DiskUsageMetrics) { - handler.fsHandler = common.NewFsHandler(time.Minute, rootfsStorageDir, "", fsInfo) + handler.fsHandler = common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, "", fsInfo) } return handler, nil @@ -196,7 +193,12 @@ func (handler *rktContainerHandler) GetSpec() (info.ContainerSpec, error) { hasNetwork := handler.hasNetwork && !handler.ignoreMetrics.Has(container.NetworkUsageMetrics) hasFilesystem := !handler.ignoreMetrics.Has(container.DiskUsageMetrics) - return common.GetSpec(handler.cgroupPaths, handler.machineInfoFactory, hasNetwork, hasFilesystem) + + spec, err := common.GetSpec(handler.cgroupPaths, handler.machineInfoFactory, hasNetwork, hasFilesystem) + + spec.Labels = handler.labels + + return spec, err } func (handler *rktContainerHandler) getFsStats(stats *info.ContainerStats) error { @@ -225,7 +227,10 @@ fsStat := info.FsStats{Device: deviceInfo.Device, Limit: limit} - fsStat.BaseUsage, fsStat.Usage = handler.fsHandler.Usage() + usage := handler.fsHandler.Usage() + fsStat.BaseUsage = usage.BaseUsageBytes + fsStat.Usage = usage.TotalUsageBytes + fsStat.Inodes = usage.InodeUsage stats.Filesystem = append(stats.Filesystem, fsStat) @@ -247,6 +252,21 @@ return stats, nil } +func (self *rktContainerHandler) GetContainerIPAddress() string { + // attempt to return the ip address of the pod + // if a specific ip address of the pod could not be determined, return the system ip address + if self.isPod && len(self.apiPod.Networks) > 0 { + address := self.apiPod.Networks[0].Ipv4 + if address != "" { + return address + } else { + return self.apiPod.Networks[0].Ipv6 + } + } else { + return "127.0.0.1" + } +} + func (handler *rktContainerHandler) GetCgroupPath(resource string) (string, error) { path, ok := handler.cgroupPaths[resource] if !ok { @@ -260,68 +280,17 @@ } func (handler *rktContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) { - containers := make(map[string]struct{}) - - // Rkt containers do not have subcontainers, only the "Pod" does. - if handler.isPod == false { - var ret []info.ContainerReference - return ret, nil - } - - // Turn the system.slice cgroups into the Pod's subcontainers - for _, cgroupPath := range handler.cgroupPaths { - err := common.ListDirectories(path.Join(cgroupPath, "system.slice"), path.Join(handler.name, "system.slice"), listType == container.ListRecursive, containers) - if err != nil { - return nil, err - } - } - - // Create the container references. for the Pod's subcontainers - ret := make([]info.ContainerReference, 0, len(handler.apiPod.Apps)) - for cont := range containers { - aliases := make([]string, 1) - parsed, err := parseName(cont) - if err != nil { - return nil, fmt.Errorf("this should be impossible!, unable to parse rkt subcontainer name = %s", cont) - } - aliases = append(aliases, parsed.Pod+":"+parsed.Container) - - labels := make(map[string]string) - if annotations, ok := findAnnotations(handler.apiPod.Apps, parsed.Container); !ok { - glog.Warningf("couldn't find application in Pod matching %v", parsed.Container) - } else { - labels = createLabels(annotations) - } - - ret = append(ret, info.ContainerReference{ - Name: cont, - Aliases: aliases, - Namespace: RktNamespace, - Labels: labels, - }) - } - - return ret, nil -} - -func (handler *rktContainerHandler) ListThreads(listType container.ListType) ([]int, error) { - // TODO(sjpotter): Implement? Not implemented with docker yet - return nil, nil + return common.ListContainers(handler.name, handler.cgroupPaths, listType) } func (handler *rktContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { return libcontainer.GetProcesses(handler.cgroupManager) } -func (handler *rktContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error { - return fmt.Errorf("watch is unimplemented in the Rkt container driver") -} - -func (handler *rktContainerHandler) StopWatchingSubcontainers() error { - // No-op for Rkt driver. - return nil -} - func (handler *rktContainerHandler) Exists() bool { return common.CgroupExists(handler.cgroupPaths) } + +func (handler *rktContainerHandler) Type() container.ContainerType { + return container.ContainerTypeRkt +} diff -Nru cadvisor-0.23.0+dfsg/container/rkt/helpers.go cadvisor-0.25.0+dfsg/container/rkt/helpers.go --- cadvisor-0.23.0+dfsg/container/rkt/helpers.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/rkt/helpers.go 2017-03-09 23:01:14.000000000 +0000 @@ -20,7 +20,9 @@ "path" "strings" + rktapi "github.com/coreos/rkt/api/v1alpha" "github.com/golang/glog" + "golang.org/x/net/context" ) type parsedName struct { @@ -28,34 +30,80 @@ Container string } -func verifyName(name string) (bool, error) { - _, err := parseName(name) - return err == nil, err +func verifyPod(name string) (bool, error) { + pod, err := cgroupToPod(name) + + if err != nil || pod == nil { + return false, err + } + + // Anything handler can handle is also accepted. + // Accept cgroups that are sub the pod cgroup, except "system.slice" + // - "system.slice" doesn't contain any processes itself + accept := !strings.HasSuffix(name, "/system.slice") + + return accept, nil +} + +func cgroupToPod(name string) (*rktapi.Pod, error) { + rktClient, err := Client() + if err != nil { + return nil, fmt.Errorf("couldn't get rkt api service: %v", err) + } + + resp, err := rktClient.ListPods(context.Background(), &rktapi.ListPodsRequest{ + Filters: []*rktapi.PodFilter{ + { + States: []rktapi.PodState{rktapi.PodState_POD_STATE_RUNNING}, + PodSubCgroups: []string{name}, + }, + }, + }) + + if err != nil { + return nil, fmt.Errorf("failed to list pods: %v", err) + } + + if len(resp.Pods) == 0 { + return nil, nil + } + + if len(resp.Pods) != 1 { + return nil, fmt.Errorf("returned %d (expected 1) pods for cgroup %v", len(resp.Pods), name) + } + + return resp.Pods[0], nil } /* Parse cgroup name into a pod/container name struct Example cgroup fs name - pod - /sys/fs/cgroup/cpu/machine.slice/machine-rkt\\x2df556b64a\\x2d17a7\\x2d47d7\\x2d93ec\\x2def2275c3d67e.scope/ - container under pod - /sys/fs/cgroup/cpu/machine.slice/machine-rkt\\x2df556b64a\\x2d17a7\\x2d47d7\\x2d93ec\\x2def2275c3d67e.scope/system.slice/alpine-sh.service + pod - /machine.slice/machine-rkt\\x2df556b64a\\x2d17a7\\x2d47d7\\x2d93ec\\x2def2275c3d67e.scope/ + or /system.slice/k8s-..../ + container under pod - /machine.slice/machine-rkt\\x2df556b64a\\x2d17a7\\x2d47d7\\x2d93ec\\x2def2275c3d67e.scope/system.slice/alpine-sh.service + or /system.slice/k8s-..../system.slice/pause.service */ -//TODO{sjpotter}: this currently only recognizes machined started pods, which actually doesn't help with k8s which uses them as systemd services, need a solution for both func parseName(name string) (*parsedName, error) { + pod, err := cgroupToPod(name) + if err != nil { + return nil, fmt.Errorf("parseName: couldn't convert %v to a rkt pod: %v", name, err) + } + if pod == nil { + return nil, fmt.Errorf("parseName: didn't return a pod for %v", name) + } + splits := strings.Split(name, "/") + + parsed := &parsedName{} + if len(splits) == 3 || len(splits) == 5 { - parsed := &parsedName{} + parsed.Pod = pod.Id - if splits[1] == "machine.slice" { - replacer := strings.NewReplacer("machine-rkt\\x2d", "", ".scope", "", "\\x2d", "-") - parsed.Pod = replacer.Replace(splits[2]) - if len(splits) == 3 { - return parsed, nil - } - if splits[3] == "system.slice" { - parsed.Container = strings.Replace(splits[4], ".service", "", -1) - return parsed, nil - } + if len(splits) == 5 { + parsed.Container = strings.Replace(splits[4], ".service", "", -1) } + + return parsed, nil } return nil, fmt.Errorf("%s not handled by rkt handler", name) @@ -80,7 +128,7 @@ bytes, err := ioutil.ReadFile(tree) if err != nil { - glog.Infof("ReadFile failed, couldn't read %v to get upper dir: %v", tree, err) + glog.Errorf("ReadFile failed, couldn't read %v to get upper dir: %v", tree, err) return "" } diff -Nru cadvisor-0.23.0+dfsg/container/systemd/factory.go cadvisor-0.25.0+dfsg/container/systemd/factory.go --- cadvisor-0.23.0+dfsg/container/systemd/factory.go 2016-04-21 20:52:38.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/systemd/factory.go 2017-03-09 23:01:14.000000000 +0000 @@ -21,6 +21,7 @@ "github.com/google/cadvisor/container" "github.com/google/cadvisor/fs" info "github.com/google/cadvisor/info/v1" + "github.com/google/cadvisor/manager/watcher" "github.com/golang/glog" ) @@ -52,6 +53,6 @@ func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error { glog.Infof("Registering systemd factory") factory := &systemdFactory{} - container.RegisterContainerHandlerFactory(factory) + container.RegisterContainerHandlerFactory(factory, []watcher.ContainerWatchSource{watcher.Raw}) return nil } diff -Nru cadvisor-0.23.0+dfsg/container/testing/mock_handler.go cadvisor-0.25.0+dfsg/container/testing/mock_handler.go --- cadvisor-0.23.0+dfsg/container/testing/mock_handler.go 1970-01-01 00:00:00.000000000 +0000 +++ cadvisor-0.25.0+dfsg/container/testing/mock_handler.go 2017-03-09 23:01:14.000000000 +0000 @@ -0,0 +1,123 @@ +// Copyright 2014 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 testing + +import ( + "github.com/google/cadvisor/container" + info "github.com/google/cadvisor/info/v1" + + "github.com/stretchr/testify/mock" +) + +// This struct mocks a container handler. +type MockContainerHandler struct { + mock.Mock + Name string + Aliases []string +} + +func NewMockContainerHandler(containerName string) *MockContainerHandler { + return &MockContainerHandler{ + Name: containerName, + } +} + +// If self.Name is not empty, then ContainerReference() will return self.Name and self.Aliases. +// Otherwise, it will use the value provided by .On().Return(). +func (self *MockContainerHandler) ContainerReference() (info.ContainerReference, error) { + if len(self.Name) > 0 { + var aliases []string + if len(self.Aliases) > 0 { + aliases = make([]string, len(self.Aliases)) + copy(aliases, self.Aliases) + } + return info.ContainerReference{ + Name: self.Name, + Aliases: aliases, + }, nil + } + args := self.Called() + return args.Get(0).(info.ContainerReference), args.Error(1) +} + +func (self *MockContainerHandler) Start() {} + +func (self *MockContainerHandler) Cleanup() {} + +func (self *MockContainerHandler) GetSpec() (info.ContainerSpec, error) { + args := self.Called() + return args.Get(0).(info.ContainerSpec), args.Error(1) +} + +func (self *MockContainerHandler) GetStats() (*info.ContainerStats, error) { + args := self.Called() + return args.Get(0).(*info.ContainerStats), args.Error(1) +} + +func (self *MockContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) { + args := self.Called(listType) + return args.Get(0).([]info.ContainerReference), args.Error(1) +} + +func (self *MockContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { + args := self.Called(listType) + return args.Get(0).([]int), args.Error(1) +} + +func (self *MockContainerHandler) Exists() bool { + args := self.Called() + return args.Get(0).(bool) +} + +func (self *MockContainerHandler) GetCgroupPath(path string) (string, error) { + args := self.Called(path) + return args.Get(0).(string), args.Error(1) +} + +func (self *MockContainerHandler) GetContainerLabels() map[string]string { + args := self.Called() + return args.Get(0).(map[string]string) +} + +func (self *MockContainerHandler) Type() container.ContainerType { + args := self.Called() + return args.Get(0).(container.ContainerType) +} + +func (self *MockContainerHandler) GetContainerIPAddress() string { + args := self.Called() + return args.Get(0).(string) +} + +type FactoryForMockContainerHandler struct { + Name string + PrepareContainerHandlerFunc func(name string, handler *MockContainerHandler) +} + +func (self *FactoryForMockContainerHandler) String() string { + return self.Name +} + +func (self *FactoryForMockContainerHandler) NewContainerHandler(name string, inHostNamespace bool) (container.ContainerHandler, error) { + handler := &MockContainerHandler{} + if self.PrepareContainerHandlerFunc != nil { + self.PrepareContainerHandlerFunc(name, handler) + } + return handler, nil +} + +func (self *FactoryForMockContainerHandler) CanHandle(name string) bool { + return true +} diff -Nru cadvisor-0.23.0+dfsg/debian/cadvisor.lintian-overrides cadvisor-0.25.0+dfsg/debian/cadvisor.lintian-overrides --- cadvisor-0.23.0+dfsg/debian/cadvisor.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ cadvisor-0.25.0+dfsg/debian/cadvisor.lintian-overrides 2017-04-05 00:03:51.000000000 +0000 @@ -0,0 +1 @@ +cadvisor: spelling-error-in-binary diff -Nru cadvisor-0.23.0+dfsg/debian/changelog cadvisor-0.25.0+dfsg/debian/changelog --- cadvisor-0.23.0+dfsg/debian/changelog 2016-05-15 09:28:43.000000000 +0000 +++ cadvisor-0.25.0+dfsg/debian/changelog 2017-04-05 00:03:51.000000000 +0000 @@ -1,3 +1,32 @@ +cadvisor (0.25.0+dfsg-1) unstable; urgency=medium + + * New upstream release. + * Update d/copyright attributes after devendoring B-Ds + * Fix various Lintian warnings: spelling-error-in-binary, add + secure URI for Vcs-Git field. + + -- Tim Potter Wed, 05 Apr 2017 10:03:51 +1000 + +cadvisor (0.24.1+dfsg-1) unstable; urgency=medium + + * New upstream release. + * (Build-)Depends: + + golang-github-docker-distribution-dev + + golang-github-docker-engine-api-dev + + golang-github-mrunalp-fileutils-dev + + golang-go-zfs-dev + + golang-github-coreos-pkg-dev + + golang-github-blang-semver-dev, + + golang-github-docker-go-connections-dev + * Remove no longer needed dh_override_gencontrol target. + * Update d/rules and d/control after move from Godeps to vendor + subdirectory for vendored files. + * Regenerate proto files on each rebuild. + * Devendor bootstrap, gcharts and jquery libraries. + * Create embedded assets on build to remove non-free JS. + + -- Tim Potter Fri, 24 Feb 2017 08:53:24 +1100 + cadvisor (0.23.0+dfsg-2) unstable; urgency=medium [ Tim Potter ] diff -Nru cadvisor-0.23.0+dfsg/debian/control cadvisor-0.25.0+dfsg/debian/control --- cadvisor-0.23.0+dfsg/debian/control 2016-05-15 09:24:59.000000000 +0000 +++ cadvisor-0.25.0+dfsg/debian/control 2017-04-05 00:03:51.000000000 +0000 @@ -2,23 +2,25 @@ Section: devel Priority: extra Maintainer: pkg-go -Uploaders: Dmitry Smirnov +Uploaders: Dmitry Smirnov , Tim Potter Build-Depends: debhelper (>= 9), dh-systemd, - dh-golang, - golang-go, + dh-golang, + go-bindata, + golang-go, golang-dbus-dev, golang-gocapability-dev, golang-google-api-dev | golang-googlecode-p-google-api-go-client-dev, golang-github-aws-aws-sdk-go-dev (>= 1.0.7~), golang-google-cloud-compute-metadata-dev, golang-prometheus-client-dev, - golang-procfs-dev, + golang-procfs-dev, golang-protobuf-extensions-dev, golang-github-abbot-go-http-auth-dev, golang-github-beorn7-perks-dev, golang-github-coreos-go-systemd-dev, - golang-github-coreos-rkt-dev, + golang-github-docker-distribution-dev, golang-github-docker-docker-dev, + golang-github-docker-engine-api-dev, golang-github-docker-go-units-dev, golang-gopkg-eapache-go-resiliency.v1-dev, golang-gopkg-eapache-queue.v1-dev, @@ -32,6 +34,7 @@ golang-github-influxdb-influxdb-dev (>= 0.12.0~), golang-github-jmespath-go-jmespath-dev, golang-github-klauspost-crc32-dev, + golang-github-mrunalp-fileutils-dev, golang-pretty-dev | golang-github-kr-pretty-dev, golang-text-dev | golang-github-kr-text-dev, golang-github-seandolphin-bqschema-dev, @@ -47,28 +50,27 @@ golang-golang-x-oauth2-dev, golang-gopkg-olivere-elastic.v2-dev, golang-google-grpc-dev, -## Un-bundled: - libjs-bootstrap, - libjs-jquery (>= 1.11.3), + golang-go-zfs-dev, + golang-github-coreos-pkg-dev, + golang-github-blang-semver-dev, + golang-github-docker-go-connections-dev, Standards-Version: 3.9.8 Homepage: https://github.com/google/cadvisor Vcs-Browser: https://anonscm.debian.org/cgit/pkg-go/packages/cadvisor.git -Vcs-Git: git://anonscm.debian.org/pkg-go/packages/cadvisor.git +Vcs-Git: https://anonscm.debian.org/git/pkg-go/packages/cadvisor.git Package: golang-github-google-cadvisor-dev Architecture: all Depends: ${misc:Depends}, - golang-go, + golang-go, golang-dbus-dev, golang-gocapability-dev, golang-google-api-dev | golang-googlecode-p-google-api-go-client-dev, golang-github-aws-aws-sdk-go-dev (>= 1.0.7~), golang-github-beorn7-perks-dev, - golang-google-cloud-compute-metadata-dev, golang-github-coreos-go-systemd-dev, - golang-github-coreos-rkt-dev, golang-prometheus-client-dev, - golang-procfs-dev, + golang-procfs-dev, golang-protobuf-extensions-dev, golang-github-abbot-go-http-auth-dev, golang-github-docker-docker-dev, @@ -85,6 +87,7 @@ golang-github-influxdb-influxdb-dev (>= 0.12.0~), golang-github-jmespath-go-jmespath-dev, golang-github-klauspost-crc32-dev, + golang-github-mrunalp-fileutils-dev, golang-pretty-dev | golang-github-kr-pretty-dev, golang-text-dev | golang-github-kr-text-dev, golang-github-seandolphin-bqschema-dev, @@ -98,6 +101,10 @@ golang-golang-x-oauth2-dev, golang-gopkg-olivere-elastic.v2-dev, golang-google-grpc-dev, + golang-go-zfs-dev, + golang-github-coreos-pkg-dev, + golang-github-blang-semver-dev, + golang-github-docker-go-connections-dev Description: analyze resource usage and performance of running containers cAdvisor (Container Advisor) provides container users an understanding of the resource usage and performance characteristics of their running @@ -113,7 +120,7 @@ Package: cadvisor Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} +Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base, libjs-bootstrap, libjs-jquery (>= 1.11.3) Built-Using: ${misc:Built-Using} ${my:Built-Using} Description: analyze resource usage and performance characteristics of running containers cAdvisor (Container Advisor) provides container users an understanding of diff -Nru cadvisor-0.23.0+dfsg/debian/copyright cadvisor-0.25.0+dfsg/debian/copyright --- cadvisor-0.23.0+dfsg/debian/copyright 2016-04-23 01:54:00.000000000 +0000 +++ cadvisor-0.25.0+dfsg/debian/copyright 2017-04-05 00:03:51.000000000 +0000 @@ -2,59 +2,63 @@ Upstream-Name: cadvisor Source: https://github.com/google/cadvisor Files-Excluded: - pages/static/gcharts_js.go - pages/static/google_jsapi_js.go - __TODO__Godeps/_workspace/src/bitbucket.org/ww/goautoneg - Godeps/_workspace/src/github.com/SeanDolphin/bqschema - Godeps/_workspace/src/github.com/Shopify/sarama - Godeps/_workspace/src/github.com/Sirupsen/logrus - Godeps/_workspace/src/github.com/abbot/go-http-auth - Godeps/_workspace/src/github.com/aws/aws-sdk-go - Godeps/_workspace/src/github.com/beorn7/perks - Godeps/_workspace/src/github.com/coreos/go-systemd - Godeps/_workspace/src/github.com/coreos/rkt - Godeps/_workspace/src/github.com/docker/docker - Godeps/_workspace/src/github.com/docker/go-units - Godeps/_workspace/src/github.com/eapache/go-resiliency - Godeps/_workspace/src/github.com/eapache/queue - Godeps/_workspace/src/github.com/fsouza/go-dockerclient - Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus - Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker - Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/opencontainers/runc - Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units - Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys - Godeps/_workspace/src/github.com/garyburd/redigo - Godeps/_workspace/src/github.com/godbus/dbus - Godeps/_workspace/src/github.com/golang/protobuf - Godeps/_workspace/src/github.com/golang/snappy - Godeps/_workspace/src/github.com/go-ini/ini - Godeps/_workspace/src/github.com/influxdb/influxdb - Godeps/_workspace/src/github.com/jmespath/go-jmespath - Godeps/_workspace/src/github.com/klauspost/crc32 - Godeps/_workspace/src/github.com/kr/pretty - Godeps/_workspace/src/github.com/kr/text - Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions - __Godeps/_workspace/src/github.com/mistifyio/go-zfs/* - Godeps/_workspace/src/github.com/opencontainers/runc - Godeps/_workspace/src/github.com/pborman/uuid - Godeps/_workspace/src/github.com/prometheus/common - Godeps/_workspace/src/github.com/seccomp/libseccomp-golang - Godeps/_workspace/src/github.com/syndtr/gocapability - Godeps/_workspace/src/github.com/stretchr/objx - Godeps/_workspace/src/github.com/stretchr/testify - Godeps/_workspace/src/github.com/prometheus/client_golang - Godeps/_workspace/src/github.com/prometheus/procfs - Godeps/_workspace/src/github.com/prometheus/client_model - Godeps/_workspace/src/github.com/golang/glog - Godeps/_workspace/src/github.com/vishvananda/netlink - Godeps/_workspace/src/golang.org/x/exp - Godeps/_workspace/src/golang.org/x/net - Godeps/_workspace/src/golang.org/x/oauth2 - Godeps/_workspace/src/gopkg.in/olivere/elastic.v2 - Godeps/_workspace/src/google.golang.org/api/bigquery - Godeps/_workspace/src/google.golang.org/api/googleapi - Godeps/_workspace/src/google.golang.org/cloud - Godeps/_workspace/src/google.golang.org/grpc + pages/static/assets.go + pages/templates.go + pages/assets/js/google-jsapi.js + pages/assets/styles/bootstrap-3.1.1.min.css + pages/assets/js/bootstrap-3.1.1.min.js + pages/assets/styles/bootstrap-theme-3.1.1.min.css + pages/assets/js/jquery-1.10.2.min.js + pages/assets/js/gcharts.js + vendor/github.com/Microsoft/go-winio + vendor/github.com/SeanDolphin/bqschema + vendor/github.com/Shopify/sarama + vendor/github.com/Sirupsen/logrus + vendor/github.com/abbot/go-http-auth + vendor/github.com/aws/aws-sdk-go + vendor/github.com/beorn7/perks + vendor/github.com/coreos/go-systemd + vendor/github.com/docker/docker + vendor/github.com/docker/go-units + vendor/github.com/eapache/go-resiliency + vendor/github.com/eapache/queue + vendor/github.com/garyburd/redigo + vendor/github.com/godbus/dbus + vendor/github.com/golang/protobuf + vendor/github.com/golang/snappy + vendor/github.com/go-ini/ini + vendor/github.com/influxdb/influxdb + vendor/github.com/jmespath/go-jmespath + vendor/github.com/klauspost/crc32 + vendor/github.com/kr/pretty + vendor/github.com/kr/text + vendor/github.com/matttproud/golang_protobuf_extensions + vendor/github.com/opencontainers/runc + vendor/github.com/pborman/uuid + vendor/github.com/prometheus/common + vendor/github.com/seccomp/libseccomp-golang + vendor/github.com/syndtr/gocapability + vendor/github.com/stretchr/objx + vendor/github.com/stretchr/testify + vendor/github.com/prometheus/client_golang + vendor/github.com/prometheus/procfs + vendor/github.com/prometheus/client_model + vendor/github.com/golang/glog + vendor/github.com/vishvananda/netlink + vendor/golang.org/x/exp + vendor/golang.org/x/net + vendor/golang.org/x/oauth2 + vendor/gopkg.in/olivere/elastic.v2 + vendor/google.golang.org/api/bigquery + vendor/google.golang.org/api/googleapi + vendor/google.golang.org/grpc + vendor/github.com/blang/semver + vendor/github.com/coreos/pkg + vendor/github.com/mistifyio + vendor/github.com/docker/go-connections + vendor/cloud.google.com/go + vendor/github.com/docker/distribution + vendor/github.com/docker/engine-api Files: * Copyright: 2014-2016 Google Inc. @@ -62,41 +66,11 @@ License: Apache-2.0 Files: - Godeps/_workspace/src/bitbucket.org/ww/goautoneg/* + vendor/bitbucket.org/ww/goautoneg/* Copyright: 2011 Open Knowledge Foundation Ltd License: BSD-3-Clause -Files: - Godeps/_workspace/src/github.com/fsouza/go-dockerclient/* -Copyright: - 2014-2016 go-dockerclient authors -License: BSD-2-Clause - -Files: - Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/* -Copyright: - 2013-2016 Docker, Inc. -License: Apache-2.0 - -Files: - Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/mflag/* -Copyright: - 2014-2015 The Docker & Go Authors -License: BSD-3-Clause - -Files: - Godeps/_workspace/src/github.com/mistifyio/go-zfs/* -Copyright: - 2014 OmniTI Computer Consulting, Inc. -License: Apache-2.0 - -Files: - Godeps/_workspace/src/github.com/influxdb/influxdb/* -Copyright: - 2013-2015 Errplane Inc. -License: Expat - Files: debian/* Copyright: 2015-2016 Dmitry Smirnov License: GPL-3+ @@ -106,11 +80,6 @@ License: GPL-3+ or Apache-2.0 Comment: patches can be licensed under the same terms as upstream. -Files: debian/missing-sources/bootstrap* -Copyright: - 2011-2014 Twitter, Inc. -License: Expat - License: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -127,27 +96,6 @@ On Debian systems, the complete text of the Apache version 2.0 license can be found in "/usr/share/common-licenses/Apache-2.0". -License: BSD-2-Clause - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - . - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - . - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - License: BSD-3-Clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -175,25 +123,6 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -License: Expat - 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. - License: GPL-3+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru cadvisor-0.23.0+dfsg/debian/missing-sources/bootstrap.css cadvisor-0.25.0+dfsg/debian/missing-sources/bootstrap.css --- cadvisor-0.23.0+dfsg/debian/missing-sources/bootstrap.css 2015-10-13 04:16:20.000000000 +0000 +++ cadvisor-0.25.0+dfsg/debian/missing-sources/bootstrap.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,5785 +0,0 @@ -/*! - * Bootstrap v3.1.1 (http://getbootstrap.com) - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ - -/*! normalize.css v3.0.0 | MIT License | git.io/normalize */ -html { - font-family: sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} -body { - margin: 0; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -nav, -section, -summary { - display: block; -} -audio, -canvas, -progress, -video { - display: inline-block; - vertical-align: baseline; -} -audio:not([controls]) { - display: none; - height: 0; -} -[hidden], -template { - display: none; -} -a { - background: transparent; -} -a:active, -a:hover { - outline: 0; -} -abbr[title] { - border-bottom: 1px dotted; -} -b, -strong { - font-weight: bold; -} -dfn { - font-style: italic; -} -h1 { - margin: .67em 0; - font-size: 2em; -} -mark { - color: #000; - background: #ff0; -} -small { - font-size: 80%; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sup { - top: -.5em; -} -sub { - bottom: -.25em; -} -img { - border: 0; -} -svg:not(:root) { - overflow: hidden; -} -figure { - margin: 1em 40px; -} -hr { - height: 0; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -pre { - overflow: auto; -} -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; -} -button, -input, -optgroup, -select, -textarea { - margin: 0; - font: inherit; - color: inherit; -} -button { - overflow: visible; -} -button, -select { - text-transform: none; -} -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - cursor: pointer; -} -button[disabled], -html input[disabled] { - cursor: default; -} -button::-moz-focus-inner, -input::-moz-focus-inner { - padding: 0; - border: 0; -} -input { - line-height: normal; -} -input[type="checkbox"], -input[type="radio"] { - box-sizing: border-box; - padding: 0; -} -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button { - height: auto; -} -input[type="search"] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield; -} -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -fieldset { - padding: .35em .625em .75em; - margin: 0 2px; - border: 1px solid #c0c0c0; -} -legend { - padding: 0; - border: 0; -} -textarea { - overflow: auto; -} -optgroup { - font-weight: bold; -} -table { - border-spacing: 0; - border-collapse: collapse; -} -td, -th { - padding: 0; -} -@media print { - * { - color: #000 !important; - text-shadow: none !important; - background: transparent !important; - box-shadow: none !important; - } - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - a[href^="javascript:"]:after, - a[href^="#"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } - select { - background: #fff !important; - } - .navbar { - display: none; - } - .table td, - .table th { - background-color: #fff !important; - } - .btn > .caret, - .dropup > .btn > .caret { - border-top-color: #000 !important; - } - .label { - border: 1px solid #000; - } - .table { - border-collapse: collapse !important; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #ddd !important; - } -} -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -html { - font-size: 62.5%; - - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #428bca; - text-decoration: none; -} -a:hover, -a:focus { - color: #2a6496; - text-decoration: underline; -} -a:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive, -.thumbnail > img, -.thumbnail a > img, -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - display: inline-block; - max-width: 100%; - height: auto; - padding: 4px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #999; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 200; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -cite { - font-style: normal; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-muted { - color: #999; -} -.text-primary { - color: #428bca; -} -a.text-primary:hover { - color: #3071a9; -} -.text-success { - color: #3c763d; -} -a.text-success:hover { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #428bca; -} -a.bg-primary:hover { - background-color: #3071a9; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - margin-left: -5px; - list-style: none; -} -.list-inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.42857143; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #999; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #999; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - text-align: right; - border-right: 5px solid #eee; - border-left: 0; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -blockquote:before, -blockquote:after { - content: ""; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143; -} -code, -kbd, -pre, -samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; -} -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - white-space: nowrap; - background-color: #f9f2f4; - border-radius: 4px; -} -kbd { - padding: 2px 4px; - font-size: 90%; - color: #fff; - background-color: #333; - border-radius: 3px; - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); -} -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #ccc; - border-radius: 4px; -} -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -@media (min-width: 768px) { - .container { - width: 750px; - } -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} -.container-fluid { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -.row { - margin-right: -15px; - margin-left: -15px; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: left; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - right: 100%; -} -.col-xs-pull-11 { - right: 91.66666667%; -} -.col-xs-pull-10 { - right: 83.33333333%; -} -.col-xs-pull-9 { - right: 75%; -} -.col-xs-pull-8 { - right: 66.66666667%; -} -.col-xs-pull-7 { - right: 58.33333333%; -} -.col-xs-pull-6 { - right: 50%; -} -.col-xs-pull-5 { - right: 41.66666667%; -} -.col-xs-pull-4 { - right: 33.33333333%; -} -.col-xs-pull-3 { - right: 25%; -} -.col-xs-pull-2 { - right: 16.66666667%; -} -.col-xs-pull-1 { - right: 8.33333333%; -} -.col-xs-pull-0 { - right: 0; -} -.col-xs-push-12 { - left: 100%; -} -.col-xs-push-11 { - left: 91.66666667%; -} -.col-xs-push-10 { - left: 83.33333333%; -} -.col-xs-push-9 { - left: 75%; -} -.col-xs-push-8 { - left: 66.66666667%; -} -.col-xs-push-7 { - left: 58.33333333%; -} -.col-xs-push-6 { - left: 50%; -} -.col-xs-push-5 { - left: 41.66666667%; -} -.col-xs-push-4 { - left: 33.33333333%; -} -.col-xs-push-3 { - left: 25%; -} -.col-xs-push-2 { - left: 16.66666667%; -} -.col-xs-push-1 { - left: 8.33333333%; -} -.col-xs-push-0 { - left: 0; -} -.col-xs-offset-12 { - margin-left: 100%; -} -.col-xs-offset-11 { - margin-left: 91.66666667%; -} -.col-xs-offset-10 { - margin-left: 83.33333333%; -} -.col-xs-offset-9 { - margin-left: 75%; -} -.col-xs-offset-8 { - margin-left: 66.66666667%; -} -.col-xs-offset-7 { - margin-left: 58.33333333%; -} -.col-xs-offset-6 { - margin-left: 50%; -} -.col-xs-offset-5 { - margin-left: 41.66666667%; -} -.col-xs-offset-4 { - margin-left: 33.33333333%; -} -.col-xs-offset-3 { - margin-left: 25%; -} -.col-xs-offset-2 { - margin-left: 16.66666667%; -} -.col-xs-offset-1 { - margin-left: 8.33333333%; -} -.col-xs-offset-0 { - margin-left: 0; -} -@media (min-width: 768px) { - .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { - float: left; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - right: 100%; - } - .col-sm-pull-11 { - right: 91.66666667%; - } - .col-sm-pull-10 { - right: 83.33333333%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-8 { - right: 66.66666667%; - } - .col-sm-pull-7 { - right: 58.33333333%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-5 { - right: 41.66666667%; - } - .col-sm-pull-4 { - right: 33.33333333%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-2 { - right: 16.66666667%; - } - .col-sm-pull-1 { - right: 8.33333333%; - } - .col-sm-pull-0 { - right: 0; - } - .col-sm-push-12 { - left: 100%; - } - .col-sm-push-11 { - left: 91.66666667%; - } - .col-sm-push-10 { - left: 83.33333333%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-8 { - left: 66.66666667%; - } - .col-sm-push-7 { - left: 58.33333333%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-5 { - left: 41.66666667%; - } - .col-sm-push-4 { - left: 33.33333333%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-2 { - left: 16.66666667%; - } - .col-sm-push-1 { - left: 8.33333333%; - } - .col-sm-push-0 { - left: 0; - } - .col-sm-offset-12 { - margin-left: 100%; - } - .col-sm-offset-11 { - margin-left: 91.66666667%; - } - .col-sm-offset-10 { - margin-left: 83.33333333%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-8 { - margin-left: 66.66666667%; - } - .col-sm-offset-7 { - margin-left: 58.33333333%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-5 { - margin-left: 41.66666667%; - } - .col-sm-offset-4 { - margin-left: 33.33333333%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-2 { - margin-left: 16.66666667%; - } - .col-sm-offset-1 { - margin-left: 8.33333333%; - } - .col-sm-offset-0 { - margin-left: 0; - } -} -@media (min-width: 992px) { - .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { - float: left; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - right: 100%; - } - .col-md-pull-11 { - right: 91.66666667%; - } - .col-md-pull-10 { - right: 83.33333333%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-8 { - right: 66.66666667%; - } - .col-md-pull-7 { - right: 58.33333333%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-5 { - right: 41.66666667%; - } - .col-md-pull-4 { - right: 33.33333333%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-2 { - right: 16.66666667%; - } - .col-md-pull-1 { - right: 8.33333333%; - } - .col-md-pull-0 { - right: 0; - } - .col-md-push-12 { - left: 100%; - } - .col-md-push-11 { - left: 91.66666667%; - } - .col-md-push-10 { - left: 83.33333333%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-8 { - left: 66.66666667%; - } - .col-md-push-7 { - left: 58.33333333%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-5 { - left: 41.66666667%; - } - .col-md-push-4 { - left: 33.33333333%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-2 { - left: 16.66666667%; - } - .col-md-push-1 { - left: 8.33333333%; - } - .col-md-push-0 { - left: 0; - } - .col-md-offset-12 { - margin-left: 100%; - } - .col-md-offset-11 { - margin-left: 91.66666667%; - } - .col-md-offset-10 { - margin-left: 83.33333333%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-8 { - margin-left: 66.66666667%; - } - .col-md-offset-7 { - margin-left: 58.33333333%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-5 { - margin-left: 41.66666667%; - } - .col-md-offset-4 { - margin-left: 33.33333333%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-2 { - margin-left: 16.66666667%; - } - .col-md-offset-1 { - margin-left: 8.33333333%; - } - .col-md-offset-0 { - margin-left: 0; - } -} -@media (min-width: 1200px) { - .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { - float: left; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - right: 100%; - } - .col-lg-pull-11 { - right: 91.66666667%; - } - .col-lg-pull-10 { - right: 83.33333333%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-8 { - right: 66.66666667%; - } - .col-lg-pull-7 { - right: 58.33333333%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-5 { - right: 41.66666667%; - } - .col-lg-pull-4 { - right: 33.33333333%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-2 { - right: 16.66666667%; - } - .col-lg-pull-1 { - right: 8.33333333%; - } - .col-lg-pull-0 { - right: 0; - } - .col-lg-push-12 { - left: 100%; - } - .col-lg-push-11 { - left: 91.66666667%; - } - .col-lg-push-10 { - left: 83.33333333%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-8 { - left: 66.66666667%; - } - .col-lg-push-7 { - left: 58.33333333%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-5 { - left: 41.66666667%; - } - .col-lg-push-4 { - left: 33.33333333%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-2 { - left: 16.66666667%; - } - .col-lg-push-1 { - left: 8.33333333%; - } - .col-lg-push-0 { - left: 0; - } - .col-lg-offset-12 { - margin-left: 100%; - } - .col-lg-offset-11 { - margin-left: 91.66666667%; - } - .col-lg-offset-10 { - margin-left: 83.33333333%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-8 { - margin-left: 66.66666667%; - } - .col-lg-offset-7 { - margin-left: 58.33333333%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-5 { - margin-left: 41.66666667%; - } - .col-lg-offset-4 { - margin-left: 33.33333333%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-2 { - margin-left: 16.66666667%; - } - .col-lg-offset-1 { - margin-left: 8.33333333%; - } - .col-lg-offset-0 { - margin-left: 0; - } -} -table { - max-width: 100%; - background-color: transparent; -} -th { - text-align: left; -} -.table { - width: 100%; - margin-bottom: 20px; -} -.table > thead > tr > th, -.table > tbody > tr > th, -.table > tfoot > tr > th, -.table > thead > tr > td, -.table > tbody > tr > td, -.table > tfoot > tr > td { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ddd; -} -.table > thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #ddd; -} -.table > caption + thead > tr:first-child > th, -.table > colgroup + thead > tr:first-child > th, -.table > thead:first-child > tr:first-child > th, -.table > caption + thead > tr:first-child > td, -.table > colgroup + thead > tr:first-child > td, -.table > thead:first-child > tr:first-child > td { - border-top: 0; -} -.table > tbody + tbody { - border-top: 2px solid #ddd; -} -.table .table { - background-color: #fff; -} -.table-condensed > thead > tr > th, -.table-condensed > tbody > tr > th, -.table-condensed > tfoot > tr > th, -.table-condensed > thead > tr > td, -.table-condensed > tbody > tr > td, -.table-condensed > tfoot > tr > td { - padding: 5px; -} -.table-bordered { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > tbody > tr > th, -.table-bordered > tfoot > tr > th, -.table-bordered > thead > tr > td, -.table-bordered > tbody > tr > td, -.table-bordered > tfoot > tr > td { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; -} -.table-striped > tbody > tr:nth-child(odd) > td, -.table-striped > tbody > tr:nth-child(odd) > th { - background-color: #f9f9f9; -} -.table-hover > tbody > tr:hover > td, -.table-hover > tbody > tr:hover > th { - background-color: #f5f5f5; -} -table col[class*="col-"] { - position: static; - display: table-column; - float: none; -} -table td[class*="col-"], -table th[class*="col-"] { - position: static; - display: table-cell; - float: none; -} -.table > thead > tr > td.active, -.table > tbody > tr > td.active, -.table > tfoot > tr > td.active, -.table > thead > tr > th.active, -.table > tbody > tr > th.active, -.table > tfoot > tr > th.active, -.table > thead > tr.active > td, -.table > tbody > tr.active > td, -.table > tfoot > tr.active > td, -.table > thead > tr.active > th, -.table > tbody > tr.active > th, -.table > tfoot > tr.active > th { - background-color: #f5f5f5; -} -.table-hover > tbody > tr > td.active:hover, -.table-hover > tbody > tr > th.active:hover, -.table-hover > tbody > tr.active:hover > td, -.table-hover > tbody > tr.active:hover > th { - background-color: #e8e8e8; -} -.table > thead > tr > td.success, -.table > tbody > tr > td.success, -.table > tfoot > tr > td.success, -.table > thead > tr > th.success, -.table > tbody > tr > th.success, -.table > tfoot > tr > th.success, -.table > thead > tr.success > td, -.table > tbody > tr.success > td, -.table > tfoot > tr.success > td, -.table > thead > tr.success > th, -.table > tbody > tr.success > th, -.table > tfoot > tr.success > th { - background-color: #dff0d8; -} -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td, -.table-hover > tbody > tr.success:hover > th { - background-color: #d0e9c6; -} -.table > thead > tr > td.info, -.table > tbody > tr > td.info, -.table > tfoot > tr > td.info, -.table > thead > tr > th.info, -.table > tbody > tr > th.info, -.table > tfoot > tr > th.info, -.table > thead > tr.info > td, -.table > tbody > tr.info > td, -.table > tfoot > tr.info > td, -.table > thead > tr.info > th, -.table > tbody > tr.info > th, -.table > tfoot > tr.info > th { - background-color: #d9edf7; -} -.table-hover > tbody > tr > td.info:hover, -.table-hover > tbody > tr > th.info:hover, -.table-hover > tbody > tr.info:hover > td, -.table-hover > tbody > tr.info:hover > th { - background-color: #c4e3f3; -} -.table > thead > tr > td.warning, -.table > tbody > tr > td.warning, -.table > tfoot > tr > td.warning, -.table > thead > tr > th.warning, -.table > tbody > tr > th.warning, -.table > tfoot > tr > th.warning, -.table > thead > tr.warning > td, -.table > tbody > tr.warning > td, -.table > tfoot > tr.warning > td, -.table > thead > tr.warning > th, -.table > tbody > tr.warning > th, -.table > tfoot > tr.warning > th { - background-color: #fcf8e3; -} -.table-hover > tbody > tr > td.warning:hover, -.table-hover > tbody > tr > th.warning:hover, -.table-hover > tbody > tr.warning:hover > td, -.table-hover > tbody > tr.warning:hover > th { - background-color: #faf2cc; -} -.table > thead > tr > td.danger, -.table > tbody > tr > td.danger, -.table > tfoot > tr > td.danger, -.table > thead > tr > th.danger, -.table > tbody > tr > th.danger, -.table > tfoot > tr > th.danger, -.table > thead > tr.danger > td, -.table > tbody > tr.danger > td, -.table > tfoot > tr.danger > td, -.table > thead > tr.danger > th, -.table > tbody > tr.danger > th, -.table > tfoot > tr.danger > th { - background-color: #f2dede; -} -.table-hover > tbody > tr > td.danger:hover, -.table-hover > tbody > tr > th.danger:hover, -.table-hover > tbody > tr.danger:hover > td, -.table-hover > tbody > tr.danger:hover > th { - background-color: #ebcccc; -} -@media (max-width: 767px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-x: scroll; - overflow-y: hidden; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar; - border: 1px solid #ddd; - } - .table-responsive > .table { - margin-bottom: 0; - } - .table-responsive > .table > thead > tr > th, - .table-responsive > .table > tbody > tr > th, - .table-responsive > .table > tfoot > tr > th, - .table-responsive > .table > thead > tr > td, - .table-responsive > .table > tbody > tr > td, - .table-responsive > .table > tfoot > tr > td { - white-space: nowrap; - } - .table-responsive > .table-bordered { - border: 0; - } - .table-responsive > .table-bordered > thead > tr > th:first-child, - .table-responsive > .table-bordered > tbody > tr > th:first-child, - .table-responsive > .table-bordered > tfoot > tr > th:first-child, - .table-responsive > .table-bordered > thead > tr > td:first-child, - .table-responsive > .table-bordered > tbody > tr > td:first-child, - .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; - } - .table-responsive > .table-bordered > thead > tr > th:last-child, - .table-responsive > .table-bordered > tbody > tr > th:last-child, - .table-responsive > .table-bordered > tfoot > tr > th:last-child, - .table-responsive > .table-bordered > thead > tr > td:last-child, - .table-responsive > .table-bordered > tbody > tr > td:last-child, - .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; - } - .table-responsive > .table-bordered > tbody > tr:last-child > th, - .table-responsive > .table-bordered > tfoot > tr:last-child > th, - .table-responsive > .table-bordered > tbody > tr:last-child > td, - .table-responsive > .table-bordered > tfoot > tr:last-child > td { - border-bottom: 0; - } -} -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -label { - display: inline-block; - margin-bottom: 5px; - font-weight: bold; -} -input[type="search"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - /* IE8-9 */ - line-height: normal; -} -input[type="file"] { - display: block; -} -input[type="range"] { - display: block; - width: 100%; -} -select[multiple], -select[size] { - height: auto; -} -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -output { - display: block; - padding-top: 7px; - font-size: 14px; - line-height: 1.42857143; - color: #555; -} -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); -} -.form-control::-moz-placeholder { - color: #999; - opacity: 1; -} -.form-control:-ms-input-placeholder { - color: #999; -} -.form-control::-webkit-input-placeholder { - color: #999; -} -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - cursor: not-allowed; - background-color: #eee; - opacity: 1; -} -textarea.form-control { - height: auto; -} -input[type="search"] { - -webkit-appearance: none; -} -input[type="date"] { - line-height: 34px; -} -.form-group { - margin-bottom: 15px; -} -.radio, -.checkbox { - display: block; - min-height: 20px; - padding-left: 20px; - margin-top: 10px; - margin-bottom: 10px; -} -.radio label, -.checkbox label { - display: inline; - font-weight: normal; - cursor: pointer; -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - float: left; - margin-left: -20px; -} -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; -} -.radio-inline, -.checkbox-inline { - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - vertical-align: middle; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -.radio[disabled], -.radio-inline[disabled], -.checkbox[disabled], -.checkbox-inline[disabled], -fieldset[disabled] input[type="radio"], -fieldset[disabled] input[type="checkbox"], -fieldset[disabled] .radio, -fieldset[disabled] .radio-inline, -fieldset[disabled] .checkbox, -fieldset[disabled] .checkbox-inline { - cursor: not-allowed; -} -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-sm { - height: 30px; - line-height: 30px; -} -textarea.input-sm, -select[multiple].input-sm { - height: auto; -} -.input-lg { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.33; - border-radius: 6px; -} -select.input-lg { - height: 46px; - line-height: 46px; -} -textarea.input-lg, -select[multiple].input-lg { - height: auto; -} -.has-feedback { - position: relative; -} -.has-feedback .form-control { - padding-right: 42.5px; -} -.has-feedback .form-control-feedback { - position: absolute; - top: 25px; - right: 0; - display: block; - width: 34px; - height: 34px; - line-height: 34px; - text-align: center; -} -.has-success .help-block, -.has-success .control-label, -.has-success .radio, -.has-success .checkbox, -.has-success .radio-inline, -.has-success .checkbox-inline { - color: #3c763d; -} -.has-success .form-control { - border-color: #3c763d; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-success .form-control:focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; -} -.has-success .input-group-addon { - color: #3c763d; - background-color: #dff0d8; - border-color: #3c763d; -} -.has-success .form-control-feedback { - color: #3c763d; -} -.has-warning .help-block, -.has-warning .control-label, -.has-warning .radio, -.has-warning .checkbox, -.has-warning .radio-inline, -.has-warning .checkbox-inline { - color: #8a6d3b; -} -.has-warning .form-control { - border-color: #8a6d3b; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-warning .form-control:focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; -} -.has-warning .input-group-addon { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #8a6d3b; -} -.has-warning .form-control-feedback { - color: #8a6d3b; -} -.has-error .help-block, -.has-error .control-label, -.has-error .radio, -.has-error .checkbox, -.has-error .radio-inline, -.has-error .checkbox-inline { - color: #a94442; -} -.has-error .form-control { - border-color: #a94442; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-error .form-control:focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; -} -.has-error .input-group-addon { - color: #a94442; - background-color: #f2dede; - border-color: #a94442; -} -.has-error .form-control-feedback { - color: #a94442; -} -.form-control-static { - margin-bottom: 0; -} -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373; -} -@media (min-width: 768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .input-group > .form-control { - width: 100%; - } - .form-inline .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio, - .form-inline .checkbox { - display: inline-block; - padding-left: 0; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - float: none; - margin-left: 0; - } - .form-inline .has-feedback .form-control-feedback { - top: 0; - } -} -.form-horizontal .control-label, -.form-horizontal .radio, -.form-horizontal .checkbox, -.form-horizontal .radio-inline, -.form-horizontal .checkbox-inline { - padding-top: 7px; - margin-top: 0; - margin-bottom: 0; -} -.form-horizontal .radio, -.form-horizontal .checkbox { - min-height: 27px; -} -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px; -} -.form-horizontal .form-control-static { - padding-top: 7px; -} -@media (min-width: 768px) { - .form-horizontal .control-label { - text-align: right; - } -} -.form-horizontal .has-feedback .form-control-feedback { - top: 0; - right: 15px; -} -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.btn:focus, -.btn:active:focus, -.btn.active:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn:hover, -.btn:focus { - color: #333; - text-decoration: none; -} -.btn:active, -.btn.active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn.disabled, -.btn[disabled], -fieldset[disabled] .btn { - pointer-events: none; - cursor: not-allowed; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; - opacity: .65; -} -.btn-default { - color: #333; - background-color: #fff; - border-color: #ccc; -} -.btn-default:hover, -.btn-default:focus, -.btn-default:active, -.btn-default.active, -.open .dropdown-toggle.btn-default { - color: #333; - background-color: #ebebeb; - border-color: #adadad; -} -.btn-default:active, -.btn-default.active, -.open .dropdown-toggle.btn-default { - background-image: none; -} -.btn-default.disabled, -.btn-default[disabled], -fieldset[disabled] .btn-default, -.btn-default.disabled:hover, -.btn-default[disabled]:hover, -fieldset[disabled] .btn-default:hover, -.btn-default.disabled:focus, -.btn-default[disabled]:focus, -fieldset[disabled] .btn-default:focus, -.btn-default.disabled:active, -.btn-default[disabled]:active, -fieldset[disabled] .btn-default:active, -.btn-default.disabled.active, -.btn-default[disabled].active, -fieldset[disabled] .btn-default.active { - background-color: #fff; - border-color: #ccc; -} -.btn-default .badge { - color: #fff; - background-color: #333; -} -.btn-primary { - color: #fff; - background-color: #428bca; - border-color: #357ebd; -} -.btn-primary:hover, -.btn-primary:focus, -.btn-primary:active, -.btn-primary.active, -.open .dropdown-toggle.btn-primary { - color: #fff; - background-color: #3276b1; - border-color: #285e8e; -} -.btn-primary:active, -.btn-primary.active, -.open .dropdown-toggle.btn-primary { - background-image: none; -} -.btn-primary.disabled, -.btn-primary[disabled], -fieldset[disabled] .btn-primary, -.btn-primary.disabled:hover, -.btn-primary[disabled]:hover, -fieldset[disabled] .btn-primary:hover, -.btn-primary.disabled:focus, -.btn-primary[disabled]:focus, -fieldset[disabled] .btn-primary:focus, -.btn-primary.disabled:active, -.btn-primary[disabled]:active, -fieldset[disabled] .btn-primary:active, -.btn-primary.disabled.active, -.btn-primary[disabled].active, -fieldset[disabled] .btn-primary.active { - background-color: #428bca; - border-color: #357ebd; -} -.btn-primary .badge { - color: #428bca; - background-color: #fff; -} -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success:hover, -.btn-success:focus, -.btn-success:active, -.btn-success.active, -.open .dropdown-toggle.btn-success { - color: #fff; - background-color: #47a447; - border-color: #398439; -} -.btn-success:active, -.btn-success.active, -.open .dropdown-toggle.btn-success { - background-image: none; -} -.btn-success.disabled, -.btn-success[disabled], -fieldset[disabled] .btn-success, -.btn-success.disabled:hover, -.btn-success[disabled]:hover, -fieldset[disabled] .btn-success:hover, -.btn-success.disabled:focus, -.btn-success[disabled]:focus, -fieldset[disabled] .btn-success:focus, -.btn-success.disabled:active, -.btn-success[disabled]:active, -fieldset[disabled] .btn-success:active, -.btn-success.disabled.active, -.btn-success[disabled].active, -fieldset[disabled] .btn-success.active { - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success .badge { - color: #5cb85c; - background-color: #fff; -} -.btn-info { - color: #fff; - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info:hover, -.btn-info:focus, -.btn-info:active, -.btn-info.active, -.open .dropdown-toggle.btn-info { - color: #fff; - background-color: #39b3d7; - border-color: #269abc; -} -.btn-info:active, -.btn-info.active, -.open .dropdown-toggle.btn-info { - background-image: none; -} -.btn-info.disabled, -.btn-info[disabled], -fieldset[disabled] .btn-info, -.btn-info.disabled:hover, -.btn-info[disabled]:hover, -fieldset[disabled] .btn-info:hover, -.btn-info.disabled:focus, -.btn-info[disabled]:focus, -fieldset[disabled] .btn-info:focus, -.btn-info.disabled:active, -.btn-info[disabled]:active, -fieldset[disabled] .btn-info:active, -.btn-info.disabled.active, -.btn-info[disabled].active, -fieldset[disabled] .btn-info.active { - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info .badge { - color: #5bc0de; - background-color: #fff; -} -.btn-warning { - color: #fff; - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning:hover, -.btn-warning:focus, -.btn-warning:active, -.btn-warning.active, -.open .dropdown-toggle.btn-warning { - color: #fff; - background-color: #ed9c28; - border-color: #d58512; -} -.btn-warning:active, -.btn-warning.active, -.open .dropdown-toggle.btn-warning { - background-image: none; -} -.btn-warning.disabled, -.btn-warning[disabled], -fieldset[disabled] .btn-warning, -.btn-warning.disabled:hover, -.btn-warning[disabled]:hover, -fieldset[disabled] .btn-warning:hover, -.btn-warning.disabled:focus, -.btn-warning[disabled]:focus, -fieldset[disabled] .btn-warning:focus, -.btn-warning.disabled:active, -.btn-warning[disabled]:active, -fieldset[disabled] .btn-warning:active, -.btn-warning.disabled.active, -.btn-warning[disabled].active, -fieldset[disabled] .btn-warning.active { - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning .badge { - color: #f0ad4e; - background-color: #fff; -} -.btn-danger { - color: #fff; - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger:hover, -.btn-danger:focus, -.btn-danger:active, -.btn-danger.active, -.open .dropdown-toggle.btn-danger { - color: #fff; - background-color: #d2322d; - border-color: #ac2925; -} -.btn-danger:active, -.btn-danger.active, -.open .dropdown-toggle.btn-danger { - background-image: none; -} -.btn-danger.disabled, -.btn-danger[disabled], -fieldset[disabled] .btn-danger, -.btn-danger.disabled:hover, -.btn-danger[disabled]:hover, -fieldset[disabled] .btn-danger:hover, -.btn-danger.disabled:focus, -.btn-danger[disabled]:focus, -fieldset[disabled] .btn-danger:focus, -.btn-danger.disabled:active, -.btn-danger[disabled]:active, -fieldset[disabled] .btn-danger:active, -.btn-danger.disabled.active, -.btn-danger[disabled].active, -fieldset[disabled] .btn-danger.active { - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger .badge { - color: #d9534f; - background-color: #fff; -} -.btn-link { - font-weight: normal; - color: #428bca; - cursor: pointer; - border-radius: 0; -} -.btn-link, -.btn-link:active, -.btn-link[disabled], -fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-link, -.btn-link:hover, -.btn-link:focus, -.btn-link:active { - border-color: transparent; -} -.btn-link:hover, -.btn-link:focus { - color: #2a6496; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -fieldset[disabled] .btn-link:hover, -.btn-link[disabled]:focus, -fieldset[disabled] .btn-link:focus { - color: #999; - text-decoration: none; -} -.btn-lg, -.btn-group-lg > .btn { - padding: 10px 16px; - font-size: 18px; - line-height: 1.33; - border-radius: 6px; -} -.btn-sm, -.btn-group-sm > .btn { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-xs, -.btn-group-xs > .btn { - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; - padding-right: 0; - padding-left: 0; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.fade { - opacity: 0; - -webkit-transition: opacity .15s linear; - transition: opacity .15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - display: none; -} -.collapse.in { - display: block; -} -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition: height .35s ease; - transition: height .35s ease; -} -@font-face { - font-family: 'Glyphicons Halflings'; - - src: url('../fonts/glyphicons-halflings-regular.eot'); - src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\2a"; -} -.glyphicon-plus:before { - content: "\2b"; -} -.glyphicon-euro:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px solid; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -} -.dropdown { - position: relative; -} -.dropdown-toggle:focus { - outline: 0; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - list-style: none; - background-color: #fff; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 1.42857143; - color: #333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - color: #262626; - text-decoration: none; - background-color: #f5f5f5; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #fff; - text-decoration: none; - background-color: #428bca; - outline: 0; -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #999; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - cursor: not-allowed; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.open > .dropdown-menu { - display: block; -} -.open > a { - outline: 0; -} -.dropdown-menu-right { - right: 0; - left: auto; -} -.dropdown-menu-left { - right: auto; - left: 0; -} -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: 12px; - line-height: 1.42857143; - color: #999; -} -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - content: ""; - border-top: 0; - border-bottom: 4px solid; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 1px; -} -@media (min-width: 768px) { - .navbar-right .dropdown-menu { - right: 0; - left: auto; - } - .navbar-right .dropdown-menu-left { - right: auto; - left: 0; - } -} -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; -} -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - float: left; -} -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover, -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus, -.btn-group > .btn:active, -.btn-group-vertical > .btn:active, -.btn-group > .btn.active, -.btn-group-vertical > .btn.active { - z-index: 2; -} -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus { - outline: none; -} -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group { - margin-left: -1px; -} -.btn-toolbar { - margin-left: -5px; -} -.btn-toolbar .btn-group, -.btn-toolbar .input-group { - float: left; -} -.btn-toolbar > .btn, -.btn-toolbar > .btn-group, -.btn-toolbar > .input-group { - margin-left: 5px; -} -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} -.btn-group > .btn:first-child { - margin-left: 0; -} -.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child > .btn:last-child, -.btn-group > .btn-group:first-child > .dropdown-toggle { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn-group:last-child > .btn:first-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-right: 12px; - padding-left: 12px; -} -.btn-group.open .dropdown-toggle { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-group.open .dropdown-toggle.btn-link { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn .caret { - margin-left: 0; -} -.btn-lg .caret { - border-width: 5px 5px 0; - border-bottom-width: 0; -} -.dropup .btn-lg .caret { - border-width: 0 5px 5px; -} -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group, -.btn-group-vertical > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; -} -.btn-group-vertical > .btn-group > .btn { - float: none; -} -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; -} -.btn-group-vertical > .btn:not(:first-child):not(:last-child) { - border-radius: 0; -} -.btn-group-vertical > .btn:first-child:not(:last-child) { - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn:last-child:not(:first-child) { - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-left-radius: 4px; -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; -} -.btn-group-justified > .btn, -.btn-group-justified > .btn-group { - display: table-cell; - float: none; - width: 1%; -} -.btn-group-justified > .btn-group .btn { - width: 100%; -} -[data-toggle="buttons"] > .btn > input[type="radio"], -[data-toggle="buttons"] > .btn > input[type="checkbox"] { - display: none; -} -.input-group { - position: relative; - display: table; - border-collapse: separate; -} -.input-group[class*="col-"] { - float: none; - padding-right: 0; - padding-left: 0; -} -.input-group .form-control { - position: relative; - z-index: 2; - float: left; - width: 100%; - margin-bottom: 0; -} -.input-group-lg > .form-control, -.input-group-lg > .input-group-addon, -.input-group-lg > .input-group-btn > .btn { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.33; - border-radius: 6px; -} -select.input-group-lg > .form-control, -select.input-group-lg > .input-group-addon, -select.input-group-lg > .input-group-btn > .btn { - height: 46px; - line-height: 46px; -} -textarea.input-group-lg > .form-control, -textarea.input-group-lg > .input-group-addon, -textarea.input-group-lg > .input-group-btn > .btn, -select[multiple].input-group-lg > .form-control, -select[multiple].input-group-lg > .input-group-addon, -select[multiple].input-group-lg > .input-group-btn > .btn { - height: auto; -} -.input-group-sm > .form-control, -.input-group-sm > .input-group-addon, -.input-group-sm > .input-group-btn > .btn { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-group-sm > .form-control, -select.input-group-sm > .input-group-addon, -select.input-group-sm > .input-group-btn > .btn { - height: 30px; - line-height: 30px; -} -textarea.input-group-sm > .form-control, -textarea.input-group-sm > .input-group-addon, -textarea.input-group-sm > .input-group-btn > .btn, -select[multiple].input-group-sm > .form-control, -select[multiple].input-group-sm > .input-group-addon, -select[multiple].input-group-sm > .input-group-btn > .btn { - height: auto; -} -.input-group-addon, -.input-group-btn, -.input-group .form-control { - display: table-cell; -} -.input-group-addon:not(:first-child):not(:last-child), -.input-group-btn:not(:first-child):not(:last-child), -.input-group .form-control:not(:first-child):not(:last-child) { - border-radius: 0; -} -.input-group-addon, -.input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; -} -.input-group-addon { - padding: 6px 12px; - font-size: 14px; - font-weight: normal; - line-height: 1; - color: #555; - text-align: center; - background-color: #eee; - border: 1px solid #ccc; - border-radius: 4px; -} -.input-group-addon.input-sm { - padding: 5px 10px; - font-size: 12px; - border-radius: 3px; -} -.input-group-addon.input-lg { - padding: 10px 16px; - font-size: 18px; - border-radius: 6px; -} -.input-group-addon input[type="radio"], -.input-group-addon input[type="checkbox"] { - margin-top: 0; -} -.input-group .form-control:first-child, -.input-group-addon:first-child, -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group > .btn, -.input-group-btn:first-child > .dropdown-toggle, -.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group-addon:first-child { - border-right: 0; -} -.input-group .form-control:last-child, -.input-group-addon:last-child, -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group > .btn, -.input-group-btn:last-child > .dropdown-toggle, -.input-group-btn:first-child > .btn:not(:first-child), -.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group-addon:last-child { - border-left: 0; -} -.input-group-btn { - position: relative; - font-size: 0; - white-space: nowrap; -} -.input-group-btn > .btn { - position: relative; -} -.input-group-btn > .btn + .btn { - margin-left: -1px; -} -.input-group-btn > .btn:hover, -.input-group-btn > .btn:focus, -.input-group-btn > .btn:active { - z-index: 2; -} -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group { - margin-right: -1px; -} -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group { - margin-left: -1px; -} -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.nav > li { - position: relative; - display: block; -} -.nav > li > a { - position: relative; - display: block; - padding: 10px 15px; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eee; -} -.nav > li.disabled > a { - color: #999; -} -.nav > li.disabled > a:hover, -.nav > li.disabled > a:focus { - color: #999; - text-decoration: none; - cursor: not-allowed; - background-color: transparent; -} -.nav .open > a, -.nav .open > a:hover, -.nav .open > a:focus { - background-color: #eee; - border-color: #428bca; -} -.nav .nav-divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.nav > li > a > img { - max-width: none; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - float: left; - margin-bottom: -1px; -} -.nav-tabs > li > a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover { - border-color: #eee #eee #ddd; -} -.nav-tabs > li.active > a, -.nav-tabs > li.active > a:hover, -.nav-tabs > li.active > a:focus { - color: #555; - cursor: default; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} -.nav-tabs.nav-justified { - width: 100%; - border-bottom: 0; -} -.nav-tabs.nav-justified > li { - float: none; -} -.nav-tabs.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-tabs.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-tabs.nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs.nav-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs.nav-justified > .active > a, -.nav-tabs.nav-justified > .active > a:hover, -.nav-tabs.nav-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs.nav-justified > .active > a, - .nav-tabs.nav-justified > .active > a:hover, - .nav-tabs.nav-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.nav-pills > li { - float: left; -} -.nav-pills > li > a { - border-radius: 4px; -} -.nav-pills > li + li { - margin-left: 2px; -} -.nav-pills > li.active > a, -.nav-pills > li.active > a:hover, -.nav-pills > li.active > a:focus { - color: #fff; - background-color: #428bca; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li + li { - margin-top: 2px; - margin-left: 0; -} -.nav-justified { - width: 100%; -} -.nav-justified > li { - float: none; -} -.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs-justified { - border-bottom: 0; -} -.nav-tabs-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs-justified > .active > a, -.nav-tabs-justified > .active > a:hover, -.nav-tabs-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs-justified > .active > a, - .nav-tabs-justified > .active > a:hover, - .nav-tabs-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.tab-content > .tab-pane { - display: none; -} -.tab-content > .active { - display: block; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent; -} -@media (min-width: 768px) { - .navbar { - border-radius: 4px; - } -} -@media (min-width: 768px) { - .navbar-header { - float: left; - } -} -.navbar-collapse { - max-height: 340px; - padding-right: 15px; - padding-left: 15px; - overflow-x: visible; - -webkit-overflow-scrolling: touch; - border-top: 1px solid transparent; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); -} -.navbar-collapse.in { - overflow-y: auto; -} -@media (min-width: 768px) { - .navbar-collapse { - width: auto; - border-top: 0; - box-shadow: none; - } - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important; - } - .navbar-collapse.in { - overflow-y: visible; - } - .navbar-fixed-top .navbar-collapse, - .navbar-static-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - padding-right: 0; - padding-left: 0; - } -} -.container > .navbar-header, -.container-fluid > .navbar-header, -.container > .navbar-collapse, -.container-fluid > .navbar-collapse { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .container > .navbar-header, - .container-fluid > .navbar-header, - .container > .navbar-collapse, - .container-fluid > .navbar-collapse { - margin-right: 0; - margin-left: 0; - } -} -.navbar-static-top { - z-index: 1000; - border-width: 0 0 1px; -} -@media (min-width: 768px) { - .navbar-static-top { - border-radius: 0; - } -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; -} -@media (min-width: 768px) { - .navbar-fixed-top, - .navbar-fixed-bottom { - border-radius: 0; - } -} -.navbar-fixed-top { - top: 0; - border-width: 0 0 1px; -} -.navbar-fixed-bottom { - bottom: 0; - margin-bottom: 0; - border-width: 1px 0 0; -} -.navbar-brand { - float: left; - height: 50px; - padding: 15px 15px; - font-size: 18px; - line-height: 20px; -} -.navbar-brand:hover, -.navbar-brand:focus { - text-decoration: none; -} -@media (min-width: 768px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: -15px; - } -} -.navbar-toggle { - position: relative; - float: right; - padding: 9px 10px; - margin-top: 8px; - margin-right: 15px; - margin-bottom: 8px; - background-color: transparent; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.navbar-toggle:focus { - outline: none; -} -.navbar-toggle .icon-bar { - display: block; - width: 22px; - height: 2px; - border-radius: 1px; -} -.navbar-toggle .icon-bar + .icon-bar { - margin-top: 4px; -} -@media (min-width: 768px) { - .navbar-toggle { - display: none; - } -} -.navbar-nav { - margin: 7.5px -15px; -} -.navbar-nav > li > a { - padding-top: 10px; - padding-bottom: 10px; - line-height: 20px; -} -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - box-shadow: none; - } - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 15px 5px 25px; - } - .navbar-nav .open .dropdown-menu > li > a { - line-height: 20px; - } - .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-nav .open .dropdown-menu > li > a:focus { - background-image: none; - } -} -@media (min-width: 768px) { - .navbar-nav { - float: left; - margin: 0; - } - .navbar-nav > li { - float: left; - } - .navbar-nav > li > a { - padding-top: 15px; - padding-bottom: 15px; - } - .navbar-nav.navbar-right:last-child { - margin-right: -15px; - } -} -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - } -} -.navbar-form { - padding: 10px 15px; - margin-top: 8px; - margin-right: -15px; - margin-bottom: 8px; - margin-left: -15px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); -} -@media (min-width: 768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .navbar-form .input-group > .form-control { - width: 100%; - } - .navbar-form .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio, - .navbar-form .checkbox { - display: inline-block; - padding-left: 0; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio input[type="radio"], - .navbar-form .checkbox input[type="checkbox"] { - float: none; - margin-left: 0; - } - .navbar-form .has-feedback .form-control-feedback { - top: 0; - } -} -@media (max-width: 767px) { - .navbar-form .form-group { - margin-bottom: 5px; - } -} -@media (min-width: 768px) { - .navbar-form { - width: auto; - padding-top: 0; - padding-bottom: 0; - margin-right: 0; - margin-left: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-form.navbar-right:last-child { - margin-right: -15px; - } -} -.navbar-nav > li > .dropdown-menu { - margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.navbar-btn { - margin-top: 8px; - margin-bottom: 8px; -} -.navbar-btn.btn-sm { - margin-top: 10px; - margin-bottom: 10px; -} -.navbar-btn.btn-xs { - margin-top: 14px; - margin-bottom: 14px; -} -.navbar-text { - margin-top: 15px; - margin-bottom: 15px; -} -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px; - } - .navbar-text.navbar-right:last-child { - margin-right: 0; - } -} -.navbar-default { - background-color: #f8f8f8; - border-color: #e7e7e7; -} -.navbar-default .navbar-brand { - color: #777; -} -.navbar-default .navbar-brand:hover, -.navbar-default .navbar-brand:focus { - color: #5e5e5e; - background-color: transparent; -} -.navbar-default .navbar-text { - color: #777; -} -.navbar-default .navbar-nav > li > a { - color: #777; -} -.navbar-default .navbar-nav > li > a:hover, -.navbar-default .navbar-nav > li > a:focus { - color: #333; - background-color: transparent; -} -.navbar-default .navbar-nav > .active > a, -.navbar-default .navbar-nav > .active > a:hover, -.navbar-default .navbar-nav > .active > a:focus { - color: #555; - background-color: #e7e7e7; -} -.navbar-default .navbar-nav > .disabled > a, -.navbar-default .navbar-nav > .disabled > a:hover, -.navbar-default .navbar-nav > .disabled > a:focus { - color: #ccc; - background-color: transparent; -} -.navbar-default .navbar-toggle { - border-color: #ddd; -} -.navbar-default .navbar-toggle:hover, -.navbar-default .navbar-toggle:focus { - background-color: #ddd; -} -.navbar-default .navbar-toggle .icon-bar { - background-color: #888; -} -.navbar-default .navbar-collapse, -.navbar-default .navbar-form { - border-color: #e7e7e7; -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .open > a:hover, -.navbar-default .navbar-nav > .open > a:focus { - color: #555; - background-color: #e7e7e7; -} -@media (max-width: 767px) { - .navbar-default .navbar-nav .open .dropdown-menu > li > a { - color: #777; - } - .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { - color: #333; - background-color: transparent; - } - .navbar-default .navbar-nav .open .dropdown-menu > .active > a, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #555; - background-color: #e7e7e7; - } - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #ccc; - background-color: transparent; - } -} -.navbar-default .navbar-link { - color: #777; -} -.navbar-default .navbar-link:hover { - color: #333; -} -.navbar-inverse { - background-color: #222; - border-color: #080808; -} -.navbar-inverse .navbar-brand { - color: #999; -} -.navbar-inverse .navbar-brand:hover, -.navbar-inverse .navbar-brand:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-text { - color: #999; -} -.navbar-inverse .navbar-nav > li > a { - color: #999; -} -.navbar-inverse .navbar-nav > li > a:hover, -.navbar-inverse .navbar-nav > li > a:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:hover, -.navbar-inverse .navbar-nav > .active > a:focus { - color: #fff; - background-color: #080808; -} -.navbar-inverse .navbar-nav > .disabled > a, -.navbar-inverse .navbar-nav > .disabled > a:hover, -.navbar-inverse .navbar-nav > .disabled > a:focus { - color: #444; - background-color: transparent; -} -.navbar-inverse .navbar-toggle { - border-color: #333; -} -.navbar-inverse .navbar-toggle:hover, -.navbar-inverse .navbar-toggle:focus { - background-color: #333; -} -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #fff; -} -.navbar-inverse .navbar-collapse, -.navbar-inverse .navbar-form { - border-color: #101010; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .open > a:hover, -.navbar-inverse .navbar-nav > .open > a:focus { - color: #fff; - background-color: #080808; -} -@media (max-width: 767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { - border-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu .divider { - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #999; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #fff; - background-color: transparent; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #444; - background-color: transparent; - } -} -.navbar-inverse .navbar-link { - color: #999; -} -.navbar-inverse .navbar-link:hover { - color: #fff; -} -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; -} -.breadcrumb > li + li:before { - padding: 0 5px; - color: #ccc; - content: "/\00a0"; -} -.breadcrumb > .active { - color: #999; -} -.pagination { - display: inline-block; - padding-left: 0; - margin: 20px 0; - border-radius: 4px; -} -.pagination > li { - display: inline; -} -.pagination > li > a, -.pagination > li > span { - position: relative; - float: left; - padding: 6px 12px; - margin-left: -1px; - line-height: 1.42857143; - color: #428bca; - text-decoration: none; - background-color: #fff; - border: 1px solid #ddd; -} -.pagination > li:first-child > a, -.pagination > li:first-child > span { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pagination > li:last-child > a, -.pagination > li:last-child > span { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pagination > li > a:hover, -.pagination > li > span:hover, -.pagination > li > a:focus, -.pagination > li > span:focus { - color: #2a6496; - background-color: #eee; - border-color: #ddd; -} -.pagination > .active > a, -.pagination > .active > span, -.pagination > .active > a:hover, -.pagination > .active > span:hover, -.pagination > .active > a:focus, -.pagination > .active > span:focus { - z-index: 2; - color: #fff; - cursor: default; - background-color: #428bca; - border-color: #428bca; -} -.pagination > .disabled > span, -.pagination > .disabled > span:hover, -.pagination > .disabled > span:focus, -.pagination > .disabled > a, -.pagination > .disabled > a:hover, -.pagination > .disabled > a:focus { - color: #999; - cursor: not-allowed; - background-color: #fff; - border-color: #ddd; -} -.pagination-lg > li > a, -.pagination-lg > li > span { - padding: 10px 16px; - font-size: 18px; -} -.pagination-lg > li:first-child > a, -.pagination-lg > li:first-child > span { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; -} -.pagination-lg > li:last-child > a, -.pagination-lg > li:last-child > span { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} -.pagination-sm > li > a, -.pagination-sm > li > span { - padding: 5px 10px; - font-size: 12px; -} -.pagination-sm > li:first-child > a, -.pagination-sm > li:first-child > span { - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; -} -.pagination-sm > li:last-child > a, -.pagination-sm > li:last-child > span { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} -.pager { - padding-left: 0; - margin: 20px 0; - text-align: center; - list-style: none; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #eee; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #999; - cursor: not-allowed; - background-color: #fff; -} -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; -} -.label[href]:hover, -.label[href]:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.label:empty { - display: none; -} -.btn .label { - position: relative; - top: -1px; -} -.label-default { - background-color: #999; -} -.label-default[href]:hover, -.label-default[href]:focus { - background-color: #808080; -} -.label-primary { - background-color: #428bca; -} -.label-primary[href]:hover, -.label-primary[href]:focus { - background-color: #3071a9; -} -.label-success { - background-color: #5cb85c; -} -.label-success[href]:hover, -.label-success[href]:focus { - background-color: #449d44; -} -.label-info { - background-color: #5bc0de; -} -.label-info[href]:hover, -.label-info[href]:focus { - background-color: #31b0d5; -} -.label-warning { - background-color: #f0ad4e; -} -.label-warning[href]:hover, -.label-warning[href]:focus { - background-color: #ec971f; -} -.label-danger { - background-color: #d9534f; -} -.label-danger[href]:hover, -.label-danger[href]:focus { - background-color: #c9302c; -} -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: 12px; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - background-color: #999; - border-radius: 10px; -} -.badge:empty { - display: none; -} -.btn .badge { - position: relative; - top: -1px; -} -.btn-xs .badge { - top: 0; - padding: 1px 5px; -} -a.badge:hover, -a.badge:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -a.list-group-item.active > .badge, -.nav-pills > .active > a > .badge { - color: #428bca; - background-color: #fff; -} -.nav-pills > li > a > .badge { - margin-left: 3px; -} -.jumbotron { - padding: 30px; - margin-bottom: 30px; - color: inherit; - background-color: #eee; -} -.jumbotron h1, -.jumbotron .h1 { - color: inherit; -} -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200; -} -.container .jumbotron { - border-radius: 6px; -} -.jumbotron .container { - max-width: 100%; -} -@media screen and (min-width: 768px) { - .jumbotron { - padding-top: 48px; - padding-bottom: 48px; - } - .container .jumbotron { - padding-right: 60px; - padding-left: 60px; - } - .jumbotron h1, - .jumbotron .h1 { - font-size: 63px; - } -} -.thumbnail { - display: block; - padding: 4px; - margin-bottom: 20px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; -} -.thumbnail > img, -.thumbnail a > img { - margin-right: auto; - margin-left: auto; -} -a.thumbnail:hover, -a.thumbnail:focus, -a.thumbnail.active { - border-color: #428bca; -} -.thumbnail .caption { - padding: 9px; - color: #333; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; -} -.alert h4 { - margin-top: 0; - color: inherit; -} -.alert .alert-link { - font-weight: bold; -} -.alert > p, -.alert > ul { - margin-bottom: 0; -} -.alert > p + p { - margin-top: 5px; -} -.alert-dismissable { - padding-right: 35px; -} -.alert-dismissable .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; -} -.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.alert-success hr { - border-top-color: #c9e2b3; -} -.alert-success .alert-link { - color: #2b542c; -} -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.alert-info hr { - border-top-color: #a6e1ec; -} -.alert-info .alert-link { - color: #245269; -} -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.alert-warning hr { - border-top-color: #f7e1b5; -} -.alert-warning .alert-link { - color: #66512c; -} -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.alert-danger hr { - border-top-color: #e4b9c0; -} -.alert-danger .alert-link { - color: #843534; -} -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f5f5f5; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); -} -.progress-bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - line-height: 20px; - color: #fff; - text-align: center; - background-color: #428bca; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - -webkit-transition: width .6s ease; - transition: width .6s ease; -} -.progress-striped .progress-bar { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-size: 40px 40px; -} -.progress.active .progress-bar { - -webkit-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-bar-success { - background-color: #5cb85c; -} -.progress-striped .progress-bar-success { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-info { - background-color: #5bc0de; -} -.progress-striped .progress-bar-info { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-warning { - background-color: #f0ad4e; -} -.progress-striped .progress-bar-warning { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-danger { - background-color: #d9534f; -} -.progress-striped .progress-bar-danger { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.media, -.media-body { - overflow: hidden; - zoom: 1; -} -.media, -.media .media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media-object { - display: block; -} -.media-heading { - margin: 0 0 5px; -} -.media > .pull-left { - margin-right: 10px; -} -.media > .pull-right { - margin-left: 10px; -} -.media-list { - padding-left: 0; - list-style: none; -} -.list-group { - padding-left: 0; - margin-bottom: 20px; -} -.list-group-item { - position: relative; - display: block; - padding: 10px 15px; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid #ddd; -} -.list-group-item:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -.list-group-item > .badge { - float: right; -} -.list-group-item > .badge + .badge { - margin-right: 5px; -} -a.list-group-item { - color: #555; -} -a.list-group-item .list-group-item-heading { - color: #333; -} -a.list-group-item:hover, -a.list-group-item:focus { - text-decoration: none; - background-color: #f5f5f5; -} -a.list-group-item.active, -a.list-group-item.active:hover, -a.list-group-item.active:focus { - z-index: 2; - color: #fff; - background-color: #428bca; - border-color: #428bca; -} -a.list-group-item.active .list-group-item-heading, -a.list-group-item.active:hover .list-group-item-heading, -a.list-group-item.active:focus .list-group-item-heading { - color: inherit; -} -a.list-group-item.active .list-group-item-text, -a.list-group-item.active:hover .list-group-item-text, -a.list-group-item.active:focus .list-group-item-text { - color: #e1edf7; -} -.list-group-item-success { - color: #3c763d; - background-color: #dff0d8; -} -a.list-group-item-success { - color: #3c763d; -} -a.list-group-item-success .list-group-item-heading { - color: inherit; -} -a.list-group-item-success:hover, -a.list-group-item-success:focus { - color: #3c763d; - background-color: #d0e9c6; -} -a.list-group-item-success.active, -a.list-group-item-success.active:hover, -a.list-group-item-success.active:focus { - color: #fff; - background-color: #3c763d; - border-color: #3c763d; -} -.list-group-item-info { - color: #31708f; - background-color: #d9edf7; -} -a.list-group-item-info { - color: #31708f; -} -a.list-group-item-info .list-group-item-heading { - color: inherit; -} -a.list-group-item-info:hover, -a.list-group-item-info:focus { - color: #31708f; - background-color: #c4e3f3; -} -a.list-group-item-info.active, -a.list-group-item-info.active:hover, -a.list-group-item-info.active:focus { - color: #fff; - background-color: #31708f; - border-color: #31708f; -} -.list-group-item-warning { - color: #8a6d3b; - background-color: #fcf8e3; -} -a.list-group-item-warning { - color: #8a6d3b; -} -a.list-group-item-warning .list-group-item-heading { - color: inherit; -} -a.list-group-item-warning:hover, -a.list-group-item-warning:focus { - color: #8a6d3b; - background-color: #faf2cc; -} -a.list-group-item-warning.active, -a.list-group-item-warning.active:hover, -a.list-group-item-warning.active:focus { - color: #fff; - background-color: #8a6d3b; - border-color: #8a6d3b; -} -.list-group-item-danger { - color: #a94442; - background-color: #f2dede; -} -a.list-group-item-danger { - color: #a94442; -} -a.list-group-item-danger .list-group-item-heading { - color: inherit; -} -a.list-group-item-danger:hover, -a.list-group-item-danger:focus { - color: #a94442; - background-color: #ebcccc; -} -a.list-group-item-danger.active, -a.list-group-item-danger.active:hover, -a.list-group-item-danger.active:focus { - color: #fff; - background-color: #a94442; - border-color: #a94442; -} -.list-group-item-heading { - margin-top: 0; - margin-bottom: 5px; -} -.list-group-item-text { - margin-bottom: 0; - line-height: 1.3; -} -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: 0 1px 1px rgba(0, 0, 0, .05); -} -.panel-body { - padding: 15px; -} -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel-heading > .dropdown .dropdown-toggle { - color: inherit; -} -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; - color: inherit; -} -.panel-title > a { - color: inherit; -} -.panel-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .list-group { - margin-bottom: 0; -} -.panel > .list-group .list-group-item { - border-width: 1px 0; - border-radius: 0; -} -.panel > .list-group:first-child .list-group-item:first-child { - border-top: 0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .list-group:last-child .list-group-item:last-child { - border-bottom: 0; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel-heading + .list-group .list-group-item:first-child { - border-top-width: 0; -} -.panel > .table, -.panel > .table-responsive > .table { - margin-bottom: 0; -} -.panel > .table:first-child, -.panel > .table-responsive:first-child > .table:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { - border-top-left-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { - border-top-right-radius: 3px; -} -.panel > .table:last-child, -.panel > .table-responsive:last-child > .table:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { - border-bottom-right-radius: 3px; -} -.panel > .panel-body + .table, -.panel > .panel-body + .table-responsive { - border-top: 1px solid #ddd; -} -.panel > .table > tbody:first-child > tr:first-child th, -.panel > .table > tbody:first-child > tr:first-child td { - border-top: 0; -} -.panel > .table-bordered, -.panel > .table-responsive > .table-bordered { - border: 0; -} -.panel > .table-bordered > thead > tr > th:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, -.panel > .table-bordered > tbody > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, -.panel > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-bordered > thead > tr > td:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, -.panel > .table-bordered > tbody > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, -.panel > .table-bordered > tfoot > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; -} -.panel > .table-bordered > thead > tr > th:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, -.panel > .table-bordered > tbody > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, -.panel > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-bordered > thead > tr > td:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, -.panel > .table-bordered > tbody > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, -.panel > .table-bordered > tfoot > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; -} -.panel > .table-bordered > thead > tr:first-child > td, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, -.panel > .table-bordered > tbody > tr:first-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, -.panel > .table-bordered > thead > tr:first-child > th, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, -.panel > .table-bordered > tbody > tr:first-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { - border-bottom: 0; -} -.panel > .table-bordered > tbody > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, -.panel > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-bordered > tbody > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, -.panel > .table-bordered > tfoot > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { - border-bottom: 0; -} -.panel > .table-responsive { - margin-bottom: 0; - border: 0; -} -.panel-group { - margin-bottom: 20px; -} -.panel-group .panel { - margin-bottom: 0; - overflow: hidden; - border-radius: 4px; -} -.panel-group .panel + .panel { - margin-top: 5px; -} -.panel-group .panel-heading { - border-bottom: 0; -} -.panel-group .panel-heading + .panel-collapse .panel-body { - border-top: 1px solid #ddd; -} -.panel-group .panel-footer { - border-top: 0; -} -.panel-group .panel-footer + .panel-collapse .panel-body { - border-bottom: 1px solid #ddd; -} -.panel-default { - border-color: #ddd; -} -.panel-default > .panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #ddd; -} -.panel-default > .panel-heading + .panel-collapse .panel-body { - border-top-color: #ddd; -} -.panel-default > .panel-footer + .panel-collapse .panel-body { - border-bottom-color: #ddd; -} -.panel-primary { - border-color: #428bca; -} -.panel-primary > .panel-heading { - color: #fff; - background-color: #428bca; - border-color: #428bca; -} -.panel-primary > .panel-heading + .panel-collapse .panel-body { - border-top-color: #428bca; -} -.panel-primary > .panel-footer + .panel-collapse .panel-body { - border-bottom-color: #428bca; -} -.panel-success { - border-color: #d6e9c6; -} -.panel-success > .panel-heading { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.panel-success > .panel-heading + .panel-collapse .panel-body { - border-top-color: #d6e9c6; -} -.panel-success > .panel-footer + .panel-collapse .panel-body { - border-bottom-color: #d6e9c6; -} -.panel-info { - border-color: #bce8f1; -} -.panel-info > .panel-heading { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.panel-info > .panel-heading + .panel-collapse .panel-body { - border-top-color: #bce8f1; -} -.panel-info > .panel-footer + .panel-collapse .panel-body { - border-bottom-color: #bce8f1; -} -.panel-warning { - border-color: #faebcc; -} -.panel-warning > .panel-heading { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.panel-warning > .panel-heading + .panel-collapse .panel-body { - border-top-color: #faebcc; -} -.panel-warning > .panel-footer + .panel-collapse .panel-body { - border-bottom-color: #faebcc; -} -.panel-danger { - border-color: #ebccd1; -} -.panel-danger > .panel-heading { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.panel-danger > .panel-heading + .panel-collapse .panel-body { - border-top-color: #ebccd1; -} -.panel-danger > .panel-footer + .panel-collapse .panel-body { - border-bottom-color: #ebccd1; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, .15); -} -.well-lg { - padding: 24px; - border-radius: 6px; -} -.well-sm { - padding: 9px; - border-radius: 3px; -} -.close { - float: right; - font-size: 21px; - font-weight: bold; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: .2; -} -.close:hover, -.close:focus { - color: #000; - text-decoration: none; - cursor: pointer; - filter: alpha(opacity=50); - opacity: .5; -} -button.close { - -webkit-appearance: none; - padding: 0; - cursor: pointer; - background: transparent; - border: 0; -} -.modal-open { - overflow: hidden; -} -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: auto; - overflow-y: scroll; - -webkit-overflow-scrolling: touch; - outline: 0; -} -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform .3s ease-out; - -moz-transition: -moz-transform .3s ease-out; - -o-transition: -o-transform .3s ease-out; - transition: transform .3s ease-out; - -webkit-transform: translate(0, -25%); - -ms-transform: translate(0, -25%); - transform: translate(0, -25%); -} -.modal.in .modal-dialog { - -webkit-transform: translate(0, 0); - -ms-transform: translate(0, 0); - transform: translate(0, 0); -} -.modal-dialog { - position: relative; - width: auto; - margin: 10px; -} -.modal-content { - position: relative; - background-color: #fff; - background-clip: padding-box; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - outline: none; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); - box-shadow: 0 3px 9px rgba(0, 0, 0, .5); -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000; -} -.modal-backdrop.fade { - filter: alpha(opacity=0); - opacity: 0; -} -.modal-backdrop.in { - filter: alpha(opacity=50); - opacity: .5; -} -.modal-header { - min-height: 16.42857143px; - padding: 15px; - border-bottom: 1px solid #e5e5e5; -} -.modal-header .close { - margin-top: -2px; -} -.modal-title { - margin: 0; - line-height: 1.42857143; -} -.modal-body { - position: relative; - padding: 20px; -} -.modal-footer { - padding: 19px 20px 20px; - margin-top: 15px; - text-align: right; - border-top: 1px solid #e5e5e5; -} -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -@media (min-width: 768px) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - } - .modal-sm { - width: 300px; - } -} -@media (min-width: 992px) { - .modal-lg { - width: 900px; - } -} -.tooltip { - position: absolute; - z-index: 1030; - display: block; - font-size: 12px; - line-height: 1.4; - visibility: visible; - filter: alpha(opacity=0); - opacity: 0; -} -.tooltip.in { - filter: alpha(opacity=90); - opacity: .9; -} -.tooltip.top { - padding: 5px 0; - margin-top: -3px; -} -.tooltip.right { - padding: 0 5px; - margin-left: 3px; -} -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px; -} -.tooltip.left { - padding: 0 5px; - margin-left: -3px; -} -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #fff; - text-align: center; - text-decoration: none; - background-color: #000; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-left .tooltip-arrow { - bottom: 0; - left: 5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-right .tooltip-arrow { - right: 5px; - bottom: 0; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-left .tooltip-arrow { - top: 0; - left: 5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-right .tooltip-arrow { - top: 0; - right: 5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1010; - display: none; - max-width: 276px; - padding: 1px; - text-align: left; - white-space: normal; - background-color: #fff; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - box-shadow: 0 5px 10px rgba(0, 0, 0, .2); -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - font-weight: normal; - line-height: 18px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0; -} -.popover-content { - padding: 9px 14px; -} -.popover > .arrow, -.popover > .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover > .arrow { - border-width: 11px; -} -.popover > .arrow:after { - content: ""; - border-width: 10px; -} -.popover.top > .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, .25); - border-bottom-width: 0; -} -.popover.top > .arrow:after { - bottom: 1px; - margin-left: -10px; - content: " "; - border-top-color: #fff; - border-bottom-width: 0; -} -.popover.right > .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, .25); - border-left-width: 0; -} -.popover.right > .arrow:after { - bottom: -10px; - left: 1px; - content: " "; - border-right-color: #fff; - border-left-width: 0; -} -.popover.bottom > .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, .25); -} -.popover.bottom > .arrow:after { - top: 1px; - margin-left: -10px; - content: " "; - border-top-width: 0; - border-bottom-color: #fff; -} -.popover.left > .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, .25); -} -.popover.left > .arrow:after { - right: 1px; - bottom: -10px; - content: " "; - border-right-width: 0; - border-left-color: #fff; -} -.carousel { - position: relative; -} -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: .6s ease-in-out left; - transition: .6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - line-height: 1; -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 15%; - font-size: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); - filter: alpha(opacity=50); - opacity: .5; -} -.carousel-control.left { - background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, .5) 0%), color-stop(rgba(0, 0, 0, .0001) 100%)); - background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control.right { - right: 0; - left: auto; - background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, .0001) 0%), color-stop(rgba(0, 0, 0, .5) 100%)); - background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control:hover, -.carousel-control:focus { - color: #fff; - text-decoration: none; - filter: alpha(opacity=90); - outline: none; - opacity: .9; -} -.carousel-control .icon-prev, -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-left, -.carousel-control .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; -} -.carousel-control .icon-prev, -.carousel-control .glyphicon-chevron-left { - left: 50%; -} -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-right { - right: 50%; -} -.carousel-control .icon-prev, -.carousel-control .icon-next { - width: 20px; - height: 20px; - margin-top: -10px; - margin-left: -10px; - font-family: serif; -} -.carousel-control .icon-prev:before { - content: '\2039'; -} -.carousel-control .icon-next:before { - content: '\203a'; -} -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - padding-left: 0; - margin-left: -30%; - text-align: center; - list-style: none; -} -.carousel-indicators li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - cursor: pointer; - background-color: #000 \9; - background-color: rgba(0, 0, 0, 0); - border: 1px solid #fff; - border-radius: 10px; -} -.carousel-indicators .active { - width: 12px; - height: 12px; - margin: 0; - background-color: #fff; -} -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); -} -.carousel-caption .btn { - text-shadow: none; -} -@media screen and (min-width: 768px) { - .carousel-control .glyphicon-chevron-left, - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-prev, - .carousel-control .icon-next { - width: 30px; - height: 30px; - margin-top: -15px; - margin-left: -15px; - font-size: 30px; - } - .carousel-caption { - right: 20%; - left: 20%; - padding-bottom: 30px; - } - .carousel-indicators { - bottom: 20px; - } -} -.clearfix:before, -.clearfix:after, -.container:before, -.container:after, -.container-fluid:before, -.container-fluid:after, -.row:before, -.row:after, -.form-horizontal .form-group:before, -.form-horizontal .form-group:after, -.btn-toolbar:before, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after, -.nav:before, -.nav:after, -.navbar:before, -.navbar:after, -.navbar-header:before, -.navbar-header:after, -.navbar-collapse:before, -.navbar-collapse:after, -.pager:before, -.pager:after, -.panel-body:before, -.panel-body:after, -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} -.clearfix:after, -.container:after, -.container-fluid:after, -.row:after, -.form-horizontal .form-group:after, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:after, -.nav:after, -.navbar:after, -.navbar-header:after, -.navbar-collapse:after, -.pager:after, -.panel-body:after, -.modal-footer:after { - clear: both; -} -.center-block { - display: block; - margin-right: auto; - margin-left: auto; -} -.pull-right { - float: right !important; -} -.pull-left { - float: left !important; -} -.hide { - display: none !important; -} -.show { - display: block !important; -} -.invisible { - visibility: hidden; -} -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.hidden { - display: none !important; - visibility: hidden !important; -} -.affix { - position: fixed; -} -@-ms-viewport { - width: device-width; -} -.visible-xs, -.visible-sm, -.visible-md, -.visible-lg { - display: none !important; -} -@media (max-width: 767px) { - .visible-xs { - display: block !important; - } - table.visible-xs { - display: table; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } - table.visible-sm { - display: table; - } - tr.visible-sm { - display: table-row !important; - } - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md { - display: block !important; - } - table.visible-md { - display: table; - } - tr.visible-md { - display: table-row !important; - } - th.visible-md, - td.visible-md { - display: table-cell !important; - } -} -@media (min-width: 1200px) { - .visible-lg { - display: block !important; - } - table.visible-lg { - display: table; - } - tr.visible-lg { - display: table-row !important; - } - th.visible-lg, - td.visible-lg { - display: table-cell !important; - } -} -@media (max-width: 767px) { - .hidden-xs { - display: none !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .hidden-sm { - display: none !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .hidden-md { - display: none !important; - } -} -@media (min-width: 1200px) { - .hidden-lg { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: block !important; - } - table.visible-print { - display: table; - } - tr.visible-print { - display: table-row !important; - } - th.visible-print, - td.visible-print { - display: table-cell !important; - } -} -@media print { - .hidden-print { - display: none !important; - } -} -/*# sourceMappingURL=bootstrap.css.map */ diff -Nru cadvisor-0.23.0+dfsg/debian/missing-sources/bootstrap.css.url cadvisor-0.25.0+dfsg/debian/missing-sources/bootstrap.css.url --- cadvisor-0.23.0+dfsg/debian/missing-sources/bootstrap.css.url 2015-10-13 04:16:03.000000000 +0000 +++ cadvisor-0.25.0+dfsg/debian/missing-sources/bootstrap.css.url 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -https://raw.githubusercontent.com/twbs/bootstrap/v3.1.1/dist/css/bootstrap.css diff -Nru cadvisor-0.23.0+dfsg/debian/missing-sources/bootstrap.js cadvisor-0.25.0+dfsg/debian/missing-sources/bootstrap.js --- cadvisor-0.23.0+dfsg/debian/missing-sources/bootstrap.js 2015-10-13 04:15:35.000000000 +0000 +++ cadvisor-0.25.0+dfsg/debian/missing-sources/bootstrap.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,1951 +0,0 @@ -/*! - * Bootstrap v3.1.1 (http://getbootstrap.com) - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ - -if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') } - -/* ======================================================================== - * Bootstrap: transition.js v3.1.1 - * http://getbootstrap.com/javascript/#transitions - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) - // ============================================================ - - function transitionEnd() { - var el = document.createElement('bootstrap') - - var transEndEventNames = { - 'WebkitTransition' : 'webkitTransitionEnd', - 'MozTransition' : 'transitionend', - 'OTransition' : 'oTransitionEnd otransitionend', - 'transition' : 'transitionend' - } - - for (var name in transEndEventNames) { - if (el.style[name] !== undefined) { - return { end: transEndEventNames[name] } - } - } - - return false // explicit for ie8 ( ._.) - } - - // http://blog.alexmaccaw.com/css-transitions - $.fn.emulateTransitionEnd = function (duration) { - var called = false, $el = this - $(this).one($.support.transition.end, function () { called = true }) - var callback = function () { if (!called) $($el).trigger($.support.transition.end) } - setTimeout(callback, duration) - return this - } - - $(function () { - $.support.transition = transitionEnd() - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: alert.js v3.1.1 - * http://getbootstrap.com/javascript/#alerts - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // ALERT CLASS DEFINITION - // ====================== - - var dismiss = '[data-dismiss="alert"]' - var Alert = function (el) { - $(el).on('click', dismiss, this.close) - } - - Alert.prototype.close = function (e) { - var $this = $(this) - var selector = $this.attr('data-target') - - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 - } - - var $parent = $(selector) - - if (e) e.preventDefault() - - if (!$parent.length) { - $parent = $this.hasClass('alert') ? $this : $this.parent() - } - - $parent.trigger(e = $.Event('close.bs.alert')) - - if (e.isDefaultPrevented()) return - - $parent.removeClass('in') - - function removeElement() { - $parent.trigger('closed.bs.alert').remove() - } - - $.support.transition && $parent.hasClass('fade') ? - $parent - .one($.support.transition.end, removeElement) - .emulateTransitionEnd(150) : - removeElement() - } - - - // ALERT PLUGIN DEFINITION - // ======================= - - var old = $.fn.alert - - $.fn.alert = function (option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.alert') - - if (!data) $this.data('bs.alert', (data = new Alert(this))) - if (typeof option == 'string') data[option].call($this) - }) - } - - $.fn.alert.Constructor = Alert - - - // ALERT NO CONFLICT - // ================= - - $.fn.alert.noConflict = function () { - $.fn.alert = old - return this - } - - - // ALERT DATA-API - // ============== - - $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: button.js v3.1.1 - * http://getbootstrap.com/javascript/#buttons - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // BUTTON PUBLIC CLASS DEFINITION - // ============================== - - var Button = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, Button.DEFAULTS, options) - this.isLoading = false - } - - Button.DEFAULTS = { - loadingText: 'loading...' - } - - Button.prototype.setState = function (state) { - var d = 'disabled' - var $el = this.$element - var val = $el.is('input') ? 'val' : 'html' - var data = $el.data() - - state = state + 'Text' - - if (!data.resetText) $el.data('resetText', $el[val]()) - - $el[val](data[state] || this.options[state]) - - // push to event loop to allow forms to submit - setTimeout($.proxy(function () { - if (state == 'loadingText') { - this.isLoading = true - $el.addClass(d).attr(d, d) - } else if (this.isLoading) { - this.isLoading = false - $el.removeClass(d).removeAttr(d) - } - }, this), 0) - } - - Button.prototype.toggle = function () { - var changed = true - var $parent = this.$element.closest('[data-toggle="buttons"]') - - if ($parent.length) { - var $input = this.$element.find('input') - if ($input.prop('type') == 'radio') { - if ($input.prop('checked') && this.$element.hasClass('active')) changed = false - else $parent.find('.active').removeClass('active') - } - if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') - } - - if (changed) this.$element.toggleClass('active') - } - - - // BUTTON PLUGIN DEFINITION - // ======================== - - var old = $.fn.button - - $.fn.button = function (option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.button') - var options = typeof option == 'object' && option - - if (!data) $this.data('bs.button', (data = new Button(this, options))) - - if (option == 'toggle') data.toggle() - else if (option) data.setState(option) - }) - } - - $.fn.button.Constructor = Button - - - // BUTTON NO CONFLICT - // ================== - - $.fn.button.noConflict = function () { - $.fn.button = old - return this - } - - - // BUTTON DATA-API - // =============== - - $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) { - var $btn = $(e.target) - if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') - $btn.button('toggle') - e.preventDefault() - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: carousel.js v3.1.1 - * http://getbootstrap.com/javascript/#carousel - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // CAROUSEL CLASS DEFINITION - // ========================= - - var Carousel = function (element, options) { - this.$element = $(element) - this.$indicators = this.$element.find('.carousel-indicators') - this.options = options - this.paused = - this.sliding = - this.interval = - this.$active = - this.$items = null - - this.options.pause == 'hover' && this.$element - .on('mouseenter', $.proxy(this.pause, this)) - .on('mouseleave', $.proxy(this.cycle, this)) - } - - Carousel.DEFAULTS = { - interval: 5000, - pause: 'hover', - wrap: true - } - - Carousel.prototype.cycle = function (e) { - e || (this.paused = false) - - this.interval && clearInterval(this.interval) - - this.options.interval - && !this.paused - && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) - - return this - } - - Carousel.prototype.getActiveIndex = function () { - this.$active = this.$element.find('.item.active') - this.$items = this.$active.parent().children() - - return this.$items.index(this.$active) - } - - Carousel.prototype.to = function (pos) { - var that = this - var activeIndex = this.getActiveIndex() - - if (pos > (this.$items.length - 1) || pos < 0) return - - if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) - if (activeIndex == pos) return this.pause().cycle() - - return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) - } - - Carousel.prototype.pause = function (e) { - e || (this.paused = true) - - if (this.$element.find('.next, .prev').length && $.support.transition) { - this.$element.trigger($.support.transition.end) - this.cycle(true) - } - - this.interval = clearInterval(this.interval) - - return this - } - - Carousel.prototype.next = function () { - if (this.sliding) return - return this.slide('next') - } - - Carousel.prototype.prev = function () { - if (this.sliding) return - return this.slide('prev') - } - - Carousel.prototype.slide = function (type, next) { - var $active = this.$element.find('.item.active') - var $next = next || $active[type]() - var isCycling = this.interval - var direction = type == 'next' ? 'left' : 'right' - var fallback = type == 'next' ? 'first' : 'last' - var that = this - - if (!$next.length) { - if (!this.options.wrap) return - $next = this.$element.find('.item')[fallback]() - } - - if ($next.hasClass('active')) return this.sliding = false - - var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction }) - this.$element.trigger(e) - if (e.isDefaultPrevented()) return - - this.sliding = true - - isCycling && this.pause() - - if (this.$indicators.length) { - this.$indicators.find('.active').removeClass('active') - this.$element.one('slid.bs.carousel', function () { - var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) - $nextIndicator && $nextIndicator.addClass('active') - }) - } - - if ($.support.transition && this.$element.hasClass('slide')) { - $next.addClass(type) - $next[0].offsetWidth // force reflow - $active.addClass(direction) - $next.addClass(direction) - $active - .one($.support.transition.end, function () { - $next.removeClass([type, direction].join(' ')).addClass('active') - $active.removeClass(['active', direction].join(' ')) - that.sliding = false - setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0) - }) - .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000) - } else { - $active.removeClass('active') - $next.addClass('active') - this.sliding = false - this.$element.trigger('slid.bs.carousel') - } - - isCycling && this.cycle() - - return this - } - - - // CAROUSEL PLUGIN DEFINITION - // ========================== - - var old = $.fn.carousel - - $.fn.carousel = function (option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.carousel') - var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) - var action = typeof option == 'string' ? option : options.slide - - if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) - if (typeof option == 'number') data.to(option) - else if (action) data[action]() - else if (options.interval) data.pause().cycle() - }) - } - - $.fn.carousel.Constructor = Carousel - - - // CAROUSEL NO CONFLICT - // ==================== - - $.fn.carousel.noConflict = function () { - $.fn.carousel = old - return this - } - - - // CAROUSEL DATA-API - // ================= - - $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { - var $this = $(this), href - var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 - var options = $.extend({}, $target.data(), $this.data()) - var slideIndex = $this.attr('data-slide-to') - if (slideIndex) options.interval = false - - $target.carousel(options) - - if (slideIndex = $this.attr('data-slide-to')) { - $target.data('bs.carousel').to(slideIndex) - } - - e.preventDefault() - }) - - $(window).on('load', function () { - $('[data-ride="carousel"]').each(function () { - var $carousel = $(this) - $carousel.carousel($carousel.data()) - }) - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: collapse.js v3.1.1 - * http://getbootstrap.com/javascript/#collapse - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // COLLAPSE PUBLIC CLASS DEFINITION - // ================================ - - var Collapse = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, Collapse.DEFAULTS, options) - this.transitioning = null - - if (this.options.parent) this.$parent = $(this.options.parent) - if (this.options.toggle) this.toggle() - } - - Collapse.DEFAULTS = { - toggle: true - } - - Collapse.prototype.dimension = function () { - var hasWidth = this.$element.hasClass('width') - return hasWidth ? 'width' : 'height' - } - - Collapse.prototype.show = function () { - if (this.transitioning || this.$element.hasClass('in')) return - - var startEvent = $.Event('show.bs.collapse') - this.$element.trigger(startEvent) - if (startEvent.isDefaultPrevented()) return - - var actives = this.$parent && this.$parent.find('> .panel > .in') - - if (actives && actives.length) { - var hasData = actives.data('bs.collapse') - if (hasData && hasData.transitioning) return - actives.collapse('hide') - hasData || actives.data('bs.collapse', null) - } - - var dimension = this.dimension() - - this.$element - .removeClass('collapse') - .addClass('collapsing') - [dimension](0) - - this.transitioning = 1 - - var complete = function () { - this.$element - .removeClass('collapsing') - .addClass('collapse in') - [dimension]('auto') - this.transitioning = 0 - this.$element.trigger('shown.bs.collapse') - } - - if (!$.support.transition) return complete.call(this) - - var scrollSize = $.camelCase(['scroll', dimension].join('-')) - - this.$element - .one($.support.transition.end, $.proxy(complete, this)) - .emulateTransitionEnd(350) - [dimension](this.$element[0][scrollSize]) - } - - Collapse.prototype.hide = function () { - if (this.transitioning || !this.$element.hasClass('in')) return - - var startEvent = $.Event('hide.bs.collapse') - this.$element.trigger(startEvent) - if (startEvent.isDefaultPrevented()) return - - var dimension = this.dimension() - - this.$element - [dimension](this.$element[dimension]()) - [0].offsetHeight - - this.$element - .addClass('collapsing') - .removeClass('collapse') - .removeClass('in') - - this.transitioning = 1 - - var complete = function () { - this.transitioning = 0 - this.$element - .trigger('hidden.bs.collapse') - .removeClass('collapsing') - .addClass('collapse') - } - - if (!$.support.transition) return complete.call(this) - - this.$element - [dimension](0) - .one($.support.transition.end, $.proxy(complete, this)) - .emulateTransitionEnd(350) - } - - Collapse.prototype.toggle = function () { - this[this.$element.hasClass('in') ? 'hide' : 'show']() - } - - - // COLLAPSE PLUGIN DEFINITION - // ========================== - - var old = $.fn.collapse - - $.fn.collapse = function (option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.collapse') - var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) - - if (!data && options.toggle && option == 'show') option = !option - if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.collapse.Constructor = Collapse - - - // COLLAPSE NO CONFLICT - // ==================== - - $.fn.collapse.noConflict = function () { - $.fn.collapse = old - return this - } - - - // COLLAPSE DATA-API - // ================= - - $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) { - var $this = $(this), href - var target = $this.attr('data-target') - || e.preventDefault() - || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 - var $target = $(target) - var data = $target.data('bs.collapse') - var option = data ? 'toggle' : $this.data() - var parent = $this.attr('data-parent') - var $parent = parent && $(parent) - - if (!data || !data.transitioning) { - if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed') - $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') - } - - $target.collapse(option) - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: dropdown.js v3.1.1 - * http://getbootstrap.com/javascript/#dropdowns - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // DROPDOWN CLASS DEFINITION - // ========================= - - var backdrop = '.dropdown-backdrop' - var toggle = '[data-toggle=dropdown]' - var Dropdown = function (element) { - $(element).on('click.bs.dropdown', this.toggle) - } - - Dropdown.prototype.toggle = function (e) { - var $this = $(this) - - if ($this.is('.disabled, :disabled')) return - - var $parent = getParent($this) - var isActive = $parent.hasClass('open') - - clearMenus() - - if (!isActive) { - if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { - // if mobile we use a backdrop because click events don't delegate - $('