Remove unneeded deps

This commit is contained in:
Christoph Blecker 2018-07-07 12:20:46 -07:00
parent 097f300a4d
commit f97fdf76dc
No known key found for this signature in database
GPG Key ID: B34A59A9D39F838B
35 changed files with 11 additions and 5407 deletions

50
Godeps/Godeps.json generated
View File

@ -134,10 +134,6 @@
"ImportPath": "github.com/PuerkitoBio/urlesc",
"Rev": "5bd2802263f21d8788851d5305584c82a5c75d7e"
},
{
"ImportPath": "github.com/abbot/go-http-auth",
"Rev": "c0ef4539dfab4d21c8ef20ba2924f9fc6f186d35"
},
{
"ImportPath": "github.com/armon/circbuf",
"Rev": "bbbad097214e2918d8543d5201d12bfd7bca254d"
@ -1526,11 +1522,6 @@
"Comment": "v0.30.2",
"Rev": "de723a090f4dd5390dc7c2acee37ba9c62f0cc09"
},
{
"ImportPath": "github.com/google/cadvisor/api",
"Comment": "v0.30.2",
"Rev": "de723a090f4dd5390dc7c2acee37ba9c62f0cc09"
},
{
"ImportPath": "github.com/google/cadvisor/cache/memory",
"Comment": "v0.30.2",
@ -1606,21 +1597,6 @@
"Comment": "v0.30.2",
"Rev": "de723a090f4dd5390dc7c2acee37ba9c62f0cc09"
},
{
"ImportPath": "github.com/google/cadvisor/healthz",
"Comment": "v0.30.2",
"Rev": "de723a090f4dd5390dc7c2acee37ba9c62f0cc09"
},
{
"ImportPath": "github.com/google/cadvisor/http",
"Comment": "v0.30.2",
"Rev": "de723a090f4dd5390dc7c2acee37ba9c62f0cc09"
},
{
"ImportPath": "github.com/google/cadvisor/http/mux",
"Comment": "v0.30.2",
"Rev": "de723a090f4dd5390dc7c2acee37ba9c62f0cc09"
},
{
"ImportPath": "github.com/google/cadvisor/info/v1",
"Comment": "v0.30.2",
@ -1661,16 +1637,6 @@
"Comment": "v0.30.2",
"Rev": "de723a090f4dd5390dc7c2acee37ba9c62f0cc09"
},
{
"ImportPath": "github.com/google/cadvisor/pages",
"Comment": "v0.30.2",
"Rev": "de723a090f4dd5390dc7c2acee37ba9c62f0cc09"
},
{
"ImportPath": "github.com/google/cadvisor/pages/static",
"Comment": "v0.30.2",
"Rev": "de723a090f4dd5390dc7c2acee37ba9c62f0cc09"
},
{
"ImportPath": "github.com/google/cadvisor/storage",
"Comment": "v0.30.2",
@ -1721,11 +1687,6 @@
"Comment": "v0.30.2",
"Rev": "de723a090f4dd5390dc7c2acee37ba9c62f0cc09"
},
{
"ImportPath": "github.com/google/cadvisor/validate",
"Comment": "v0.30.2",
"Rev": "de723a090f4dd5390dc7c2acee37ba9c62f0cc09"
},
{
"ImportPath": "github.com/google/cadvisor/version",
"Comment": "v0.30.2",
@ -1738,34 +1699,42 @@
},
{
"ImportPath": "github.com/google/certificate-transparency-go",
"Comment": "v1.0.10",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency-go/asn1",
"Comment": "v1.0.10",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency-go/client",
"Comment": "v1.0.10",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency-go/client/configpb",
"Comment": "v1.0.10",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency-go/jsonclient",
"Comment": "v1.0.10",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency-go/tls",
"Comment": "v1.0.10",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency-go/x509",
"Comment": "v1.0.10",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency-go/x509/pkix",
"Comment": "v1.0.10",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
@ -2007,6 +1976,7 @@
},
{
"ImportPath": "github.com/inconshreveable/mousetrap",
"Comment": "v1.0",
"Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
},
{
@ -2562,6 +2532,7 @@
},
{
"ImportPath": "github.com/quobyte/api",
"Comment": "v0.1.1-4-g206ef83",
"Rev": "206ef832283c1a0144bbc762be2634d49987b5ff"
},
{
@ -2876,6 +2847,7 @@
},
{
"ImportPath": "github.com/xiang90/probing",
"Comment": "0.0.1",
"Rev": "07dd2e8dfe18522e9c447ba95f2fe95262f63bb2"
},
{

1572
Godeps/LICENSES generated

File diff suppressed because it is too large Load Diff

6
vendor/BUILD vendored
View File

@ -31,7 +31,6 @@ filegroup(
"//vendor/github.com/Nvveen/Gotty:all-srcs",
"//vendor/github.com/PuerkitoBio/purell:all-srcs",
"//vendor/github.com/PuerkitoBio/urlesc:all-srcs",
"//vendor/github.com/abbot/go-http-auth:all-srcs",
"//vendor/github.com/armon/circbuf:all-srcs",
"//vendor/github.com/asaskevich/govalidator:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/aws:all-srcs",
@ -228,7 +227,6 @@ filegroup(
"//vendor/github.com/golang/protobuf/ptypes:all-srcs",
"//vendor/github.com/google/btree:all-srcs",
"//vendor/github.com/google/cadvisor/accelerators:all-srcs",
"//vendor/github.com/google/cadvisor/api:all-srcs",
"//vendor/github.com/google/cadvisor/cache/memory:all-srcs",
"//vendor/github.com/google/cadvisor/client/v2:all-srcs",
"//vendor/github.com/google/cadvisor/collector:all-srcs",
@ -236,18 +234,14 @@ filegroup(
"//vendor/github.com/google/cadvisor/devicemapper:all-srcs",
"//vendor/github.com/google/cadvisor/events:all-srcs",
"//vendor/github.com/google/cadvisor/fs:all-srcs",
"//vendor/github.com/google/cadvisor/healthz:all-srcs",
"//vendor/github.com/google/cadvisor/http:all-srcs",
"//vendor/github.com/google/cadvisor/info/v1:all-srcs",
"//vendor/github.com/google/cadvisor/info/v2:all-srcs",
"//vendor/github.com/google/cadvisor/machine:all-srcs",
"//vendor/github.com/google/cadvisor/manager:all-srcs",
"//vendor/github.com/google/cadvisor/metrics:all-srcs",
"//vendor/github.com/google/cadvisor/pages:all-srcs",
"//vendor/github.com/google/cadvisor/storage:all-srcs",
"//vendor/github.com/google/cadvisor/summary:all-srcs",
"//vendor/github.com/google/cadvisor/utils:all-srcs",
"//vendor/github.com/google/cadvisor/validate:all-srcs",
"//vendor/github.com/google/cadvisor/version:all-srcs",
"//vendor/github.com/google/cadvisor/zfs:all-srcs",
"//vendor/github.com/google/certificate-transparency-go:all-srcs",

View File

@ -1,5 +0,0 @@
*~
*.a
*.6
*.out
_testmain.go

View File

@ -1,30 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"auth.go",
"basic.go",
"digest.go",
"md5crypt.go",
"misc.go",
"users.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/abbot/go-http-auth",
importpath = "github.com/abbot/go-http-auth",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,178 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -1,12 +0,0 @@
include $(GOROOT)/src/Make.inc
TARG=auth_digest
GOFILES=\
auth.go\
digest.go\
basic.go\
misc.go\
md5crypt.go\
users.go\
include $(GOROOT)/src/Make.pkg

View File

@ -1,70 +0,0 @@
HTTP Authentication implementation in Go
========================================
This is an implementation of HTTP Basic and HTTP Digest authentication
in Go language. It is designed as a simple wrapper for
http.RequestHandler functions.
Features
--------
* Supports HTTP Basic and HTTP Digest authentication.
* Supports htpasswd and htdigest formatted files.
* Automatic reloading of password files.
* Pluggable interface for user/password storage.
* Supports MD5 and SHA1 for Basic authentication password storage.
* Configurable Digest nonce cache size with expiration.
* Wrapper for legacy http handlers (http.HandlerFunc interface)
Example usage
-------------
This is a complete working example for Basic auth:
package main
import (
auth "github.com/abbot/go-http-auth"
"fmt"
"net/http"
)
func Secret(user, realm string) string {
if user == "john" {
// password is "hello"
return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1"
}
return ""
}
func handle(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
fmt.Fprintf(w, "<html><body><h1>Hello, %s!</h1></body></html>", r.Username)
}
func main() {
authenticator := auth.NewBasicAuthenticator("example.com", Secret)
http.HandleFunc("/", authenticator.Wrap(handle))
http.ListenAndServe(":8080", nil)
}
See more examples in the "examples" directory.
Legal
-----
This module is developed under Apache 2.0 license, and can be used for
open and proprietary projects.
Copyright 2012-2013 Lev Shamardin
Licensed under the Apache License, Version 2.0 (the "License"); you
may not use this file or any other part of this project 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.

View File

@ -1,48 +0,0 @@
package auth
import "net/http"
/*
Request handlers must take AuthenticatedRequest instead of http.Request
*/
type AuthenticatedRequest struct {
http.Request
/*
Authenticated user name. Current API implies that Username is
never empty, which means that authentication is always done
before calling the request handler.
*/
Username string
}
/*
AuthenticatedHandlerFunc is like http.HandlerFunc, but takes
AuthenticatedRequest instead of http.Request
*/
type AuthenticatedHandlerFunc func(http.ResponseWriter, *AuthenticatedRequest)
/*
Authenticator wraps an AuthenticatedHandlerFunc with
authentication-checking code.
Typical Authenticator usage is something like:
authenticator := SomeAuthenticator(...)
http.HandleFunc("/", authenticator(my_handler))
Authenticator wrapper checks the user authentication and calls the
wrapped function only after authentication has succeeded. Otherwise,
it returns a handler which initiates the authentication procedure.
*/
type Authenticator func(AuthenticatedHandlerFunc) http.HandlerFunc
type AuthenticatorInterface interface {
Wrap(AuthenticatedHandlerFunc) http.HandlerFunc
}
func JustCheck(auth AuthenticatorInterface, wrapped http.HandlerFunc) http.HandlerFunc {
return auth.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
ar.Header.Set("X-Authenticated-Username", ar.Username)
wrapped(w, &ar.Request)
})
}

View File

@ -1,88 +0,0 @@
package auth
import (
"crypto/sha1"
"encoding/base64"
"net/http"
"strings"
)
type BasicAuth struct {
Realm string
Secrets SecretProvider
}
/*
Checks the username/password combination from the request. Returns
either an empty string (authentication failed) or the name of the
authenticated user.
Supports MD5 and SHA1 password entries
*/
func (a *BasicAuth) CheckAuth(r *http.Request) string {
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
if len(s) != 2 || s[0] != "Basic" {
return ""
}
b, err := base64.StdEncoding.DecodeString(s[1])
if err != nil {
return ""
}
pair := strings.SplitN(string(b), ":", 2)
if len(pair) != 2 {
return ""
}
passwd := a.Secrets(pair[0], a.Realm)
if passwd == "" {
return ""
}
if strings.HasPrefix(passwd, "{SHA}") {
d := sha1.New()
d.Write([]byte(pair[1]))
if passwd[5:] != base64.StdEncoding.EncodeToString(d.Sum(nil)) {
return ""
}
} else {
e := NewMD5Entry(passwd)
if e == nil {
return ""
}
if passwd != string(MD5Crypt([]byte(pair[1]), e.Salt, e.Magic)) {
return ""
}
}
return pair[0]
}
/*
http.Handler for BasicAuth which initiates the authentication process
(or requires reauthentication).
*/
func (a *BasicAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
w.Header().Set("WWW-Authenticate", `Basic realm="`+a.Realm+`"`)
w.WriteHeader(401)
w.Write([]byte("401 Unauthorized\n"))
}
/*
BasicAuthenticator returns a function, which wraps an
AuthenticatedHandlerFunc converting it to http.HandlerFunc. This
wrapper function checks the authentication and either sends back
required authentication headers, or calls the wrapped function with
authenticated username in the AuthenticatedRequest.
*/
func (a *BasicAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if username := a.CheckAuth(r); username == "" {
a.RequireAuth(w, r)
} else {
ar := &AuthenticatedRequest{Request: *r, Username: username}
wrapped(w, ar)
}
}
}
func NewBasicAuthenticator(realm string, secrets SecretProvider) *BasicAuth {
return &BasicAuth{Realm: realm, Secrets: secrets}
}

View File

@ -1,226 +0,0 @@
package auth
import (
"fmt"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"sync"
"time"
)
type digest_client struct {
nc uint64
last_seen int64
}
type DigestAuth struct {
Realm string
Opaque string
Secrets SecretProvider
PlainTextSecrets bool
/*
Approximate size of Client's Cache. When actual number of
tracked client nonces exceeds
ClientCacheSize+ClientCacheTolerance, ClientCacheTolerance*2
older entries are purged.
*/
ClientCacheSize int
ClientCacheTolerance int
clients map[string]*digest_client
mutex sync.Mutex
}
type digest_cache_entry struct {
nonce string
last_seen int64
}
type digest_cache []digest_cache_entry
func (c digest_cache) Less(i, j int) bool {
return c[i].last_seen < c[j].last_seen
}
func (c digest_cache) Len() int {
return len(c)
}
func (c digest_cache) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
/*
Remove count oldest entries from DigestAuth.clients
*/
func (a *DigestAuth) Purge(count int) {
entries := make([]digest_cache_entry, 0, len(a.clients))
for nonce, client := range a.clients {
entries = append(entries, digest_cache_entry{nonce, client.last_seen})
}
cache := digest_cache(entries)
sort.Sort(cache)
for _, client := range cache[:count] {
delete(a.clients, client.nonce)
}
}
/*
http.Handler for DigestAuth which initiates the authentication process
(or requires reauthentication).
*/
func (a *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
if len(a.clients) > a.ClientCacheSize+a.ClientCacheTolerance {
a.Purge(a.ClientCacheTolerance * 2)
}
nonce := RandomKey()
a.clients[nonce] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()}
w.Header().Set("WWW-Authenticate",
fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="MD5", qop="auth"`,
a.Realm, nonce, a.Opaque))
w.WriteHeader(401)
w.Write([]byte("401 Unauthorized\n"))
}
/*
Parse Authorization header from the http.Request. Returns a map of
auth parameters or nil if the header is not a valid parsable Digest
auth header.
*/
func DigestAuthParams(r *http.Request) map[string]string {
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
if len(s) != 2 || s[0] != "Digest" {
return nil
}
result := map[string]string{}
for _, kv := range strings.Split(s[1], ",") {
parts := strings.SplitN(kv, "=", 2)
if len(parts) != 2 {
continue
}
result[strings.Trim(parts[0], "\" ")] = strings.Trim(parts[1], "\" ")
}
return result
}
/*
Check if request contains valid authentication data. Returns a pair
of username, authinfo where username is the name of the authenticated
user or an empty string and authinfo is the contents for the optional
Authentication-Info response header.
*/
func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *string) {
da.mutex.Lock()
defer da.mutex.Unlock()
username = ""
authinfo = nil
auth := DigestAuthParams(r)
if auth == nil || da.Opaque != auth["opaque"] || auth["algorithm"] != "MD5" || auth["qop"] != "auth" {
return
}
// Check if the requested URI matches auth header
switch u, err := url.Parse(auth["uri"]); {
case err != nil:
return
case r.URL == nil:
return
case len(u.Path) > len(r.URL.Path):
return
case !strings.HasPrefix(r.URL.Path, u.Path):
return
}
HA1 := da.Secrets(auth["username"], da.Realm)
if da.PlainTextSecrets {
HA1 = H(auth["username"] + ":" + da.Realm + ":" + HA1)
}
HA2 := H(r.Method + ":" + auth["uri"])
KD := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], HA2}, ":"))
if KD != auth["response"] {
return
}
// At this point crypto checks are completed and validated.
// Now check if the session is valid.
nc, err := strconv.ParseUint(auth["nc"], 16, 64)
if err != nil {
return
}
if client, ok := da.clients[auth["nonce"]]; !ok {
return
} else {
if client.nc != 0 && client.nc >= nc {
return
}
client.nc = nc
client.last_seen = time.Now().UnixNano()
}
resp_HA2 := H(":" + auth["uri"])
rspauth := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], resp_HA2}, ":"))
info := fmt.Sprintf(`qop="auth", rspauth="%s", cnonce="%s", nc="%s"`, rspauth, auth["cnonce"], auth["nc"])
return auth["username"], &info
}
/*
Default values for ClientCacheSize and ClientCacheTolerance for DigestAuth
*/
const DefaultClientCacheSize = 1000
const DefaultClientCacheTolerance = 100
/*
Wrap returns an Authenticator which uses HTTP Digest
authentication. Arguments:
realm: The authentication realm.
secrets: SecretProvider which must return HA1 digests for the same
realm as above.
*/
func (a *DigestAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if username, authinfo := a.CheckAuth(r); username == "" {
a.RequireAuth(w, r)
} else {
ar := &AuthenticatedRequest{Request: *r, Username: username}
if authinfo != nil {
w.Header().Set("Authentication-Info", *authinfo)
}
wrapped(w, ar)
}
}
}
/*
JustCheck returns function which converts an http.HandlerFunc into a
http.HandlerFunc which requires authentication. Username is passed as
an extra X-Authenticated-Username header.
*/
func (a *DigestAuth) JustCheck(wrapped http.HandlerFunc) http.HandlerFunc {
return a.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
ar.Header.Set("X-Authenticated-Username", ar.Username)
wrapped(w, &ar.Request)
})
}
func NewDigestAuthenticator(realm string, secrets SecretProvider) *DigestAuth {
da := &DigestAuth{
Opaque: RandomKey(),
Realm: realm,
Secrets: secrets,
PlainTextSecrets: false,
ClientCacheSize: DefaultClientCacheSize,
ClientCacheTolerance: DefaultClientCacheTolerance,
clients: map[string]*digest_client{}}
return da
}

View File

@ -1,92 +0,0 @@
package auth
import "crypto/md5"
import "strings"
const itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
var md5_crypt_swaps = [16]int{12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11}
type MD5Entry struct {
Magic, Salt, Hash []byte
}
func NewMD5Entry(e string) *MD5Entry {
parts := strings.SplitN(e, "$", 4)
if len(parts) != 4 {
return nil
}
return &MD5Entry{
Magic: []byte("$" + parts[1] + "$"),
Salt: []byte(parts[2]),
Hash: []byte(parts[3]),
}
}
/*
MD5 password crypt implementation
*/
func MD5Crypt(password, salt, magic []byte) []byte {
d := md5.New()
d.Write(password)
d.Write(magic)
d.Write(salt)
d2 := md5.New()
d2.Write(password)
d2.Write(salt)
d2.Write(password)
for i, mixin := 0, d2.Sum(nil); i < len(password); i++ {
d.Write([]byte{mixin[i%16]})
}
for i := len(password); i != 0; i >>= 1 {
if i&1 == 0 {
d.Write([]byte{password[0]})
} else {
d.Write([]byte{0})
}
}
final := d.Sum(nil)
for i := 0; i < 1000; i++ {
d2 := md5.New()
if i&1 == 0 {
d2.Write(final)
} else {
d2.Write(password)
}
if i%3 != 0 {
d2.Write(salt)
}
if i%7 != 0 {
d2.Write(password)
}
if i&1 == 0 {
d2.Write(password)
} else {
d2.Write(final)
}
final = d2.Sum(nil)
}
result := make([]byte, 0, 22)
v := uint(0)
bits := uint(0)
for _, i := range md5_crypt_swaps {
v |= (uint(final[i]) << bits)
for bits = bits + 8; bits > 6; bits -= 6 {
result = append(result, itoa64[v&0x3f])
v >>= 6
}
}
result = append(result, itoa64[v&0x3f])
return append(append(append(magic, salt...), '$'), result...)
}

View File

@ -1,30 +0,0 @@
package auth
import "encoding/base64"
import "crypto/md5"
import "crypto/rand"
import "fmt"
/*
Return a random 16-byte base64 alphabet string
*/
func RandomKey() string {
k := make([]byte, 12)
for bytes := 0; bytes < len(k); {
n, err := rand.Read(k[bytes:])
if err != nil {
panic("rand.Read() failed")
}
bytes += n
}
return base64.StdEncoding.EncodeToString(k)
}
/*
H function for MD5 algorithm (returns a lower-case hex MD5 digest)
*/
func H(data string) string {
digest := md5.New()
digest.Write([]byte(data))
return fmt.Sprintf("%x", digest.Sum(nil))
}

View File

@ -1 +0,0 @@
test:example.com:aa78524fceb0e50fd8ca96dd818b8cf9

View File

@ -1,2 +0,0 @@
test:{SHA}qvTGHdzF6KLavt4PO0gs2a6pQ00=
test2:$apr1$a0j62R97$mYqFkloXH0/UOaUnAiV2b0

View File

@ -1,136 +0,0 @@
package auth
import "encoding/csv"
import "os"
/*
SecretProvider is used by authenticators. Takes user name and realm
as an argument, returns secret required for authentication (HA1 for
digest authentication, properly encrypted password for basic).
*/
type SecretProvider func(user, realm string) string
/*
Common functions for file auto-reloading
*/
type File struct {
Path string
Info os.FileInfo
/* must be set in inherited types during initialization */
Reload func()
}
func (f *File) ReloadIfNeeded() {
info, err := os.Stat(f.Path)
if err != nil {
panic(err)
}
if f.Info == nil || f.Info.ModTime() != info.ModTime() {
f.Info = info
f.Reload()
}
}
/*
Structure used for htdigest file authentication. Users map realms to
maps of users to their HA1 digests.
*/
type HtdigestFile struct {
File
Users map[string]map[string]string
}
func reload_htdigest(hf *HtdigestFile) {
r, err := os.Open(hf.Path)
if err != nil {
panic(err)
}
csv_reader := csv.NewReader(r)
csv_reader.Comma = ':'
csv_reader.Comment = '#'
csv_reader.TrimLeadingSpace = true
records, err := csv_reader.ReadAll()
if err != nil {
panic(err)
}
hf.Users = make(map[string]map[string]string)
for _, record := range records {
_, exists := hf.Users[record[1]]
if !exists {
hf.Users[record[1]] = make(map[string]string)
}
hf.Users[record[1]][record[0]] = record[2]
}
}
/*
SecretProvider implementation based on htdigest-formated files. Will
reload htdigest file on changes. Will panic on syntax errors in
htdigest files.
*/
func HtdigestFileProvider(filename string) SecretProvider {
hf := &HtdigestFile{File: File{Path: filename}}
hf.Reload = func() { reload_htdigest(hf) }
return func(user, realm string) string {
hf.ReloadIfNeeded()
_, exists := hf.Users[realm]
if !exists {
return ""
}
digest, exists := hf.Users[realm][user]
if !exists {
return ""
}
return digest
}
}
/*
Structure used for htdigest file authentication. Users map users to
their salted encrypted password
*/
type HtpasswdFile struct {
File
Users map[string]string
}
func reload_htpasswd(h *HtpasswdFile) {
r, err := os.Open(h.Path)
if err != nil {
panic(err)
}
csv_reader := csv.NewReader(r)
csv_reader.Comma = ':'
csv_reader.Comment = '#'
csv_reader.TrimLeadingSpace = true
records, err := csv_reader.ReadAll()
if err != nil {
panic(err)
}
h.Users = make(map[string]string)
for _, record := range records {
h.Users[record[0]] = record[1]
}
}
/*
SecretProvider implementation based on htpasswd-formated files. Will
reload htpasswd file on changes. Will panic on syntax errors in
htpasswd files. Realm argument of the SecretProvider is ignored.
*/
func HtpasswdFileProvider(filename string) SecretProvider {
h := &HtpasswdFile{File: File{Path: filename}}
h.Reload = func() { reload_htpasswd(h) }
return func(user, realm string) string {
h.ReloadIfNeeded()
password, exists := h.Users[user]
if !exists {
return ""
}
return password
}
}

View File

@ -1,34 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"handler.go",
"versions.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/google/cadvisor/api",
importpath = "github.com/google/cadvisor/api",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/google/cadvisor/events:go_default_library",
"//vendor/github.com/google/cadvisor/http/mux:go_default_library",
"//vendor/github.com/google/cadvisor/info/v1:go_default_library",
"//vendor/github.com/google/cadvisor/info/v2:go_default_library",
"//vendor/github.com/google/cadvisor/manager:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,250 +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.
// Package api provides a handler for /api/
package api
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"path"
"regexp"
"sort"
"strconv"
"strings"
"time"
"github.com/google/cadvisor/events"
httpmux "github.com/google/cadvisor/http/mux"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/manager"
"github.com/golang/glog"
)
const (
apiResource = "/api/"
)
func RegisterHandlers(mux httpmux.Mux, m manager.Manager) error {
apiVersions := getApiVersions()
supportedApiVersions := make(map[string]ApiVersion, len(apiVersions))
for _, v := range apiVersions {
supportedApiVersions[v.Version()] = v
}
mux.HandleFunc(apiResource, func(w http.ResponseWriter, r *http.Request) {
err := handleRequest(supportedApiVersions, m, w, r)
if err != nil {
http.Error(w, err.Error(), 500)
}
})
return nil
}
// Captures the API version, requestType [optional], and remaining request [optional].
var apiRegexp = regexp.MustCompile(`/api/([^/]+)/?([^/]+)?(.*)`)
const (
apiVersion = iota + 1
apiRequestType
apiRequestArgs
)
func handleRequest(supportedApiVersions map[string]ApiVersion, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
start := time.Now()
defer func() {
glog.V(4).Infof("Request took %s", time.Since(start))
}()
request := r.URL.Path
const apiPrefix = "/api"
if !strings.HasPrefix(request, apiPrefix) {
return fmt.Errorf("incomplete API request %q", request)
}
// If the request doesn't have an API version, list those.
if request == apiPrefix || request == apiResource {
versions := make([]string, 0, len(supportedApiVersions))
for v := range supportedApiVersions {
versions = append(versions, v)
}
sort.Strings(versions)
http.Error(w, fmt.Sprintf("Supported API versions: %s", strings.Join(versions, ",")), http.StatusBadRequest)
return nil
}
// Verify that we have all the elements we expect:
// /<version>/<request type>[/<args...>]
requestElements := apiRegexp.FindStringSubmatch(request)
if len(requestElements) == 0 {
return fmt.Errorf("malformed request %q", request)
}
version := requestElements[apiVersion]
requestType := requestElements[apiRequestType]
requestArgs := strings.Split(requestElements[apiRequestArgs], "/")
// Check supported versions.
versionHandler, ok := supportedApiVersions[version]
if !ok {
return fmt.Errorf("unsupported API version %q", version)
}
// If no request type, list possible request types.
if requestType == "" {
requestTypes := versionHandler.SupportedRequestTypes()
sort.Strings(requestTypes)
http.Error(w, fmt.Sprintf("Supported request types: %q", strings.Join(requestTypes, ",")), http.StatusBadRequest)
return nil
}
// Trim the first empty element from the request.
if len(requestArgs) > 0 && requestArgs[0] == "" {
requestArgs = requestArgs[1:]
}
return versionHandler.HandleRequest(requestType, requestArgs, m, w, r)
}
func writeResult(res interface{}, w http.ResponseWriter) error {
out, err := json.Marshal(res)
if err != nil {
return fmt.Errorf("failed to marshall response %+v with error: %s", res, err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(out)
return nil
}
func streamResults(eventChannel *events.EventChannel, w http.ResponseWriter, r *http.Request, m manager.Manager) error {
cn, ok := w.(http.CloseNotifier)
if !ok {
return errors.New("could not access http.CloseNotifier")
}
flusher, ok := w.(http.Flusher)
if !ok {
return errors.New("could not access http.Flusher")
}
w.Header().Set("Transfer-Encoding", "chunked")
w.WriteHeader(http.StatusOK)
flusher.Flush()
enc := json.NewEncoder(w)
for {
select {
case <-cn.CloseNotify():
m.CloseEventChannel(eventChannel.GetWatchId())
return nil
case ev := <-eventChannel.GetChannel():
err := enc.Encode(ev)
if err != nil {
glog.Errorf("error encoding message %+v for result stream: %v", ev, err)
}
flusher.Flush()
}
}
}
func getContainerInfoRequest(body io.ReadCloser) (*info.ContainerInfoRequest, error) {
query := info.DefaultContainerInfoRequest()
decoder := json.NewDecoder(body)
err := decoder.Decode(&query)
if err != nil && err != io.EOF {
return nil, fmt.Errorf("unable to decode the json value: %s", err)
}
return &query, nil
}
// The user can set any or none of the following arguments in any order
// with any twice defined arguments being assigned the first value.
// If the value type for the argument is wrong the field will be assumed to be
// unassigned
// bools: stream, subcontainers, oom_events, creation_events, deletion_events
// ints: max_events, start_time (unix timestamp), end_time (unix timestamp)
// example r.URL: http://localhost:8080/api/v1.3/events?oom_events=true&stream=true
func getEventRequest(r *http.Request) (*events.Request, bool, error) {
query := events.NewRequest()
stream := false
urlMap := r.URL.Query()
if val, ok := urlMap["stream"]; ok {
newBool, err := strconv.ParseBool(val[0])
if err == nil {
stream = newBool
}
}
if val, ok := urlMap["subcontainers"]; ok {
newBool, err := strconv.ParseBool(val[0])
if err == nil {
query.IncludeSubcontainers = newBool
}
}
eventTypes := map[string]info.EventType{
"oom_events": info.EventOom,
"oom_kill_events": info.EventOomKill,
"creation_events": info.EventContainerCreation,
"deletion_events": info.EventContainerDeletion,
}
allEventTypes := false
if val, ok := urlMap["all_events"]; ok {
newBool, err := strconv.ParseBool(val[0])
if err == nil {
allEventTypes = newBool
}
}
for opt, eventType := range eventTypes {
if allEventTypes {
query.EventType[eventType] = true
} else if val, ok := urlMap[opt]; ok {
newBool, err := strconv.ParseBool(val[0])
if err == nil {
query.EventType[eventType] = newBool
}
}
}
if val, ok := urlMap["max_events"]; ok {
newInt, err := strconv.Atoi(val[0])
if err == nil {
query.MaxEventsReturned = int(newInt)
}
}
if val, ok := urlMap["start_time"]; ok {
newTime, err := time.Parse(time.RFC3339, val[0])
if err == nil {
query.StartTime = newTime
}
}
if val, ok := urlMap["end_time"]; ok {
newTime, err := time.Parse(time.RFC3339, val[0])
if err == nil {
query.EndTime = newTime
}
}
return query, stream, nil
}
func getContainerName(request []string) string {
return path.Join("/", strings.Join(request, "/"))
}

View File

@ -1,559 +0,0 @@
// 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.
package api
import (
"fmt"
"net/http"
"path"
"strconv"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/info/v2"
"github.com/google/cadvisor/manager"
"github.com/golang/glog"
)
const (
containersApi = "containers"
subcontainersApi = "subcontainers"
machineApi = "machine"
machineStatsApi = "machinestats"
dockerApi = "docker"
summaryApi = "summary"
statsApi = "stats"
specApi = "spec"
eventsApi = "events"
storageApi = "storage"
attributesApi = "attributes"
versionApi = "version"
psApi = "ps"
customMetricsApi = "appmetrics"
)
// Interface for a cAdvisor API version
type ApiVersion interface {
// Returns the version string.
Version() string
// List of supported API endpoints.
SupportedRequestTypes() []string
// Handles a request. The second argument is the parameters after /api/<version>/<endpoint>
HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error
}
// Gets all supported API versions.
func getApiVersions() []ApiVersion {
v1_0 := &version1_0{}
v1_1 := newVersion1_1(v1_0)
v1_2 := newVersion1_2(v1_1)
v1_3 := newVersion1_3(v1_2)
v2_0 := newVersion2_0()
v2_1 := newVersion2_1(v2_0)
return []ApiVersion{v1_0, v1_1, v1_2, v1_3, v2_0, v2_1}
}
// API v1.0
type version1_0 struct {
}
func (self *version1_0) Version() string {
return "v1.0"
}
func (self *version1_0) SupportedRequestTypes() []string {
return []string{containersApi, machineApi}
}
func (self *version1_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
switch requestType {
case machineApi:
glog.V(4).Infof("Api - Machine")
// Get the MachineInfo
machineInfo, err := m.GetMachineInfo()
if err != nil {
return err
}
err = writeResult(machineInfo, w)
if err != nil {
return err
}
case containersApi:
containerName := getContainerName(request)
glog.V(4).Infof("Api - Container(%s)", containerName)
// Get the query request.
query, err := getContainerInfoRequest(r.Body)
if err != nil {
return err
}
// Get the container.
cont, err := m.GetContainerInfo(containerName, query)
if err != nil {
return fmt.Errorf("failed to get container %q with error: %s", containerName, err)
}
// Only output the container as JSON.
err = writeResult(cont, w)
if err != nil {
return err
}
default:
return fmt.Errorf("unknown request type %q", requestType)
}
return nil
}
// API v1.1
type version1_1 struct {
baseVersion *version1_0
}
// v1.1 builds on v1.0.
func newVersion1_1(v *version1_0) *version1_1 {
return &version1_1{
baseVersion: v,
}
}
func (self *version1_1) Version() string {
return "v1.1"
}
func (self *version1_1) SupportedRequestTypes() []string {
return append(self.baseVersion.SupportedRequestTypes(), subcontainersApi)
}
func (self *version1_1) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
switch requestType {
case subcontainersApi:
containerName := getContainerName(request)
glog.V(4).Infof("Api - Subcontainers(%s)", containerName)
// Get the query request.
query, err := getContainerInfoRequest(r.Body)
if err != nil {
return err
}
// Get the subcontainers.
containers, err := m.SubcontainersInfo(containerName, query)
if err != nil {
return fmt.Errorf("failed to get subcontainers for container %q with error: %s", containerName, err)
}
// Only output the containers as JSON.
err = writeResult(containers, w)
if err != nil {
return err
}
return nil
default:
return self.baseVersion.HandleRequest(requestType, request, m, w, r)
}
}
// API v1.2
type version1_2 struct {
baseVersion *version1_1
}
// v1.2 builds on v1.1.
func newVersion1_2(v *version1_1) *version1_2 {
return &version1_2{
baseVersion: v,
}
}
func (self *version1_2) Version() string {
return "v1.2"
}
func (self *version1_2) SupportedRequestTypes() []string {
return append(self.baseVersion.SupportedRequestTypes(), dockerApi)
}
func (self *version1_2) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
switch requestType {
case dockerApi:
glog.V(4).Infof("Api - Docker(%v)", request)
// Get the query request.
query, err := getContainerInfoRequest(r.Body)
if err != nil {
return err
}
var containers map[string]info.ContainerInfo
// map requests for "docker/" to "docker"
if len(request) == 1 && len(request[0]) == 0 {
request = request[:0]
}
switch len(request) {
case 0:
// Get all Docker containers.
containers, err = m.AllDockerContainers(query)
if err != nil {
return fmt.Errorf("failed to get all Docker containers with error: %v", err)
}
case 1:
// Get one Docker container.
var cont info.ContainerInfo
cont, err = m.DockerContainer(request[0], query)
if err != nil {
return fmt.Errorf("failed to get Docker container %q with error: %v", request[0], err)
}
containers = map[string]info.ContainerInfo{
cont.Name: cont,
}
default:
return fmt.Errorf("unknown request for Docker container %v", request)
}
// Only output the containers as JSON.
err = writeResult(containers, w)
if err != nil {
return err
}
return nil
default:
return self.baseVersion.HandleRequest(requestType, request, m, w, r)
}
}
// API v1.3
type version1_3 struct {
baseVersion *version1_2
}
// v1.3 builds on v1.2.
func newVersion1_3(v *version1_2) *version1_3 {
return &version1_3{
baseVersion: v,
}
}
func (self *version1_3) Version() string {
return "v1.3"
}
func (self *version1_3) SupportedRequestTypes() []string {
return append(self.baseVersion.SupportedRequestTypes(), eventsApi)
}
func (self *version1_3) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
switch requestType {
case eventsApi:
return handleEventRequest(request, m, w, r)
default:
return self.baseVersion.HandleRequest(requestType, request, m, w, r)
}
}
func handleEventRequest(request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
query, stream, err := getEventRequest(r)
if err != nil {
return err
}
query.ContainerName = path.Join("/", getContainerName(request))
glog.V(4).Infof("Api - Events(%v)", query)
if !stream {
pastEvents, err := m.GetPastEvents(query)
if err != nil {
return err
}
return writeResult(pastEvents, w)
}
eventChannel, err := m.WatchForEvents(query)
if err != nil {
return err
}
return streamResults(eventChannel, w, r, m)
}
// API v2.0
type version2_0 struct {
}
func newVersion2_0() *version2_0 {
return &version2_0{}
}
func (self *version2_0) Version() string {
return "v2.0"
}
func (self *version2_0) SupportedRequestTypes() []string {
return []string{versionApi, attributesApi, eventsApi, machineApi, summaryApi, statsApi, specApi, storageApi, psApi, customMetricsApi}
}
func (self *version2_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
opt, err := getRequestOptions(r)
if err != nil {
return err
}
switch requestType {
case versionApi:
glog.V(4).Infof("Api - Version")
versionInfo, err := m.GetVersionInfo()
if err != nil {
return err
}
return writeResult(versionInfo.CadvisorVersion, w)
case attributesApi:
glog.V(4).Info("Api - Attributes")
machineInfo, err := m.GetMachineInfo()
if err != nil {
return err
}
versionInfo, err := m.GetVersionInfo()
if err != nil {
return err
}
info := v2.GetAttributes(machineInfo, versionInfo)
return writeResult(info, w)
case machineApi:
glog.V(4).Info("Api - Machine")
// TODO(rjnagal): Move machineInfo from v1.
machineInfo, err := m.GetMachineInfo()
if err != nil {
return err
}
return writeResult(machineInfo, w)
case summaryApi:
containerName := getContainerName(request)
glog.V(4).Infof("Api - Summary for container %q, options %+v", containerName, opt)
stats, err := m.GetDerivedStats(containerName, opt)
if err != nil {
return err
}
return writeResult(stats, w)
case statsApi:
name := getContainerName(request)
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 {
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 {
contStats[name] = v2.DeprecatedStatsFromV1(cinfo)
}
return writeResult(contStats, w)
case customMetricsApi:
containerName := getContainerName(request)
glog.V(4).Infof("Api - Custom Metrics: Looking for metrics for container %q, options %+v", containerName, opt)
infos, err := m.GetContainerInfoV2(containerName, opt)
if err != nil {
return err
}
contMetrics := make(map[string]map[string]map[string][]info.MetricValBasic, 0)
for _, cinfo := range infos {
metrics := make(map[string]map[string][]info.MetricValBasic, 0)
for _, contStat := range cinfo.Stats {
if len(contStat.CustomMetrics) == 0 {
continue
}
for name, allLabels := range contStat.CustomMetrics {
metricLabels := make(map[string][]info.MetricValBasic, 0)
for _, metric := range allLabels {
if !metric.Timestamp.IsZero() {
metVal := info.MetricValBasic{
Timestamp: metric.Timestamp,
IntValue: metric.IntValue,
FloatValue: metric.FloatValue,
}
labels := metrics[name]
if labels != nil {
values := labels[metric.Label]
values = append(values, metVal)
labels[metric.Label] = values
metrics[name] = labels
} else {
metricLabels[metric.Label] = []info.MetricValBasic{metVal}
metrics[name] = metricLabels
}
}
}
}
}
contMetrics[containerName] = metrics
}
return writeResult(contMetrics, w)
case specApi:
containerName := getContainerName(request)
glog.V(4).Infof("Api - Spec for container %q, options %+v", containerName, opt)
specs, err := m.GetContainerSpec(containerName, opt)
if err != nil {
return err
}
return writeResult(specs, w)
case storageApi:
label := r.URL.Query().Get("label")
uuid := r.URL.Query().Get("uuid")
switch {
case uuid != "":
fi, err := m.GetFsInfoByFsUUID(uuid)
if err != nil {
return err
}
return writeResult(fi, w)
case label != "":
// Get a specific label.
fi, err := m.GetFsInfo(label)
if err != nil {
return err
}
return writeResult(fi, w)
default:
// Get all global filesystems info.
fi, err := m.GetFsInfo("")
if err != nil {
return err
}
return writeResult(fi, w)
}
case eventsApi:
return handleEventRequest(request, m, w, r)
case psApi:
// reuse container type from request.
// ignore recursive.
// TODO(rjnagal): consider count to limit ps output.
name := getContainerName(request)
glog.V(4).Infof("Api - Spec for container %q, options %+v", name, opt)
ps, err := m.GetProcessList(name, opt)
if err != nil {
return fmt.Errorf("process listing failed: %v", err)
}
return writeResult(ps, w)
default:
return fmt.Errorf("unknown request type %q", requestType)
}
}
type version2_1 struct {
baseVersion *version2_0
}
func newVersion2_1(v *version2_0) *version2_1 {
return &version2_1{
baseVersion: v,
}
}
func (self *version2_1) Version() string {
return "v2.1"
}
func (self *version2_1) SupportedRequestTypes() []string {
return append([]string{machineStatsApi}, self.baseVersion.SupportedRequestTypes()...)
}
func (self *version2_1) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
// Get the query request.
opt, err := getRequestOptions(r)
if err != nil {
return err
}
switch requestType {
case machineStatsApi:
glog.V(4).Infof("Api - MachineStats(%v)", request)
cont, err := m.GetRequestedContainersInfo("/", opt)
if err != nil {
if len(cont) == 0 {
return err
}
glog.Errorf("Error calling GetRequestedContainersInfo: %v", err)
}
return writeResult(v2.MachineStatsFromV1(cont["/"]), w)
case statsApi:
name := getContainerName(request)
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 {
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 {
if name == "/" {
// Root cgroup stats should be exposed as machine stats
continue
}
contStats[name] = v2.ContainerInfo{
Spec: v2.ContainerSpecFromV1(&cont.Spec, cont.Aliases, cont.Namespace),
Stats: v2.ContainerStatsFromV1(name, &cont.Spec, cont.Stats),
}
}
return writeResult(contStats, w)
default:
return self.baseVersion.HandleRequest(requestType, request, m, w, r)
}
}
func getRequestOptions(r *http.Request) (v2.RequestOptions, error) {
supportedTypes := map[string]bool{
v2.TypeName: true,
v2.TypeDocker: true,
}
// fill in the defaults.
opt := v2.RequestOptions{
IdType: v2.TypeName,
Count: 64,
Recursive: false,
}
idType := r.URL.Query().Get("type")
if len(idType) != 0 {
if !supportedTypes[idType] {
return opt, fmt.Errorf("unknown 'type' %q", idType)
}
opt.IdType = idType
}
count := r.URL.Query().Get("count")
if len(count) != 0 {
n, err := strconv.ParseUint(count, 10, 32)
if err != nil {
return opt, fmt.Errorf("failed to parse 'count' option: %v", count)
}
opt.Count = int(n)
}
recursive := r.URL.Query().Get("recursive")
if recursive == "true" {
opt.Recursive = true
}
return opt, nil
}

View File

@ -1,24 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["healthz.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/google/cadvisor/healthz",
importpath = "github.com/google/cadvisor/healthz",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/google/cadvisor/http/mux:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,32 +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.
package healthz
import (
"net/http"
httpmux "github.com/google/cadvisor/http/mux"
)
func handleHealthz(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
}
// Register simple HTTP /healthz handler to return "ok".
func RegisterHandler(mux httpmux.Mux) error {
mux.HandleFunc("/healthz", handleHealthz)
return nil
}

View File

@ -1,40 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["handlers.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/google/cadvisor/http",
importpath = "github.com/google/cadvisor/http",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/abbot/go-http-auth:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/google/cadvisor/api:go_default_library",
"//vendor/github.com/google/cadvisor/healthz:go_default_library",
"//vendor/github.com/google/cadvisor/http/mux:go_default_library",
"//vendor/github.com/google/cadvisor/manager:go_default_library",
"//vendor/github.com/google/cadvisor/metrics:go_default_library",
"//vendor/github.com/google/cadvisor/pages:go_default_library",
"//vendor/github.com/google/cadvisor/pages/static:go_default_library",
"//vendor/github.com/google/cadvisor/validate:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus/promhttp:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/google/cadvisor/http/mux:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,112 +0,0 @@
// 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.
package http
import (
"fmt"
"net/http"
"os"
"github.com/google/cadvisor/api"
"github.com/google/cadvisor/healthz"
httpmux "github.com/google/cadvisor/http/mux"
"github.com/google/cadvisor/manager"
"github.com/google/cadvisor/metrics"
"github.com/google/cadvisor/pages"
"github.com/google/cadvisor/pages/static"
"github.com/google/cadvisor/validate"
auth "github.com/abbot/go-http-auth"
"github.com/golang/glog"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func RegisterHandlers(mux httpmux.Mux, containerManager manager.Manager, httpAuthFile, httpAuthRealm, httpDigestFile, httpDigestRealm string) error {
// Basic health handler.
if err := healthz.RegisterHandler(mux); err != nil {
return fmt.Errorf("failed to register healthz handler: %s", err)
}
// Validation/Debug handler.
mux.HandleFunc(validate.ValidatePage, func(w http.ResponseWriter, r *http.Request) {
err := validate.HandleRequest(w, containerManager)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
// Register API handler.
if err := api.RegisterHandlers(mux, containerManager); err != nil {
return fmt.Errorf("failed to register API handlers: %s", err)
}
// Redirect / to containers page.
mux.Handle("/", http.RedirectHandler(pages.ContainersPage, http.StatusTemporaryRedirect))
var authenticated bool
// Setup the authenticator object
if httpAuthFile != "" {
glog.V(1).Infof("Using auth file %s", httpAuthFile)
secrets := auth.HtpasswdFileProvider(httpAuthFile)
authenticator := auth.NewBasicAuthenticator(httpAuthRealm, secrets)
mux.HandleFunc(static.StaticResource, authenticator.Wrap(staticHandler))
if err := pages.RegisterHandlersBasic(mux, containerManager, authenticator); err != nil {
return fmt.Errorf("failed to register pages auth handlers: %s", err)
}
authenticated = true
}
if httpAuthFile == "" && httpDigestFile != "" {
glog.V(1).Infof("Using digest file %s", httpDigestFile)
secrets := auth.HtdigestFileProvider(httpDigestFile)
authenticator := auth.NewDigestAuthenticator(httpDigestRealm, secrets)
mux.HandleFunc(static.StaticResource, authenticator.Wrap(staticHandler))
if err := pages.RegisterHandlersDigest(mux, containerManager, authenticator); err != nil {
return fmt.Errorf("failed to register pages digest handlers: %s", err)
}
authenticated = true
}
// Change handler based on authenticator initalization
if !authenticated {
mux.HandleFunc(static.StaticResource, staticHandlerNoAuth)
if err := pages.RegisterHandlersBasic(mux, containerManager, nil); err != nil {
return fmt.Errorf("failed to register pages handlers: %s", err)
}
}
return nil
}
// RegisterPrometheusHandler creates a new PrometheusCollector and configures
// the provided HTTP mux to handle the given Prometheus endpoint.
func RegisterPrometheusHandler(mux httpmux.Mux, containerManager manager.Manager, prometheusEndpoint string, f metrics.ContainerLabelsFunc) {
r := prometheus.NewRegistry()
r.MustRegister(
metrics.NewPrometheusCollector(containerManager, f),
prometheus.NewGoCollector(),
prometheus.NewProcessCollector(os.Getpid(), ""),
)
mux.Handle(prometheusEndpoint, promhttp.HandlerFor(r, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError}))
}
func staticHandlerNoAuth(w http.ResponseWriter, r *http.Request) {
static.HandleRequest(w, r.URL)
}
func staticHandler(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
static.HandleRequest(w, r.URL)
}

View File

@ -1,23 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["mux.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/google/cadvisor/http/mux",
importpath = "github.com/google/cadvisor/http/mux",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,26 +0,0 @@
// 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.
package mux
import (
"net/http"
)
// Mux interface expected by cAdvisor components.
type Mux interface {
HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
Handler(r *http.Request) (http.Handler, string)
Handle(pattern string, handler http.Handler)
}

View File

@ -1,39 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"containers.go",
"docker.go",
"pages.go",
"templates.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/google/cadvisor/pages",
importpath = "github.com/google/cadvisor/pages",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/abbot/go-http-auth:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/google/cadvisor/container/docker:go_default_library",
"//vendor/github.com/google/cadvisor/http/mux:go_default_library",
"//vendor/github.com/google/cadvisor/info/v1:go_default_library",
"//vendor/github.com/google/cadvisor/manager:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/google/cadvisor/pages/static:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,253 +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.
// Page for /containers/
package pages
import (
"fmt"
"html/template"
"net/http"
"net/url"
"path"
"strconv"
"strings"
"time"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/manager"
"github.com/golang/glog"
)
const ContainersPage = "/containers/"
// from http://golang.org/doc/effective_go.html#constants
type ByteSize float64
const (
_ = iota
// KB - kilobyte
KB ByteSize = 1 << (10 * iota)
// MB - megabyte
MB
// GB - gigabyte
GB
// TB - terabyte
TB
// PB - petabyte
PB
// EB - exabyte
EB
// ZB - zettabyte
ZB
// YB - yottabyte
YB
)
func (b ByteSize) Size() string {
for _, i := range [...]ByteSize{YB, ZB, EB, PB, TB, GB, MB, KB} {
if b >= i {
return fmt.Sprintf("%.2f", b/i)
}
}
return fmt.Sprintf("%.2f", b)
}
func (b ByteSize) Unit() string {
switch {
case b >= YB:
return "YB"
case b >= ZB:
return "ZB"
case b >= EB:
return "EB"
case b >= PB:
return "PB"
case b >= TB:
return "TB"
case b >= GB:
return "GB"
case b >= MB:
return "MB"
case b >= KB:
return "KB"
}
return "B"
}
var funcMap = template.FuncMap{
"printMask": printMask,
"printCores": printCores,
"printShares": printShares,
"printSize": printSize,
"printUnit": printUnit,
}
func printMask(mask string, numCores int) interface{} {
masks := make([]string, numCores)
activeCores := getActiveCores(mask)
for i := 0; i < numCores; i++ {
coreClass := "inactive-cpu"
if activeCores[i] {
coreClass = "active-cpu"
}
masks[i] = fmt.Sprintf("<span class=\"%s\">%d</span>", coreClass, i)
}
return template.HTML(strings.Join(masks, "&nbsp;"))
}
func getActiveCores(mask string) map[int]bool {
activeCores := make(map[int]bool)
for _, corebits := range strings.Split(mask, ",") {
cores := strings.Split(corebits, "-")
if len(cores) == 1 {
index, err := strconv.Atoi(cores[0])
if err != nil {
// Ignore malformed strings.
continue
}
activeCores[index] = true
} else if len(cores) == 2 {
start, err := strconv.Atoi(cores[0])
if err != nil {
continue
}
end, err := strconv.Atoi(cores[1])
if err != nil {
continue
}
for i := start; i <= end; i++ {
activeCores[i] = true
}
}
}
return activeCores
}
func printCores(millicores *uint64) string {
cores := float64(*millicores) / 1000
return strconv.FormatFloat(cores, 'f', 3, 64)
}
func printShares(shares *uint64) string {
return fmt.Sprintf("%d", *shares)
}
// Size after which we consider memory to be "unlimited". This is not
// MaxInt64 due to rounding by the kernel.
const maxMemorySize = uint64(1 << 62)
func printSize(bytes uint64) string {
if bytes >= maxMemorySize {
return "unlimited"
}
return ByteSize(bytes).Size()
}
func printUnit(bytes uint64) string {
if bytes >= maxMemorySize {
return ""
}
return ByteSize(bytes).Unit()
}
func serveContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) {
start := time.Now()
// The container name is the path after the handler
containerName := u.Path[len(ContainersPage)-1:]
// Get the container.
reqParams := info.ContainerInfoRequest{
NumStats: 60,
}
cont, err := m.GetContainerInfo(containerName, &reqParams)
if err != nil {
http.Error(w, fmt.Sprintf("failed to get container %q with error: %v", containerName, err), http.StatusNotFound)
return
}
displayName := getContainerDisplayName(cont.ContainerReference)
// Get the MachineInfo
machineInfo, err := m.GetMachineInfo()
if err != nil {
http.Error(w, fmt.Sprintf("failed to get machine info: %v", err), http.StatusInternalServerError)
return
}
rootDir := getRootDir(containerName)
// Make a list of the parent containers and their links
pathParts := strings.Split(string(cont.Name), "/")
parentContainers := make([]link, 0, len(pathParts))
parentContainers = append(parentContainers, link{
Text: "root",
Link: path.Join(rootDir, ContainersPage),
})
for i := 1; i < len(pathParts); i++ {
// Skip empty parts.
if pathParts[i] == "" {
continue
}
parentContainers = append(parentContainers, link{
Text: pathParts[i],
Link: path.Join(rootDir, ContainersPage, path.Join(pathParts[1:i+1]...)),
})
}
// Build the links for the subcontainers.
subcontainerLinks := make([]link, 0, len(cont.Subcontainers))
for _, sub := range cont.Subcontainers {
if !m.Exists(sub.Name) {
continue
}
subcontainerLinks = append(subcontainerLinks, link{
Text: getContainerDisplayName(sub),
Link: path.Join(rootDir, ContainersPage, sub.Name),
})
}
data := &pageData{
DisplayName: displayName,
ContainerName: escapeContainerName(cont.Name),
ParentContainers: parentContainers,
Subcontainers: subcontainerLinks,
Spec: cont.Spec,
Stats: cont.Stats,
MachineInfo: machineInfo,
IsRoot: cont.Name == "/",
ResourcesAvailable: cont.Spec.HasCpu || cont.Spec.HasMemory || cont.Spec.HasNetwork || cont.Spec.HasFilesystem,
CpuAvailable: cont.Spec.HasCpu,
MemoryAvailable: cont.Spec.HasMemory,
NetworkAvailable: cont.Spec.HasNetwork,
FsAvailable: cont.Spec.HasFilesystem,
CustomMetricsAvailable: cont.Spec.HasCustomMetrics,
SubcontainersAvailable: len(subcontainerLinks) > 0,
Root: rootDir,
}
err = pageTemplate.Execute(w, data)
if err != nil {
glog.Errorf("Failed to apply template: %s", err)
}
glog.V(5).Infof("Request took %s", time.Since(start))
}
// Build a relative path to the root of the container page.
func getRootDir(containerName string) string {
// The root is at: container depth
levels := (strings.Count(containerName, "/"))
return strings.Repeat("../", levels)
}

View File

@ -1,162 +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.
package pages
import (
"fmt"
"net/http"
"net/url"
"path"
"strconv"
"time"
"github.com/google/cadvisor/container/docker"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/manager"
"github.com/golang/glog"
)
const DockerPage = "/docker/"
func toStatusKV(status info.DockerStatus) ([]keyVal, []keyVal) {
ds := []keyVal{
{Key: "Driver", Value: status.Driver},
}
for k, v := range status.DriverStatus {
ds = append(ds, keyVal{Key: k, Value: v})
}
return []keyVal{
{Key: "Docker Version", Value: status.Version},
{Key: "Docker API Version", Value: status.APIVersion},
{Key: "Kernel Version", Value: status.KernelVersion},
{Key: "OS Version", Value: status.OS},
{Key: "Host Name", Value: status.Hostname},
{Key: "Docker Root Directory", Value: status.RootDir},
{Key: "Execution Driver", Value: status.ExecDriver},
{Key: "Number of Images", Value: strconv.Itoa(status.NumImages)},
{Key: "Number of Containers", Value: strconv.Itoa(status.NumContainers)},
}, ds
}
func serveDockerPage(m manager.Manager, w http.ResponseWriter, u *url.URL) {
start := time.Now()
// The container name is the path after the handler
containerName := u.Path[len(DockerPage)-1:]
rootDir := getRootDir(containerName)
var data *pageData
if containerName == "/" {
// Get the containers.
reqParams := info.ContainerInfoRequest{
NumStats: 0,
}
conts, err := m.AllDockerContainers(&reqParams)
if err != nil {
http.Error(w, fmt.Sprintf("failed to get container %q with error: %v", containerName, err), http.StatusNotFound)
return
}
subcontainers := make([]link, 0, len(conts))
for _, cont := range conts {
subcontainers = append(subcontainers, link{
Text: getContainerDisplayName(cont.ContainerReference),
Link: path.Join(rootDir, DockerPage, docker.ContainerNameToDockerId(cont.ContainerReference.Name)),
})
}
// Get Docker status
status, err := m.DockerInfo()
if err != nil {
http.Error(w, fmt.Sprintf("failed to get docker info: %v", err), http.StatusInternalServerError)
return
}
dockerStatus, driverStatus := toStatusKV(status)
// Get Docker Images
images, err := m.DockerImages()
if err != nil {
http.Error(w, fmt.Sprintf("failed to get docker images: %v", err), http.StatusInternalServerError)
return
}
dockerContainersText := "Docker Containers"
data = &pageData{
DisplayName: dockerContainersText,
ParentContainers: []link{
{
Text: dockerContainersText,
Link: path.Join(rootDir, DockerPage),
}},
Subcontainers: subcontainers,
Root: rootDir,
DockerStatus: dockerStatus,
DockerDriverStatus: driverStatus,
DockerImages: images,
}
} else {
// Get the container.
reqParams := info.ContainerInfoRequest{
NumStats: 60,
}
cont, err := m.DockerContainer(containerName[1:], &reqParams)
if err != nil {
http.Error(w, fmt.Sprintf("failed to get container %q with error: %v", containerName, err), http.StatusNotFound)
return
}
displayName := getContainerDisplayName(cont.ContainerReference)
// Make a list of the parent containers and their links
var parentContainers []link
parentContainers = append(parentContainers, link{
Text: "Docker Containers",
Link: path.Join(rootDir, DockerPage),
})
parentContainers = append(parentContainers, link{
Text: displayName,
Link: path.Join(rootDir, DockerPage, docker.ContainerNameToDockerId(cont.Name)),
})
// Get the MachineInfo
machineInfo, err := m.GetMachineInfo()
if err != nil {
http.Error(w, fmt.Sprintf("failed to get machine info: %v", err), http.StatusInternalServerError)
return
}
data = &pageData{
DisplayName: displayName,
ContainerName: escapeContainerName(cont.Name),
ParentContainers: parentContainers,
Spec: cont.Spec,
Stats: cont.Stats,
MachineInfo: machineInfo,
ResourcesAvailable: cont.Spec.HasCpu || cont.Spec.HasMemory || cont.Spec.HasNetwork,
CpuAvailable: cont.Spec.HasCpu,
MemoryAvailable: cont.Spec.HasMemory,
NetworkAvailable: cont.Spec.HasNetwork,
FsAvailable: cont.Spec.HasFilesystem,
CustomMetricsAvailable: cont.Spec.HasCustomMetrics,
Root: rootDir,
}
}
err := pageTemplate.Execute(w, data)
if err != nil {
glog.Errorf("Failed to apply template: %s", err)
}
glog.V(5).Infof("Request took %s", time.Since(start))
return
}

View File

@ -1,163 +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.
package pages
import (
"fmt"
"html/template"
"net/http"
"net/url"
"strings"
httpmux "github.com/google/cadvisor/http/mux"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/manager"
auth "github.com/abbot/go-http-auth"
"github.com/golang/glog"
)
var pageTemplate *template.Template
type link struct {
// Text to show in the link.
Text string
// Web address to link to.
Link string
}
type keyVal struct {
Key string
Value string
}
type pageData struct {
DisplayName string
ContainerName string
ParentContainers []link
Subcontainers []link
Spec info.ContainerSpec
Stats []*info.ContainerStats
MachineInfo *info.MachineInfo
IsRoot bool
ResourcesAvailable bool
CpuAvailable bool
MemoryAvailable bool
NetworkAvailable bool
FsAvailable bool
CustomMetricsAvailable bool
SubcontainersAvailable bool
Root string
DockerStatus []keyVal
DockerDriverStatus []keyVal
DockerImages []info.DockerImage
}
func init() {
containersHtmlTemplate, _ := Asset("pages/assets/html/containers.html")
pageTemplate = template.New("containersTemplate").Funcs(funcMap)
_, err := pageTemplate.Parse(string(containersHtmlTemplate))
if err != nil {
glog.Fatalf("Failed to parse template: %s", err)
}
}
func containerHandlerNoAuth(containerManager manager.Manager) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
serveContainersPage(containerManager, w, r.URL)
}
}
func containerHandler(containerManager manager.Manager) auth.AuthenticatedHandlerFunc {
return func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
serveContainersPage(containerManager, w, r.URL)
}
}
func dockerHandlerNoAuth(containerManager manager.Manager) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
serveDockerPage(containerManager, w, r.URL)
}
}
func dockerHandler(containerManager manager.Manager) auth.AuthenticatedHandlerFunc {
return func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
serveDockerPage(containerManager, w, r.URL)
}
}
// Register http handlers
func RegisterHandlersDigest(mux httpmux.Mux, containerManager manager.Manager, authenticator *auth.DigestAuth) error {
// Register the handler for the containers page.
if authenticator != nil {
mux.HandleFunc(ContainersPage, authenticator.Wrap(containerHandler(containerManager)))
mux.HandleFunc(DockerPage, authenticator.Wrap(dockerHandler(containerManager)))
} else {
mux.HandleFunc(ContainersPage, containerHandlerNoAuth(containerManager))
mux.HandleFunc(DockerPage, dockerHandlerNoAuth(containerManager))
}
return nil
}
func RegisterHandlersBasic(mux httpmux.Mux, containerManager manager.Manager, authenticator *auth.BasicAuth) error {
// Register the handler for the containers and docker age.
if authenticator != nil {
mux.HandleFunc(ContainersPage, authenticator.Wrap(containerHandler(containerManager)))
mux.HandleFunc(DockerPage, authenticator.Wrap(dockerHandler(containerManager)))
} else {
mux.HandleFunc(ContainersPage, containerHandlerNoAuth(containerManager))
mux.HandleFunc(DockerPage, dockerHandlerNoAuth(containerManager))
}
return nil
}
func getContainerDisplayName(cont info.ContainerReference) string {
// Pick a user-added alias as display name.
displayName := ""
for _, alias := range cont.Aliases {
// ignore container id as alias.
if strings.Contains(cont.Name, alias) {
continue
}
// pick shortest display name if multiple aliases are available.
if displayName == "" || len(displayName) >= len(alias) {
displayName = alias
}
}
if displayName == "" {
displayName = cont.Name
} else if len(displayName) > 50 {
// truncate display name to fit in one line.
displayName = displayName[:50] + "..."
}
// Add the full container name to the display name.
if displayName != cont.Name {
displayName = fmt.Sprintf("%s (%s)", displayName, cont.Name)
}
return displayName
}
// Escape the non-path characters on a container name.
func escapeContainerName(containerName string) string {
parts := strings.Split(containerName, "/")
for i := range parts {
parts[i] = url.QueryEscape(parts[i])
}
return strings.Join(parts, "/")
}

View File

@ -1,27 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"assets.go",
"static.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/google/cadvisor/pages/static",
importpath = "github.com/google/cadvisor/pages/static",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/golang/glog:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

File diff suppressed because one or more lines are too long

View File

@ -1,77 +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.
// Handler for /static content.
package static
import (
"fmt"
"mime"
"net/http"
"net/url"
"path"
"github.com/golang/glog"
)
const StaticResource = "/static/"
var popper, _ = Asset("pages/assets/js/popper.min.js")
var bootstrapJs, _ = Asset("pages/assets/js/bootstrap-4.0.0-beta.2.min.js")
var containersJs, _ = Asset("pages/assets/js/containers.js")
var gchartsJs, _ = Asset("pages/assets/js/gcharts.js")
var googleJsapiJs, _ = Asset("pages/assets/js/google-jsapi.js")
var jqueryJs, _ = Asset("pages/assets/js/jquery-3.0.0.min.js")
var bootstrapCss, _ = Asset("pages/assets/styles/bootstrap-4.0.0-beta.2.min.css")
var bootstrapThemeCss, _ = Asset("pages/assets/styles/bootstrap-theme-3.1.1.min.css")
var containersCss, _ = Asset("pages/assets/styles/containers.css")
var staticFiles = map[string][]byte{
"popper.min.js": popper,
"bootstrap-4.0.0-beta.2.min.css": bootstrapCss,
"bootstrap-4.0.0-beta.2.min.js": bootstrapJs,
"bootstrap-theme-3.1.1.min.css": bootstrapThemeCss,
"containers.css": containersCss,
"containers.js": containersJs,
"gcharts.js": gchartsJs,
"google-jsapi.js": googleJsapiJs,
"jquery-3.0.0.min.js": jqueryJs,
}
func HandleRequest(w http.ResponseWriter, u *url.URL) {
if len(u.Path) <= len(StaticResource) {
http.Error(w, fmt.Sprintf("unknown static resource %q", u.Path), http.StatusNotFound)
return
}
// Get the static content if it exists.
resource := u.Path[len(StaticResource):]
content, ok := staticFiles[resource]
if !ok {
http.Error(w, fmt.Sprintf("unknown static resource %q", u.Path), http.StatusNotFound)
return
}
// Set Content-Type if we were able to detect it.
contentType := mime.TypeByExtension(path.Ext(resource))
if contentType != "" {
w.Header().Set("Content-Type", contentType)
}
if _, err := w.Write(content); err != nil {
glog.Errorf("Failed to write response: %v", err)
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,29 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["validate.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/google/cadvisor/validate",
importpath = "github.com/google/cadvisor/validate",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/google/cadvisor/container/docker:go_default_library",
"//vendor/github.com/google/cadvisor/manager:go_default_library",
"//vendor/github.com/google/cadvisor/utils:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,324 +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.
// Handler for /validate content.
// Validates cadvisor dependencies - kernel, os, docker setup.
package validate
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"strings"
"github.com/google/cadvisor/container/docker"
"github.com/google/cadvisor/manager"
"github.com/google/cadvisor/utils"
"github.com/opencontainers/runc/libcontainer/cgroups"
)
const (
ValidatePage = "/validate/"
Supported = "[Supported, but not recommended]"
Unsupported = "[Unsupported]"
Recommended = "[Supported and recommended]"
Unknown = "[Unknown]"
VersionFormat = "%d.%d%s"
OutputFormat = "%s: %s\n\t%s\n\n"
)
func getMajorMinor(version string) (int, int, error) {
var major, minor int
var ign string
n, err := fmt.Sscanf(version, VersionFormat, &major, &minor, &ign)
if n != 3 || err != nil {
log.Printf("Failed to parse version for %s", version)
return -1, -1, err
}
return major, minor, nil
}
func validateKernelVersion(version string) (string, string) {
desc := fmt.Sprintf("Kernel version is %s. Versions >= 2.6 are supported. 3.0+ are recommended.\n", version)
major, minor, err := getMajorMinor(version)
if err != nil {
desc = fmt.Sprintf("Could not parse kernel version. %s", desc)
return Unknown, desc
}
if major < 2 {
return Unsupported, desc
}
if major == 2 && minor < 6 {
return Unsupported, desc
}
if major >= 3 {
return Recommended, desc
}
return Supported, desc
}
func validateDockerVersion(version string) (string, string) {
desc := fmt.Sprintf("Docker version is %s. Versions >= 1.0 are supported. 1.2+ are recommended.\n", version)
major, minor, err := getMajorMinor(version)
if err != nil {
desc = fmt.Sprintf("Could not parse docker version. %s\n\t", desc)
return Unknown, desc
}
if major < 1 {
return Unsupported, desc
}
if major == 1 && minor < 2 {
return Supported, desc
}
return Recommended, desc
}
func getEnabledCgroups() (map[string]int, error) {
out, err := ioutil.ReadFile("/proc/cgroups")
if err != nil {
return nil, err
}
cgroups := make(map[string]int)
for i, line := range strings.Split(string(out), "\n") {
var cgroup string
var ign, enabled int
if i == 0 || line == "" {
continue
}
n, err := fmt.Sscanf(line, "%s %d %d %d", &cgroup, &ign, &ign, &enabled)
if n != 4 || err != nil {
if err == nil {
err = fmt.Errorf("failed to parse /proc/cgroup entry %s", line)
}
return nil, err
}
cgroups[cgroup] = enabled
}
return cgroups, nil
}
func areCgroupsPresent(available map[string]int, desired []string) (bool, string) {
for _, cgroup := range desired {
enabled, ok := available[cgroup]
if !ok {
reason := fmt.Sprintf("Missing cgroup %s. Available cgroups: %v\n", cgroup, available)
return false, reason
}
if enabled != 1 {
reason := fmt.Sprintf("Cgroup %s not enabled. Available cgroups: %v\n", cgroup, available)
return false, reason
}
}
return true, ""
}
func validateCpuCfsBandwidth(available_cgroups map[string]int) string {
ok, _ := areCgroupsPresent(available_cgroups, []string{"cpu"})
if !ok {
return "\tCpu cfs bandwidth status unknown: cpu cgroup not enabled.\n"
}
mnt, err := cgroups.FindCgroupMountpoint("cpu")
if err != nil {
return "\tCpu cfs bandwidth status unknown: cpu cgroup not mounted.\n"
}
_, err = os.Stat(path.Join(mnt, "cpu.cfs_period_us"))
if os.IsNotExist(err) {
return "\tCpu cfs bandwidth is disabled. Recompile kernel with \"CONFIG_CFS_BANDWIDTH\" enabled.\n"
}
return "\tCpu cfs bandwidth is enabled.\n"
}
func validateMemoryAccounting(available_cgroups map[string]int) string {
ok, _ := areCgroupsPresent(available_cgroups, []string{"memory"})
if !ok {
return "\tHierarchical memory accounting status unknown: memory cgroup not enabled.\n"
}
mnt, err := cgroups.FindCgroupMountpoint("memory")
if err != nil {
return "\tHierarchical memory accounting status unknown: memory cgroup not mounted.\n"
}
hier, err := ioutil.ReadFile(path.Join(mnt, "memory.use_hierarchy"))
if err != nil {
return "\tHierarchical memory accounting status unknown: hierarchy interface unavailable.\n"
}
var enabled int
n, err := fmt.Sscanf(string(hier), "%d", &enabled)
if err != nil || n != 1 {
return "\tHierarchical memory accounting status unknown: hierarchy interface unreadable.\n"
}
if enabled == 1 {
return "\tHierarchical memory accounting enabled. Reported memory usage includes memory used by child containers.\n"
}
return "\tHierarchical memory accounting disabled. Memory usage does not include usage from child containers.\n"
}
func validateCgroups() (string, string) {
required_cgroups := []string{"cpu", "cpuacct"}
recommended_cgroups := []string{"memory", "blkio", "cpuset", "devices", "freezer"}
available_cgroups, err := getEnabledCgroups()
desc := fmt.Sprintf("\tFollowing cgroups are required: %v\n\tFollowing other cgroups are recommended: %v\n", required_cgroups, recommended_cgroups)
if err != nil {
desc = fmt.Sprintf("Could not parse /proc/cgroups.\n%s", desc)
return Unknown, desc
}
ok, out := areCgroupsPresent(available_cgroups, required_cgroups)
if !ok {
out += desc
return Unsupported, out
}
ok, out = areCgroupsPresent(available_cgroups, recommended_cgroups)
if !ok {
// supported, but not recommended.
out += desc
return Supported, out
}
out = fmt.Sprintf("Available cgroups: %v\n", available_cgroups)
out += desc
out += validateMemoryAccounting(available_cgroups)
out += validateCpuCfsBandwidth(available_cgroups)
return Recommended, out
}
func validateDockerInfo() (string, string) {
info, err := docker.ValidateInfo()
if err != nil {
return Unsupported, fmt.Sprintf("Docker setup is invalid: %v", err)
}
desc := fmt.Sprintf("Storage driver is %s.\n", info.Driver)
return Recommended, desc
}
func validateCgroupMounts() (string, string) {
const recommendedMount = "/sys/fs/cgroup"
desc := fmt.Sprintf("\tAny cgroup mount point that is detectible and accessible is supported. %s is recommended as a standard location.\n", recommendedMount)
mnt, err := cgroups.FindCgroupMountpoint("cpu")
if err != nil {
out := "Could not locate cgroup mount point.\n"
out += desc
return Unknown, out
}
mnt = path.Dir(mnt)
if !utils.FileExists(mnt) {
out := fmt.Sprintf("Cgroup mount directory %s inaccessible.\n", mnt)
out += desc
return Unsupported, out
}
mounts, err := ioutil.ReadDir(mnt)
if err != nil {
out := fmt.Sprintf("Could not read cgroup mount directory %s.\n", mnt)
out += desc
return Unsupported, out
}
mountNames := "\tCgroup mount directories: "
for _, mount := range mounts {
mountNames += mount.Name() + " "
}
mountNames += "\n"
out := fmt.Sprintf("Cgroups are mounted at %s.\n", mnt)
out += mountNames
out += desc
info, err := ioutil.ReadFile("/proc/mounts")
if err != nil {
out := fmt.Sprintf("Could not read /proc/mounts.\n")
out += desc
return Unsupported, out
}
out += "\tCgroup mounts:\n"
for _, line := range strings.Split(string(info), "\n") {
if strings.Contains(line, " cgroup ") {
out += "\t" + line + "\n"
}
}
if mnt == recommendedMount {
return Recommended, out
}
return Supported, out
}
func validateIoScheduler(containerManager manager.Manager) (string, string) {
var desc string
mi, err := containerManager.GetMachineInfo()
if err != nil {
return Unknown, "Machine info not available\n\t"
}
cfq := false
for _, disk := range mi.DiskMap {
desc += fmt.Sprintf("\t Disk %q Scheduler type %q.\n", disk.Name, disk.Scheduler)
if disk.Scheduler == "cfq" {
cfq = true
}
}
// Since we get lot of random block devices, report recommended if
// at least one of them is on cfq. Report Supported otherwise.
if cfq {
desc = "At least one device supports 'cfq' I/O scheduler. Some disk stats can be reported.\n" + desc
return Recommended, desc
}
desc = "None of the devices support 'cfq' I/O scheduler. No disk stats can be reported.\n" + desc
return Supported, desc
}
func HandleRequest(w http.ResponseWriter, containerManager manager.Manager) error {
// Get cAdvisor version Info.
versionInfo, err := containerManager.GetVersionInfo()
if err != nil {
return err
}
out := fmt.Sprintf("cAdvisor version: %s\n\n", versionInfo.CadvisorVersion)
// No OS is preferred or unsupported as of now.
out += fmt.Sprintf("OS version: %s\n\n", versionInfo.ContainerOsVersion)
kernelValidation, desc := validateKernelVersion(versionInfo.KernelVersion)
out += fmt.Sprintf(OutputFormat, "Kernel version", kernelValidation, desc)
cgroupValidation, desc := validateCgroups()
out += fmt.Sprintf(OutputFormat, "Cgroup setup", cgroupValidation, desc)
mountsValidation, desc := validateCgroupMounts()
out += fmt.Sprintf(OutputFormat, "Cgroup mount setup", mountsValidation, desc)
dockerValidation, desc := validateDockerVersion(versionInfo.DockerVersion)
out += fmt.Sprintf(OutputFormat, "Docker version", dockerValidation, desc)
dockerInfoValidation, desc := validateDockerInfo()
out += fmt.Sprintf(OutputFormat, "Docker driver setup", dockerInfoValidation, desc)
ioSchedulerValidation, desc := validateIoScheduler(containerManager)
out += fmt.Sprintf(OutputFormat, "Block device setup", ioSchedulerValidation, desc)
// Output debug info.
debugInfo := containerManager.DebugInfo()
for category, lines := range debugInfo {
out += fmt.Sprintf(OutputFormat, category, "", strings.Join(lines, "\n\t"))
}
_, err = w.Write([]byte(out))
return err
}