mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-06 19:52:42 +00:00
Merge pull request #5305 from vmarmol/cadvisor-dep
Add HTTP cAdvisor dependencies.
This commit is contained in:
77
Godeps/Godeps.json
generated
77
Godeps/Godeps.json
generated
@@ -54,6 +54,10 @@
|
||||
"Comment": "v0.6.2-10-g51fe59a",
|
||||
"Rev": "51fe59aca108dc5680109e7b2051cbdcfa5a253c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/abbot/go-http-auth",
|
||||
"Rev": "c0ef4539dfab4d21c8ef20ba2924f9fc6f186d35"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/beorn7/perks/quantile",
|
||||
"Rev": "b965b613227fddccbfffe13eae360ed3fa822f8d"
|
||||
@@ -179,60 +183,85 @@
|
||||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
"Rev": "7f07925444bb51fa4cf9dfe6f7661876f8852275"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/api",
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/client",
|
||||
"Comment": "0.10.1-36-g62a1788",
|
||||
"Rev": "62a1788621f4adee2dbf08c26060ed7fb8c0297d"
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/container",
|
||||
"Comment": "0.10.1-36-g62a1788",
|
||||
"Rev": "62a1788621f4adee2dbf08c26060ed7fb8c0297d"
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/events",
|
||||
"Comment": "0.10.1-36-g62a1788",
|
||||
"Rev": "62a1788621f4adee2dbf08c26060ed7fb8c0297d"
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/fs",
|
||||
"Comment": "0.10.1-36-g62a1788",
|
||||
"Rev": "62a1788621f4adee2dbf08c26060ed7fb8c0297d"
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/healthz",
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/http",
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/info/v1",
|
||||
"Comment": "0.10.1-36-g62a1788",
|
||||
"Rev": "62a1788621f4adee2dbf08c26060ed7fb8c0297d"
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/info/v2",
|
||||
"Comment": "0.10.1-36-g62a1788",
|
||||
"Rev": "62a1788621f4adee2dbf08c26060ed7fb8c0297d"
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/manager",
|
||||
"Comment": "0.10.1-36-g62a1788",
|
||||
"Rev": "62a1788621f4adee2dbf08c26060ed7fb8c0297d"
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/pages",
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/storage",
|
||||
"Comment": "0.10.1-36-g62a1788",
|
||||
"Rev": "62a1788621f4adee2dbf08c26060ed7fb8c0297d"
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/summary",
|
||||
"Comment": "0.10.1-36-g62a1788",
|
||||
"Rev": "62a1788621f4adee2dbf08c26060ed7fb8c0297d"
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/utils",
|
||||
"Comment": "0.10.1-36-g62a1788",
|
||||
"Rev": "62a1788621f4adee2dbf08c26060ed7fb8c0297d"
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/validate",
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/version",
|
||||
"Comment": "0.10.1-36-g62a1788",
|
||||
"Rev": "62a1788621f4adee2dbf08c26060ed7fb8c0297d"
|
||||
"Comment": "0.10.1-42-g4e2479b",
|
||||
"Rev": "4e2479bcabe7af08825066f5ece2122553562b34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/gofuzz",
|
||||
@@ -264,10 +293,6 @@
|
||||
"ImportPath": "github.com/mitchellh/goamz/ec2",
|
||||
"Rev": "703cfb45985762869e465f37ed030ff01615ff1e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mitchellh/goamz/elb",
|
||||
"Rev": "703cfb45985762869e465f37ed030ff01615ff1e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mitchellh/mapstructure",
|
||||
"Rev": "740c764bc6149d3f1806231418adb9f52c11bcbf"
|
||||
|
5
Godeps/_workspace/src/github.com/abbot/go-http-auth/.gitignore
generated
vendored
Normal file
5
Godeps/_workspace/src/github.com/abbot/go-http-auth/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
*~
|
||||
*.a
|
||||
*.6
|
||||
*.out
|
||||
_testmain.go
|
178
Godeps/_workspace/src/github.com/abbot/go-http-auth/LICENSE
generated
vendored
Normal file
178
Godeps/_workspace/src/github.com/abbot/go-http-auth/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
|
||||
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
|
||||
|
12
Godeps/_workspace/src/github.com/abbot/go-http-auth/Makefile
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/abbot/go-http-auth/Makefile
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
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
|
70
Godeps/_workspace/src/github.com/abbot/go-http-auth/README.md
generated
vendored
Normal file
70
Godeps/_workspace/src/github.com/abbot/go-http-auth/README.md
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
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.
|
48
Godeps/_workspace/src/github.com/abbot/go-http-auth/auth.go
generated
vendored
Normal file
48
Godeps/_workspace/src/github.com/abbot/go-http-auth/auth.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
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)
|
||||
})
|
||||
}
|
88
Godeps/_workspace/src/github.com/abbot/go-http-auth/basic.go
generated
vendored
Normal file
88
Godeps/_workspace/src/github.com/abbot/go-http-auth/basic.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
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}
|
||||
}
|
38
Godeps/_workspace/src/github.com/abbot/go-http-auth/basic_test.go
generated
vendored
Normal file
38
Godeps/_workspace/src/github.com/abbot/go-http-auth/basic_test.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAuthBasic(t *testing.T) {
|
||||
secrets := HtpasswdFileProvider("test.htpasswd")
|
||||
a := &BasicAuth{Realm: "example.com", Secrets: secrets}
|
||||
r := &http.Request{}
|
||||
r.Method = "GET"
|
||||
if a.CheckAuth(r) != "" {
|
||||
t.Fatal("CheckAuth passed on empty headers")
|
||||
}
|
||||
r.Header = http.Header(make(map[string][]string))
|
||||
r.Header.Set("Authorization", "Digest blabla ololo")
|
||||
if a.CheckAuth(r) != "" {
|
||||
t.Fatal("CheckAuth passed on bad headers")
|
||||
}
|
||||
r.Header.Set("Authorization", "Basic !@#")
|
||||
if a.CheckAuth(r) != "" {
|
||||
t.Fatal("CheckAuth passed on bad base64 data")
|
||||
}
|
||||
|
||||
data := [][]string{
|
||||
{"test", "hello"},
|
||||
{"test2", "hello2"},
|
||||
}
|
||||
for _, tc := range data {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(tc[0] + ":" + tc[1]))
|
||||
r.Header.Set("Authorization", "Basic "+auth)
|
||||
if a.CheckAuth(r) != tc[0] {
|
||||
t.Fatalf("CheckAuth failed for user '%s'", tc[0])
|
||||
}
|
||||
}
|
||||
}
|
226
Godeps/_workspace/src/github.com/abbot/go-http-auth/digest.go
generated
vendored
Normal file
226
Godeps/_workspace/src/github.com/abbot/go-http-auth/digest.go
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
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
|
||||
}
|
57
Godeps/_workspace/src/github.com/abbot/go-http-auth/digest_test.go
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/abbot/go-http-auth/digest_test.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestAuthDigest(t *testing.T) {
|
||||
secrets := HtdigestFileProvider("test.htdigest")
|
||||
da := &DigestAuth{Opaque: "U7H+ier3Ae8Skd/g",
|
||||
Realm: "example.com",
|
||||
Secrets: secrets,
|
||||
clients: map[string]*digest_client{}}
|
||||
r := &http.Request{}
|
||||
r.Method = "GET"
|
||||
if u, _ := da.CheckAuth(r); u != "" {
|
||||
t.Fatal("non-empty auth for empty request header")
|
||||
}
|
||||
r.Header = http.Header(make(map[string][]string))
|
||||
r.Header.Set("Authorization", "Digest blabla")
|
||||
if u, _ := da.CheckAuth(r); u != "" {
|
||||
t.Fatal("non-empty auth for bad request header")
|
||||
}
|
||||
r.Header.Set("Authorization", `Digest username="test", realm="example.com", nonce="Vb9BP/h81n3GpTTB", uri="/t2", cnonce="NjE4MTM2", nc=00000001, qop="auth", response="ffc357c4eba74773c8687e0bc724c9a3", opaque="U7H+ier3Ae8Skd/g", algorithm="MD5"`)
|
||||
if u, _ := da.CheckAuth(r); u != "" {
|
||||
t.Fatal("non-empty auth for unknown client")
|
||||
}
|
||||
|
||||
r.URL, _ = url.Parse("/t2")
|
||||
da.clients["Vb9BP/h81n3GpTTB"] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()}
|
||||
if u, _ := da.CheckAuth(r); u != "test" {
|
||||
t.Fatal("empty auth for legitimate client")
|
||||
}
|
||||
if u, _ := da.CheckAuth(r); u != "" {
|
||||
t.Fatal("non-empty auth for outdated nc")
|
||||
}
|
||||
|
||||
r.URL, _ = url.Parse("/")
|
||||
da.clients["Vb9BP/h81n3GpTTB"] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()}
|
||||
if u, _ := da.CheckAuth(r); u != "" {
|
||||
t.Fatal("non-empty auth for bad request path")
|
||||
}
|
||||
|
||||
r.URL, _ = url.Parse("/t3")
|
||||
da.clients["Vb9BP/h81n3GpTTB"] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()}
|
||||
if u, _ := da.CheckAuth(r); u != "" {
|
||||
t.Fatal("non-empty auth for bad request path")
|
||||
}
|
||||
|
||||
da.clients["+RbVXSbIoa1SaJk1"] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()}
|
||||
r.Header.Set("Authorization", `Digest username="test", realm="example.com", nonce="+RbVXSbIoa1SaJk1", uri="/", cnonce="NjE4NDkw", nc=00000001, qop="auth", response="c08918024d7faaabd5424654c4e3ad1c", opaque="U7H+ier3Ae8Skd/g", algorithm="MD5"`)
|
||||
if u, _ := da.CheckAuth(r); u != "test" {
|
||||
t.Fatal("empty auth for valid request in subpath")
|
||||
}
|
||||
}
|
35
Godeps/_workspace/src/github.com/abbot/go-http-auth/examples/basic.go
generated
vendored
Normal file
35
Godeps/_workspace/src/github.com/abbot/go-http-auth/examples/basic.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// +build ignore
|
||||
|
||||
/*
|
||||
Example application using Basic auth
|
||||
|
||||
Build with:
|
||||
|
||||
go build basic.go
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
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)
|
||||
}
|
35
Godeps/_workspace/src/github.com/abbot/go-http-auth/examples/digest.go
generated
vendored
Normal file
35
Godeps/_workspace/src/github.com/abbot/go-http-auth/examples/digest.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// +build ignore
|
||||
|
||||
/*
|
||||
Example application using Digest auth
|
||||
|
||||
Build with:
|
||||
|
||||
go build digest.go
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
auth ".."
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Secret(user, realm string) string {
|
||||
if user == "john" {
|
||||
// password is "hello"
|
||||
return "b98e16cbc3d01734b264adba7baa3bf9"
|
||||
}
|
||||
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.NewDigestAuthenticator("example.com", Secret)
|
||||
http.HandleFunc("/", authenticator.Wrap(handle))
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
36
Godeps/_workspace/src/github.com/abbot/go-http-auth/examples/wrapped.go
generated
vendored
Normal file
36
Godeps/_workspace/src/github.com/abbot/go-http-auth/examples/wrapped.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// +build ignore
|
||||
|
||||
/*
|
||||
Example demonstrating how to wrap an application which is unaware of
|
||||
authenticated requests with a "pass-through" authentication
|
||||
|
||||
Build with:
|
||||
|
||||
go build wrapped.go
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
auth ".."
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Secret(user, realm string) string {
|
||||
if user == "john" {
|
||||
// password is "hello"
|
||||
return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func regular_handler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "<html><body><h1>This application is unaware of authentication</h1></body></html>")
|
||||
}
|
||||
|
||||
func main() {
|
||||
authenticator := auth.NewBasicAuthenticator("example.com", Secret)
|
||||
http.HandleFunc("/", auth.JustCheck(authenticator, regular_handler))
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
92
Godeps/_workspace/src/github.com/abbot/go-http-auth/md5crypt.go
generated
vendored
Normal file
92
Godeps/_workspace/src/github.com/abbot/go-http-auth/md5crypt.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
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...)
|
||||
}
|
18
Godeps/_workspace/src/github.com/abbot/go-http-auth/md5crypt_test.go
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/abbot/go-http-auth/md5crypt_test.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package auth
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_MD5Crypt(t *testing.T) {
|
||||
test_cases := [][]string{
|
||||
{"apache", "$apr1$J.w5a/..$IW9y6DR0oO/ADuhlMF5/X1"},
|
||||
{"pass", "$1$YeNsbWdH$wvOF8JdqsoiLix754LTW90"},
|
||||
}
|
||||
for _, tc := range test_cases {
|
||||
e := NewMD5Entry(tc[1])
|
||||
result := MD5Crypt([]byte(tc[0]), e.Salt, e.Magic)
|
||||
if string(result) != tc[1] {
|
||||
t.Fatalf("MD5Crypt returned '%s' instead of '%s'", string(result), tc[1])
|
||||
}
|
||||
t.Logf("MD5Crypt: '%s' (%s%s$) -> %s", tc[0], e.Magic, e.Salt, result)
|
||||
}
|
||||
}
|
30
Godeps/_workspace/src/github.com/abbot/go-http-auth/misc.go
generated
vendored
Normal file
30
Godeps/_workspace/src/github.com/abbot/go-http-auth/misc.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
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))
|
||||
}
|
12
Godeps/_workspace/src/github.com/abbot/go-http-auth/misc_test.go
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/abbot/go-http-auth/misc_test.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package auth
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestH(t *testing.T) {
|
||||
const hello = "Hello, world!"
|
||||
const hello_md5 = "6cd3556deb0da54bca060b4c39479839"
|
||||
h := H(hello)
|
||||
if h != hello_md5 {
|
||||
t.Fatal("Incorrect digest for test string:", h, "instead of", hello_md5)
|
||||
}
|
||||
}
|
1
Godeps/_workspace/src/github.com/abbot/go-http-auth/test.htdigest
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/abbot/go-http-auth/test.htdigest
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
test:example.com:aa78524fceb0e50fd8ca96dd818b8cf9
|
2
Godeps/_workspace/src/github.com/abbot/go-http-auth/test.htpasswd
generated
vendored
Normal file
2
Godeps/_workspace/src/github.com/abbot/go-http-auth/test.htpasswd
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
test:{SHA}qvTGHdzF6KLavt4PO0gs2a6pQ00=
|
||||
test2:$apr1$a0j62R97$mYqFkloXH0/UOaUnAiV2b0
|
136
Godeps/_workspace/src/github.com/abbot/go-http-auth/users.go
generated
vendored
Normal file
136
Godeps/_workspace/src/github.com/abbot/go-http-auth/users.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
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
|
||||
}
|
||||
}
|
33
Godeps/_workspace/src/github.com/abbot/go-http-auth/users_test.go
generated
vendored
Normal file
33
Godeps/_workspace/src/github.com/abbot/go-http-auth/users_test.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHtdigestFile(t *testing.T) {
|
||||
secrets := HtdigestFileProvider("test.htdigest")
|
||||
digest := secrets("test", "example.com")
|
||||
if digest != "aa78524fceb0e50fd8ca96dd818b8cf9" {
|
||||
t.Fatal("Incorrect digest for test user:", digest)
|
||||
}
|
||||
digest = secrets("test", "example1.com")
|
||||
if digest != "" {
|
||||
t.Fatal("Got digest for user in non-existant realm:", digest)
|
||||
}
|
||||
digest = secrets("test1", "example.com")
|
||||
if digest != "" {
|
||||
t.Fatal("Got digest for non-existant user:", digest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHtpasswdFile(t *testing.T) {
|
||||
secrets := HtpasswdFileProvider("test.htpasswd")
|
||||
passwd := secrets("test", "blah")
|
||||
if passwd != "{SHA}qvTGHdzF6KLavt4PO0gs2a6pQ00=" {
|
||||
t.Fatal("Incorrect passwd for test user:", passwd)
|
||||
}
|
||||
passwd = secrets("test3", "blah")
|
||||
if passwd != "" {
|
||||
t.Fatal("Got passwd for non-existant user:", passwd)
|
||||
}
|
||||
}
|
251
Godeps/_workspace/src/github.com/google/cadvisor/api/handler.go
generated
vendored
Normal file
251
Godeps/_workspace/src/github.com/google/cadvisor/api/handler.go
generated
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
// 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/golang/glog"
|
||||
"github.com/google/cadvisor/events"
|
||||
httpMux "github.com/google/cadvisor/http/mux"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/manager"
|
||||
)
|
||||
|
||||
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(2).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)
|
||||
fmt.Fprintf(w, "Supported API versions: %s", strings.Join(versions, ","))
|
||||
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)
|
||||
fmt.Fprintf(w, "Supported request types: %q", strings.Join(requestTypes, ","))
|
||||
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(results chan *events.Event, w http.ResponseWriter, r *http.Request) 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():
|
||||
glog.Infof("Client stopped listening")
|
||||
return nil
|
||||
case ev := <-results:
|
||||
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) {
|
||||
var query info.ContainerInfoRequest
|
||||
|
||||
// Default stats and samples is 64.
|
||||
query.NumStats = 64
|
||||
|
||||
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: historical, 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&historical=true&max_events=10
|
||||
func getEventRequest(r *http.Request) (*events.Request, bool, error) {
|
||||
query := events.NewRequest()
|
||||
getHistoricalEvents := false
|
||||
|
||||
urlMap := r.URL.Query()
|
||||
|
||||
if val, ok := urlMap["historical"]; ok {
|
||||
newBool, err := strconv.ParseBool(val[0])
|
||||
if err == nil {
|
||||
getHistoricalEvents = newBool
|
||||
}
|
||||
}
|
||||
if val, ok := urlMap["subcontainers"]; ok {
|
||||
newBool, err := strconv.ParseBool(val[0])
|
||||
if err == nil {
|
||||
query.IncludeSubcontainers = newBool
|
||||
}
|
||||
}
|
||||
if val, ok := urlMap["oom_events"]; ok {
|
||||
newBool, err := strconv.ParseBool(val[0])
|
||||
if err == nil {
|
||||
query.EventType[events.TypeOom] = newBool
|
||||
}
|
||||
}
|
||||
if val, ok := urlMap["creation_events"]; ok {
|
||||
newBool, err := strconv.ParseBool(val[0])
|
||||
if err == nil {
|
||||
query.EventType[events.TypeContainerCreation] = newBool
|
||||
}
|
||||
}
|
||||
if val, ok := urlMap["deletion_events"]; ok {
|
||||
newBool, err := strconv.ParseBool(val[0])
|
||||
if err == nil {
|
||||
query.EventType[events.TypeContainerDeletion] = 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
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(2).Infof(
|
||||
"%v was returned in api/handler.go:getEventRequest from the url rawQuery %v",
|
||||
query, r.URL.RawQuery)
|
||||
return query, getHistoricalEvents, nil
|
||||
}
|
||||
|
||||
func getContainerName(request []string) string {
|
||||
return path.Join("/", strings.Join(request, "/"))
|
||||
}
|
348
Godeps/_workspace/src/github.com/google/cadvisor/api/versions.go
generated
vendored
Normal file
348
Godeps/_workspace/src/github.com/google/cadvisor/api/versions.go
generated
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
// 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"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/google/cadvisor/events"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/info/v2"
|
||||
"github.com/google/cadvisor/manager"
|
||||
)
|
||||
|
||||
const (
|
||||
containersApi = "containers"
|
||||
subcontainersApi = "subcontainers"
|
||||
machineApi = "machine"
|
||||
dockerApi = "docker"
|
||||
summaryApi = "summary"
|
||||
specApi = "spec"
|
||||
eventsApi = "events"
|
||||
)
|
||||
|
||||
// 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(v1_3)
|
||||
|
||||
return []ApiVersion{v1_0, v1_1, v1_2, v1_3, v2_0}
|
||||
|
||||
}
|
||||
|
||||
// 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(2).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(2).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(2).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(2).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:
|
||||
query, eventsFromAllTime, err := getEventRequest(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(2).Infof("Api - Events(%v)", query)
|
||||
if eventsFromAllTime {
|
||||
pastEvents, err := m.GetPastEvents(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeResult(pastEvents, w)
|
||||
}
|
||||
eventsChannel := make(chan *events.Event, 10)
|
||||
err = m.WatchForEvents(query, eventsChannel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return streamResults(eventsChannel, w, r)
|
||||
default:
|
||||
return self.baseVersion.HandleRequest(requestType, request, m, w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// API v2.0
|
||||
|
||||
// v2.0 builds on v1.3
|
||||
type version2_0 struct {
|
||||
baseVersion *version1_3
|
||||
}
|
||||
|
||||
func newVersion2_0(v *version1_3) *version2_0 {
|
||||
return &version2_0{
|
||||
baseVersion: v,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *version2_0) Version() string {
|
||||
return "v2.0"
|
||||
}
|
||||
|
||||
func (self *version2_0) SupportedRequestTypes() []string {
|
||||
return append(self.baseVersion.SupportedRequestTypes(), summaryApi)
|
||||
}
|
||||
|
||||
func (self *version2_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
|
||||
switch requestType {
|
||||
case summaryApi:
|
||||
containerName := getContainerName(request)
|
||||
glog.V(2).Infof("Api - Summary(%v)", containerName)
|
||||
|
||||
stats, err := m.GetContainerDerivedStats(containerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeResult(stats, w)
|
||||
case specApi:
|
||||
containerName := getContainerName(request)
|
||||
glog.V(2).Infof("Api - Spec(%v)", containerName)
|
||||
spec, err := m.GetContainerSpec(containerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
specV2 := convertSpec(spec)
|
||||
return writeResult(specV2, w)
|
||||
default:
|
||||
return self.baseVersion.HandleRequest(requestType, request, m, w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert container spec from v1 to v2.
|
||||
func convertSpec(specV1 info.ContainerSpec) v2.ContainerSpec {
|
||||
specV2 := v2.ContainerSpec{
|
||||
CreationTime: specV1.CreationTime,
|
||||
HasCpu: specV1.HasCpu,
|
||||
HasMemory: specV1.HasMemory,
|
||||
}
|
||||
if specV1.HasCpu {
|
||||
specV2.Cpu.Limit = specV1.Cpu.Limit
|
||||
specV2.Cpu.MaxLimit = specV1.Cpu.MaxLimit
|
||||
specV2.Cpu.Mask = specV1.Cpu.Mask
|
||||
}
|
||||
if specV1.HasMemory {
|
||||
specV2.Memory.Limit = specV1.Memory.Limit
|
||||
specV2.Memory.Reservation = specV1.Memory.Reservation
|
||||
specV2.Memory.SwapLimit = specV1.Memory.SwapLimit
|
||||
}
|
||||
return specV2
|
||||
}
|
81
Godeps/_workspace/src/github.com/google/cadvisor/api/versions_test.go
generated
vendored
Normal file
81
Godeps/_workspace/src/github.com/google/cadvisor/api/versions_test.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
// 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 (
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/cadvisor/events"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// returns an http.Request pointer for an input url test string
|
||||
func makeHTTPRequest(requestURL string, t *testing.T) *http.Request {
|
||||
dummyReader, _ := io.Pipe()
|
||||
r, err := http.NewRequest("GET", requestURL, dummyReader)
|
||||
assert.Nil(t, err)
|
||||
return r
|
||||
}
|
||||
|
||||
func TestGetEventRequestBasicRequest(t *testing.T) {
|
||||
r := makeHTTPRequest("http://localhost:8080/api/v1.3/events?oom_events=true&historical=true&max_events=10", t)
|
||||
expectedQuery := &events.Request{
|
||||
EventType: map[events.EventType]bool{
|
||||
events.TypeOom: true,
|
||||
},
|
||||
MaxEventsReturned: 10,
|
||||
}
|
||||
|
||||
receivedQuery, getHistoricalEvents, err := getEventRequest(r)
|
||||
|
||||
if !reflect.DeepEqual(expectedQuery, receivedQuery) {
|
||||
t.Errorf("expected %v but received %v", expectedQuery, receivedQuery)
|
||||
}
|
||||
assert.True(t, getHistoricalEvents)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestGetEventEmptyRequest(t *testing.T) {
|
||||
r := makeHTTPRequest("", t)
|
||||
expectedQuery := events.NewRequest()
|
||||
|
||||
receivedQuery, getHistoricalEvents, err := getEventRequest(r)
|
||||
|
||||
if !reflect.DeepEqual(expectedQuery, receivedQuery) {
|
||||
t.Errorf("expected %v but received %v", expectedQuery, receivedQuery)
|
||||
}
|
||||
assert.False(t, getHistoricalEvents)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestGetEventRequestDoubleArgument(t *testing.T) {
|
||||
r := makeHTTPRequest("http://localhost:8080/api/v1.3/events?historical=true&oom_events=true&oom_events=false", t)
|
||||
expectedQuery := &events.Request{
|
||||
EventType: map[events.EventType]bool{
|
||||
events.TypeOom: true,
|
||||
},
|
||||
}
|
||||
|
||||
receivedQuery, getHistoricalEvents, err := getEventRequest(r)
|
||||
|
||||
if !reflect.DeepEqual(expectedQuery, receivedQuery) {
|
||||
t.Errorf("expected %v but received %v", expectedQuery, receivedQuery)
|
||||
}
|
||||
assert.True(t, getHistoricalEvents)
|
||||
assert.Nil(t, err)
|
||||
}
|
32
Godeps/_workspace/src/github.com/google/cadvisor/healthz/healthz.go
generated
vendored
Normal file
32
Godeps/_workspace/src/github.com/google/cadvisor/healthz/healthz.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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
|
||||
}
|
101
Godeps/_workspace/src/github.com/google/cadvisor/http/handlers.go
generated
vendored
Normal file
101
Godeps/_workspace/src/github.com/google/cadvisor/http/handlers.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// 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"
|
||||
|
||||
auth "github.com/abbot/go-http-auth"
|
||||
"github.com/golang/glog"
|
||||
"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/pages"
|
||||
"github.com/google/cadvisor/pages/static"
|
||||
"github.com/google/cadvisor/validate"
|
||||
)
|
||||
|
||||
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 {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
})
|
||||
|
||||
// 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 = false
|
||||
|
||||
// Setup the authenticator object
|
||||
if httpAuthFile != "" {
|
||||
glog.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.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 {
|
||||
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
|
||||
}
|
||||
|
||||
func staticHandlerNoAuth(w http.ResponseWriter, r *http.Request) {
|
||||
err := static.HandleRequest(w, r.URL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func staticHandler(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
|
||||
err := static.HandleRequest(w, r.URL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
}
|
26
Godeps/_workspace/src/github.com/google/cadvisor/http/mux/mux.go
generated
vendored
Normal file
26
Godeps/_workspace/src/github.com/google/cadvisor/http/mux/mux.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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)
|
||||
}
|
2
Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go
generated
vendored
2
Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go
generated
vendored
@@ -127,7 +127,7 @@ func New(memoryStorage *memory.InMemoryStorage, sysfs sysfs.SysFs) (Manager, err
|
||||
// Register the raw driver.
|
||||
err = raw.Register(newManager)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("registration of the raw container factory failed: %v", err)
|
||||
glog.Errorf("Registration of the raw container factory failed: %v", err)
|
||||
}
|
||||
|
||||
return newManager, nil
|
||||
|
247
Godeps/_workspace/src/github.com/google/cadvisor/pages/containers.go
generated
vendored
Normal file
247
Godeps/_workspace/src/github.com/google/cadvisor/pages/containers.go
generated
vendored
Normal file
@@ -0,0 +1,247 @@
|
||||
// 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"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/manager"
|
||||
)
|
||||
|
||||
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, " "))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func toMegabytes(bytes uint64) float64 {
|
||||
return float64(bytes) / (1 << 20)
|
||||
}
|
||||
|
||||
func printSize(bytes uint64) string {
|
||||
if bytes >= math.MaxInt64 {
|
||||
return "unlimited"
|
||||
}
|
||||
return ByteSize(bytes).Size()
|
||||
}
|
||||
|
||||
func printUnit(bytes uint64) string {
|
||||
if bytes >= math.MaxInt64 {
|
||||
return ""
|
||||
}
|
||||
return ByteSize(bytes).Unit()
|
||||
}
|
||||
|
||||
func toMemoryPercent(usage uint64, spec *info.ContainerSpec, machine *info.MachineInfo) int {
|
||||
// Saturate limit to the machine size.
|
||||
limit := uint64(spec.Memory.Limit)
|
||||
if limit > uint64(machine.MemoryCapacity) {
|
||||
limit = uint64(machine.MemoryCapacity)
|
||||
}
|
||||
|
||||
return int((usage * 100) / limit)
|
||||
}
|
||||
|
||||
func serveContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
|
||||
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 {
|
||||
return fmt.Errorf("failed to get container %q with error: %v", containerName, err)
|
||||
}
|
||||
displayName := getContainerDisplayName(cont.ContainerReference)
|
||||
|
||||
// Get the MachineInfo
|
||||
machineInfo, err := m.GetMachineInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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: 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(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 {
|
||||
subcontainerLinks = append(subcontainerLinks, link{
|
||||
Text: getContainerDisplayName(sub),
|
||||
Link: path.Join(ContainersPage, sub.Name),
|
||||
})
|
||||
}
|
||||
|
||||
data := &pageData{
|
||||
DisplayName: displayName,
|
||||
ContainerName: 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,
|
||||
}
|
||||
err = pageTemplate.Execute(w, data)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to apply template: %s", err)
|
||||
}
|
||||
|
||||
glog.V(1).Infof("Request took %s", time.Since(start))
|
||||
return nil
|
||||
}
|
186
Godeps/_workspace/src/github.com/google/cadvisor/pages/containers_html.go
generated
vendored
Normal file
186
Godeps/_workspace/src/github.com/google/cadvisor/pages/containers_html.go
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
// 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
|
||||
|
||||
const containersHtmlTemplate = `
|
||||
<html>
|
||||
<head>
|
||||
<title>cAdvisor - {{.DisplayName}}</title>
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="/static/bootstrap-3.1.1.min.css">
|
||||
|
||||
<!-- Optional theme -->
|
||||
<link rel="stylesheet" href="/static/bootstrap-theme-3.1.1.min.css">
|
||||
|
||||
<link rel="stylesheet" href="/static/containers.css">
|
||||
|
||||
<!-- Latest compiled and minified JavaScript -->
|
||||
<script src="/static/jquery-1.10.2.min.js"></script>
|
||||
<script src="/static/bootstrap-3.1.1.min.js"></script>
|
||||
<script type="text/javascript" src="/static/google-jsapi.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/static/containers.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container theme-showcase" >
|
||||
<div class="col-sm-12" id="logo">
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h1>{{.DisplayName}}</h1>
|
||||
</div>
|
||||
<ol class="breadcrumb">
|
||||
{{range $parentContainer := .ParentContainers}}
|
||||
<li><a href="{{$parentContainer.Link}}">{{$parentContainer.Text}}</a></li>
|
||||
{{end}}
|
||||
</ol>
|
||||
</div>
|
||||
{{if .IsRoot}}
|
||||
<div class="col-sm-12">
|
||||
<h4><a href="/docker">Docker Containers</a></h4>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Subcontainers}}
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h3>Subcontainers</h3>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
{{range $subcontainer := .Subcontainers}}
|
||||
<a href="{{$subcontainer.Link}}" class="list-group-item">{{$subcontainer.Text}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .ResourcesAvailable}}
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h3>Isolation</h3>
|
||||
</div>
|
||||
{{if .CpuAvailable}}
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item active isolation-title panel-title">CPU</li>
|
||||
{{if .Spec.Cpu.Limit}}
|
||||
<li class="list-group-item"><span class="stat-label">Shares</span> {{printShares .Spec.Cpu.Limit}} <span class="unit-label">shares</span></li>
|
||||
{{end}}
|
||||
{{if .Spec.Cpu.MaxLimit}}
|
||||
<li class="list-group-item"><span class="stat-label">Max Limit</span> {{printCores .Spec.Cpu.MaxLimit}} <span class="unit-label">cores</span></li>
|
||||
{{end}}
|
||||
{{if .Spec.Cpu.Mask}}
|
||||
<li class="list-group-item"><span class="stat-label">Allowed Cores</span> {{printMask .Spec.Cpu.Mask .MachineInfo.NumCores}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
{{if .MemoryAvailable}}
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item active isolation-title panel-title">Memory</li>
|
||||
{{if .Spec.Memory.Reservation}}
|
||||
<li class="list-group-item"><span class="stat-label">Reservation</span> {{printSize .Spec.Memory.Reservation}} <span class="unit-label">{{printUnit .Spec.Memory.Reservation}}</span></li>
|
||||
{{end}}
|
||||
{{if .Spec.Memory.Limit}}
|
||||
<li class="list-group-item"><span class="stat-label">Limit</span> {{printSize .Spec.Memory.Limit}} <span class="unit-label">{{printUnit .Spec.Memory.Limit}}</span></li>
|
||||
{{end}}
|
||||
{{if .Spec.Memory.SwapLimit}}
|
||||
<li class="list-group-item"><span class="stat-label">Swap Limit</span> {{printSize .Spec.Memory.SwapLimit}} <span class="unit-label">{{printUnit .Spec.Memory.SwapLimit}}</span></li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h3>Usage</h3>
|
||||
</div>
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Overview</h3>
|
||||
</div>
|
||||
<div id="usage-gauge" class="panel-body"></div>
|
||||
</div>
|
||||
{{if .CpuAvailable}}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">CPU</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<h4>Total Usage</h4>
|
||||
<div id="cpu-total-usage-chart"></div>
|
||||
<!-- <h4>CPU Load Average</h4>
|
||||
<div id="cpu-load-chart"></div> -->
|
||||
<h4>Usage per Core</h4>
|
||||
<div id="cpu-per-core-usage-chart"></div>
|
||||
<h4>Usage Breakdown</h4>
|
||||
<div id="cpu-usage-breakdown-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .MemoryAvailable}}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Memory</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<h4>Total Usage</h4>
|
||||
<div id="memory-usage-chart"></div>
|
||||
<br/>
|
||||
<div class="row col-sm-12">
|
||||
<h4>Usage Breakdown</h4>
|
||||
<div class="col-sm-9">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-danger" id="progress-hot-memory">
|
||||
<span class="sr-only">Hot Memory</span>
|
||||
</div>
|
||||
<div class="progress-bar progress-bar-info" id="progress-cold-memory">
|
||||
<span class="sr-only">Cold Memory</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3" id="memory-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .NetworkAvailable}}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Network</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<h4>Throughput</h4>
|
||||
<div id="network-bytes-chart"></div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<h4>Errors</h4>
|
||||
<div id="network-errors-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .FsAvailable}}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Filesystem</h3>
|
||||
</div>
|
||||
<div id="filesystem-usage" class="panel-body">
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
startPage({{.ContainerName}}, {{.CpuAvailable}}, {{.MemoryAvailable}});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
116
Godeps/_workspace/src/github.com/google/cadvisor/pages/docker.go
generated
vendored
Normal file
116
Godeps/_workspace/src/github.com/google/cadvisor/pages/docker.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// 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"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/google/cadvisor/container/docker"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/manager"
|
||||
)
|
||||
|
||||
const DockerPage = "/docker/"
|
||||
|
||||
func serveDockerPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
|
||||
start := time.Now()
|
||||
|
||||
// The container name is the path after the handler
|
||||
containerName := u.Path[len(DockerPage):]
|
||||
|
||||
var data *pageData
|
||||
if containerName == "" {
|
||||
// Get the containers.
|
||||
reqParams := info.ContainerInfoRequest{
|
||||
NumStats: 0,
|
||||
}
|
||||
conts, err := m.AllDockerContainers(&reqParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get container %q with error: %v", containerName, err)
|
||||
}
|
||||
subcontainers := make([]link, 0, len(conts))
|
||||
for _, cont := range conts {
|
||||
subcontainers = append(subcontainers, link{
|
||||
Text: getContainerDisplayName(cont.ContainerReference),
|
||||
Link: path.Join("/docker", docker.ContainerNameToDockerId(cont.ContainerReference.Name)),
|
||||
})
|
||||
}
|
||||
|
||||
dockerContainersText := "Docker Containers"
|
||||
data = &pageData{
|
||||
DisplayName: dockerContainersText,
|
||||
ParentContainers: []link{
|
||||
{
|
||||
Text: dockerContainersText,
|
||||
Link: DockerPage,
|
||||
}},
|
||||
Subcontainers: subcontainers,
|
||||
}
|
||||
} else {
|
||||
// Get the container.
|
||||
reqParams := info.ContainerInfoRequest{
|
||||
NumStats: 60,
|
||||
}
|
||||
cont, err := m.DockerContainer(containerName, &reqParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get container %q with error: %v", containerName, err)
|
||||
}
|
||||
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: DockerPage,
|
||||
})
|
||||
parentContainers = append(parentContainers, link{
|
||||
Text: displayName,
|
||||
Link: path.Join(DockerPage, docker.ContainerNameToDockerId(cont.Name)),
|
||||
})
|
||||
|
||||
// Get the MachineInfo
|
||||
machineInfo, err := m.GetMachineInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data = &pageData{
|
||||
DisplayName: displayName,
|
||||
ContainerName: 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,
|
||||
}
|
||||
}
|
||||
|
||||
err := pageTemplate.Execute(w, data)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to apply template: %s", err)
|
||||
}
|
||||
|
||||
glog.V(1).Infof("Request took %s", time.Since(start))
|
||||
return nil
|
||||
}
|
152
Godeps/_workspace/src/github.com/google/cadvisor/pages/pages.go
generated
vendored
Normal file
152
Godeps/_workspace/src/github.com/google/cadvisor/pages/pages.go
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
// 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"
|
||||
"strings"
|
||||
|
||||
auth "github.com/abbot/go-http-auth"
|
||||
"github.com/golang/glog"
|
||||
httpMux "github.com/google/cadvisor/http/mux"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/manager"
|
||||
)
|
||||
|
||||
var pageTemplate *template.Template
|
||||
|
||||
type link struct {
|
||||
// Text to show in the link.
|
||||
Text string
|
||||
|
||||
// Web address to link to.
|
||||
Link 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
|
||||
}
|
||||
|
||||
func init() {
|
||||
pageTemplate = template.New("containersTemplate").Funcs(funcMap)
|
||||
_, err := pageTemplate.Parse(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) {
|
||||
err := serveContainersPage(containerManager, w, r.URL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func containerHandler(containerManager manager.Manager) auth.AuthenticatedHandlerFunc {
|
||||
return func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
|
||||
err := serveContainersPage(containerManager, w, r.URL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dockerHandlerNoAuth(containerManager manager.Manager) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
err := serveDockerPage(containerManager, w, r.URL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dockerHandler(containerManager manager.Manager) auth.AuthenticatedHandlerFunc {
|
||||
return func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
|
||||
err := serveDockerPage(containerManager, w, r.URL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
25
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/bootstrap_min_css.go
generated
vendored
Normal file
25
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/bootstrap_min_css.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
24
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/bootstrap_min_js.go
generated
vendored
Normal file
24
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/bootstrap_min_js.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
25
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/bootstrap_theme_min_css.go
generated
vendored
Normal file
25
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/bootstrap_theme_min_css.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
50
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_css.go
generated
vendored
Normal file
50
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_css.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1918
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_js.go
generated
vendored
Normal file
1918
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_js.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
57
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/google_jsapi_js.go
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/google_jsapi_js.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
24
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/jquery_min_js.go
generated
vendored
Normal file
24
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/jquery_min_js.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
51
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/static.go
generated
vendored
Normal file
51
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/static.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const StaticResource = "/static/"
|
||||
|
||||
var staticFiles = map[string]string{
|
||||
"containers.css": containersCss,
|
||||
"containers.js": containersJs,
|
||||
"bootstrap-3.1.1.min.css": bootstrapCss,
|
||||
"bootstrap-theme-3.1.1.min.css": bootstrapThemeCss,
|
||||
"jquery-1.10.2.min.js": jqueryJs,
|
||||
"bootstrap-3.1.1.min.js": bootstrapJs,
|
||||
"google-jsapi.js": googleJsapiJs,
|
||||
}
|
||||
|
||||
func HandleRequest(w http.ResponseWriter, u *url.URL) error {
|
||||
if len(u.Path) <= len(StaticResource) {
|
||||
return fmt.Errorf("unknown static resource %q", u.Path)
|
||||
}
|
||||
|
||||
// Get the static content if it exists.
|
||||
resource := u.Path[len(StaticResource):]
|
||||
content, ok := staticFiles[resource]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown static resource %q", resource)
|
||||
}
|
||||
|
||||
_, err := w.Write([]byte(content))
|
||||
return err
|
||||
}
|
318
Godeps/_workspace/src/github.com/google/cadvisor/validate/validate.go
generated
vendored
Normal file
318
Godeps/_workspace/src/github.com/google/cadvisor/validate/validate.go
generated
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
// 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"
|
||||
"github.com/google/cadvisor/manager"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
dclient "github.com/fsouza/go-dockerclient"
|
||||
"github.com/google/cadvisor/container/docker"
|
||||
"github.com/google/cadvisor/utils"
|
||||
)
|
||||
|
||||
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 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)
|
||||
return Recommended, out
|
||||
}
|
||||
|
||||
func validateDockerInfo() (string, string) {
|
||||
client, err := dclient.NewClient(*docker.ArgDockerEndpoint)
|
||||
if err == nil {
|
||||
info, err := client.Info()
|
||||
if err == nil {
|
||||
execDriver := info.Get("ExecutionDriver")
|
||||
storageDriver := info.Get("Driver")
|
||||
desc := fmt.Sprintf("Docker exec driver is %s. Storage driver is %s.\n", execDriver, storageDriver)
|
||||
if docker.UseSystemd() {
|
||||
desc += "\tsystemd is being used to create cgroups.\n"
|
||||
} else {
|
||||
desc += "\tCgroups are being created through cgroup filesystem.\n"
|
||||
}
|
||||
if strings.Contains(execDriver, "native") {
|
||||
stateFile := docker.DockerStateDir()
|
||||
if !utils.FileExists(stateFile) {
|
||||
desc += fmt.Sprintf("\tDocker container state directory %q is not accessible.\n", stateFile)
|
||||
return Unsupported, desc
|
||||
}
|
||||
desc += fmt.Sprintf("\tDocker container state directory is at %q and is accessible.\n", stateFile)
|
||||
return Recommended, desc
|
||||
} else if strings.Contains(execDriver, "lxc") {
|
||||
return Supported, desc
|
||||
}
|
||||
return Unknown, desc
|
||||
}
|
||||
}
|
||||
return Unknown, "Docker remote API not reachable\n\t"
|
||||
}
|
||||
|
||||
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)
|
||||
_, err = w.Write([]byte(out))
|
||||
return err
|
||||
}
|
575
Godeps/_workspace/src/github.com/mitchellh/goamz/elb/elb.go
generated
vendored
575
Godeps/_workspace/src/github.com/mitchellh/goamz/elb/elb.go
generated
vendored
@@ -1,575 +0,0 @@
|
||||
// The elb package provides types and functions for interaction with the AWS
|
||||
// Elastic Load Balancing service (ELB)
|
||||
package elb
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/goamz/aws"
|
||||
)
|
||||
|
||||
// The ELB type encapsulates operations operations with the elb endpoint.
|
||||
type ELB struct {
|
||||
aws.Auth
|
||||
aws.Region
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
const APIVersion = "2012-06-01"
|
||||
|
||||
// New creates a new ELB instance.
|
||||
func New(auth aws.Auth, region aws.Region) *ELB {
|
||||
return NewWithClient(auth, region, aws.RetryingClient)
|
||||
}
|
||||
|
||||
func NewWithClient(auth aws.Auth, region aws.Region, httpClient *http.Client) *ELB {
|
||||
return &ELB{auth, region, httpClient}
|
||||
}
|
||||
|
||||
func (elb *ELB) query(params map[string]string, resp interface{}) error {
|
||||
params["Version"] = APIVersion
|
||||
params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339)
|
||||
|
||||
endpoint, err := url.Parse(elb.Region.ELBEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sign(elb.Auth, "GET", "/", params, endpoint.Host)
|
||||
endpoint.RawQuery = multimap(params).Encode()
|
||||
r, err := elb.httpClient.Get(endpoint.String())
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
if r.StatusCode > 200 {
|
||||
return buildError(r)
|
||||
}
|
||||
|
||||
decoder := xml.NewDecoder(r.Body)
|
||||
decodedBody := decoder.Decode(resp)
|
||||
|
||||
return decodedBody
|
||||
}
|
||||
|
||||
func buildError(r *http.Response) error {
|
||||
var (
|
||||
err Error
|
||||
errors xmlErrors
|
||||
)
|
||||
xml.NewDecoder(r.Body).Decode(&errors)
|
||||
if len(errors.Errors) > 0 {
|
||||
err = errors.Errors[0]
|
||||
}
|
||||
err.StatusCode = r.StatusCode
|
||||
if err.Message == "" {
|
||||
err.Message = r.Status
|
||||
}
|
||||
return &err
|
||||
}
|
||||
|
||||
func multimap(p map[string]string) url.Values {
|
||||
q := make(url.Values, len(p))
|
||||
for k, v := range p {
|
||||
q[k] = []string{v}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func makeParams(action string) map[string]string {
|
||||
params := make(map[string]string)
|
||||
params["Action"] = action
|
||||
return params
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ELB objects
|
||||
|
||||
// A listener attaches to an elb
|
||||
type Listener struct {
|
||||
InstancePort int64 `xml:"Listener>InstancePort"`
|
||||
InstanceProtocol string `xml:"Listener>InstanceProtocol"`
|
||||
SSLCertificateId string `xml:"Listener>SSLCertificateId"`
|
||||
LoadBalancerPort int64 `xml:"Listener>LoadBalancerPort"`
|
||||
Protocol string `xml:"Listener>Protocol"`
|
||||
}
|
||||
|
||||
// An Instance attaches to an elb
|
||||
type Instance struct {
|
||||
InstanceId string `xml:"InstanceId"`
|
||||
}
|
||||
|
||||
// A tag attached to an elb
|
||||
type Tag struct {
|
||||
Key string `xml:"Key"`
|
||||
Value string `xml:"Value"`
|
||||
}
|
||||
|
||||
// An InstanceState from an elb health query
|
||||
type InstanceState struct {
|
||||
InstanceId string `xml:"InstanceId"`
|
||||
Description string `xml:"Description"`
|
||||
State string `xml:"State"`
|
||||
ReasonCode string `xml:"ReasonCode"`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// AddTags
|
||||
|
||||
type AddTags struct {
|
||||
LoadBalancerNames []string
|
||||
Tags []Tag
|
||||
}
|
||||
|
||||
type AddTagsResp struct {
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
func (elb *ELB) AddTags(options *AddTags) (resp *AddTagsResp, err error) {
|
||||
params := makeParams("AddTags")
|
||||
|
||||
for i, v := range options.LoadBalancerNames {
|
||||
params["LoadBalancerNames.member."+strconv.Itoa(i+1)] = v
|
||||
}
|
||||
|
||||
for i, v := range options.Tags {
|
||||
params["Tags.member."+strconv.Itoa(i+1)+".Key"] = v.Key
|
||||
params["Tags.member."+strconv.Itoa(i+1)+".Value"] = v.Value
|
||||
}
|
||||
|
||||
resp = &AddTagsResp{}
|
||||
|
||||
err = elb.query(params, resp)
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RemoveTags
|
||||
|
||||
type RemoveTags struct {
|
||||
LoadBalancerNames []string
|
||||
TagKeys []string
|
||||
}
|
||||
|
||||
type RemoveTagsResp struct {
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
func (elb *ELB) RemoveTags(options *RemoveTags) (resp *RemoveTagsResp, err error) {
|
||||
params := makeParams("RemoveTags")
|
||||
|
||||
for i, v := range options.LoadBalancerNames {
|
||||
params["LoadBalancerNames.member."+strconv.Itoa(i+1)] = v
|
||||
}
|
||||
|
||||
for i, v := range options.TagKeys {
|
||||
params["Tags.member."+strconv.Itoa(i+1)+".Key"] = v
|
||||
}
|
||||
|
||||
resp = &RemoveTagsResp{}
|
||||
|
||||
err = elb.query(params, resp)
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Create
|
||||
|
||||
// The CreateLoadBalancer request parameters
|
||||
type CreateLoadBalancer struct {
|
||||
AvailZone []string
|
||||
Listeners []Listener
|
||||
LoadBalancerName string
|
||||
Internal bool // true for vpc elbs
|
||||
SecurityGroups []string
|
||||
Subnets []string
|
||||
Tags []Tag
|
||||
}
|
||||
|
||||
type CreateLoadBalancerResp struct {
|
||||
DNSName string `xml:"CreateLoadBalancerResult>DNSName"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
func (elb *ELB) CreateLoadBalancer(options *CreateLoadBalancer) (resp *CreateLoadBalancerResp, err error) {
|
||||
params := makeParams("CreateLoadBalancer")
|
||||
|
||||
params["LoadBalancerName"] = options.LoadBalancerName
|
||||
|
||||
for i, v := range options.AvailZone {
|
||||
params["AvailabilityZones.member."+strconv.Itoa(i+1)] = v
|
||||
}
|
||||
|
||||
for i, v := range options.SecurityGroups {
|
||||
params["SecurityGroups.member."+strconv.Itoa(i+1)] = v
|
||||
}
|
||||
|
||||
for i, v := range options.Subnets {
|
||||
params["Subnets.member."+strconv.Itoa(i+1)] = v
|
||||
}
|
||||
|
||||
for i, v := range options.Listeners {
|
||||
params["Listeners.member."+strconv.Itoa(i+1)+".LoadBalancerPort"] = strconv.FormatInt(v.LoadBalancerPort, 10)
|
||||
params["Listeners.member."+strconv.Itoa(i+1)+".InstancePort"] = strconv.FormatInt(v.InstancePort, 10)
|
||||
params["Listeners.member."+strconv.Itoa(i+1)+".Protocol"] = v.Protocol
|
||||
params["Listeners.member."+strconv.Itoa(i+1)+".InstanceProtocol"] = v.InstanceProtocol
|
||||
params["Listeners.member."+strconv.Itoa(i+1)+".SSLCertificateId"] = v.SSLCertificateId
|
||||
}
|
||||
|
||||
for i, v := range options.Tags {
|
||||
params["Tags.member."+strconv.Itoa(i+1)+".Key"] = v.Key
|
||||
params["Tags.member."+strconv.Itoa(i+1)+".Value"] = v.Value
|
||||
}
|
||||
|
||||
if options.Internal {
|
||||
params["Scheme"] = "internal"
|
||||
}
|
||||
|
||||
resp = &CreateLoadBalancerResp{}
|
||||
|
||||
err = elb.query(params, resp)
|
||||
|
||||
if err != nil {
|
||||
resp = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Destroy
|
||||
|
||||
// The DestroyLoadBalancer request parameters
|
||||
type DeleteLoadBalancer struct {
|
||||
LoadBalancerName string
|
||||
}
|
||||
|
||||
func (elb *ELB) DeleteLoadBalancer(options *DeleteLoadBalancer) (resp *SimpleResp, err error) {
|
||||
params := makeParams("DeleteLoadBalancer")
|
||||
|
||||
params["LoadBalancerName"] = options.LoadBalancerName
|
||||
|
||||
resp = &SimpleResp{}
|
||||
|
||||
err = elb.query(params, resp)
|
||||
|
||||
if err != nil {
|
||||
resp = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Describe
|
||||
|
||||
// An individual load balancer
|
||||
type LoadBalancer struct {
|
||||
LoadBalancerName string `xml:"LoadBalancerName"`
|
||||
Listeners []Listener `xml:"ListenerDescriptions>member"`
|
||||
Instances []Instance `xml:"Instances>member"`
|
||||
HealthCheck HealthCheck `xml:"HealthCheck"`
|
||||
AvailabilityZones []string `xml:"AvailabilityZones>member"`
|
||||
HostedZoneNameID string `xml:"CanonicalHostedZoneNameID"`
|
||||
DNSName string `xml:"DNSName"`
|
||||
SecurityGroups []string `xml:"SecurityGroups>member"`
|
||||
Scheme string `xml:"Scheme"`
|
||||
Subnets []string `xml:"Subnets>member"`
|
||||
}
|
||||
|
||||
// DescribeLoadBalancer request params
|
||||
type DescribeLoadBalancer struct {
|
||||
Names []string
|
||||
}
|
||||
|
||||
type DescribeLoadBalancersResp struct {
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
LoadBalancers []LoadBalancer `xml:"DescribeLoadBalancersResult>LoadBalancerDescriptions>member"`
|
||||
}
|
||||
|
||||
func (elb *ELB) DescribeLoadBalancers(options *DescribeLoadBalancer) (resp *DescribeLoadBalancersResp, err error) {
|
||||
params := makeParams("DescribeLoadBalancers")
|
||||
|
||||
for i, v := range options.Names {
|
||||
params["LoadBalancerNames.member."+strconv.Itoa(i+1)] = v
|
||||
}
|
||||
|
||||
resp = &DescribeLoadBalancersResp{}
|
||||
|
||||
err = elb.query(params, resp)
|
||||
|
||||
if err != nil {
|
||||
resp = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Attributes
|
||||
|
||||
type AccessLog struct {
|
||||
EmitInterval int64
|
||||
Enabled bool
|
||||
S3BucketName string
|
||||
S3BucketPrefix string
|
||||
}
|
||||
|
||||
type ConnectionDraining struct {
|
||||
Enabled bool
|
||||
Timeout int64
|
||||
}
|
||||
|
||||
type LoadBalancerAttributes struct {
|
||||
CrossZoneLoadBalancingEnabled bool
|
||||
ConnectionSettingsIdleTimeout int64
|
||||
ConnectionDraining ConnectionDraining
|
||||
AccessLog AccessLog
|
||||
}
|
||||
|
||||
type ModifyLoadBalancerAttributes struct {
|
||||
LoadBalancerName string
|
||||
LoadBalancerAttributes LoadBalancerAttributes
|
||||
}
|
||||
|
||||
func (elb *ELB) ModifyLoadBalancerAttributes(options *ModifyLoadBalancerAttributes) (resp *SimpleResp, err error) {
|
||||
params := makeParams("ModifyLoadBalancerAttributes")
|
||||
|
||||
params["LoadBalancerName"] = options.LoadBalancerName
|
||||
params["LoadBalancerAttributes.CrossZoneLoadBalancing.Enabled"] = strconv.FormatBool(options.LoadBalancerAttributes.CrossZoneLoadBalancingEnabled)
|
||||
if options.LoadBalancerAttributes.ConnectionSettingsIdleTimeout > 0 {
|
||||
params["LoadBalancerAttributes.ConnectionSettings.IdleTimeout"] = strconv.Itoa(int(options.LoadBalancerAttributes.ConnectionSettingsIdleTimeout))
|
||||
}
|
||||
if options.LoadBalancerAttributes.ConnectionDraining.Timeout > 0 {
|
||||
params["LoadBalancerAttributes.ConnectionDraining.Timeout"] = strconv.Itoa(int(options.LoadBalancerAttributes.ConnectionDraining.Timeout))
|
||||
}
|
||||
params["LoadBalancerAttributes.ConnectionDraining.Enabled"] = strconv.FormatBool(options.LoadBalancerAttributes.ConnectionDraining.Enabled)
|
||||
params["LoadBalancerAttributes.AccessLog.Enabled"] = strconv.FormatBool(options.LoadBalancerAttributes.AccessLog.Enabled)
|
||||
if options.LoadBalancerAttributes.AccessLog.Enabled {
|
||||
params["LoadBalancerAttributes.AccessLog.EmitInterval"] = strconv.Itoa(int(options.LoadBalancerAttributes.AccessLog.EmitInterval))
|
||||
params["LoadBalancerAttributes.AccessLog.S3BucketName"] = options.LoadBalancerAttributes.AccessLog.S3BucketName
|
||||
params["LoadBalancerAttributes.AccessLog.S3BucketPrefix"] = options.LoadBalancerAttributes.AccessLog.S3BucketPrefix
|
||||
}
|
||||
|
||||
resp = &SimpleResp{}
|
||||
|
||||
err = elb.query(params, resp)
|
||||
|
||||
if err != nil {
|
||||
resp = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Instance Registration / deregistration
|
||||
|
||||
// The RegisterInstancesWithLoadBalancer request parameters
|
||||
type RegisterInstancesWithLoadBalancer struct {
|
||||
LoadBalancerName string
|
||||
Instances []string
|
||||
}
|
||||
|
||||
type RegisterInstancesWithLoadBalancerResp struct {
|
||||
Instances []Instance `xml:"RegisterInstancesWithLoadBalancerResult>Instances>member"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
func (elb *ELB) RegisterInstancesWithLoadBalancer(options *RegisterInstancesWithLoadBalancer) (resp *RegisterInstancesWithLoadBalancerResp, err error) {
|
||||
params := makeParams("RegisterInstancesWithLoadBalancer")
|
||||
|
||||
params["LoadBalancerName"] = options.LoadBalancerName
|
||||
|
||||
for i, v := range options.Instances {
|
||||
params["Instances.member."+strconv.Itoa(i+1)+".InstanceId"] = v
|
||||
}
|
||||
|
||||
resp = &RegisterInstancesWithLoadBalancerResp{}
|
||||
|
||||
err = elb.query(params, resp)
|
||||
|
||||
if err != nil {
|
||||
resp = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// The DeregisterInstancesFromLoadBalancer request parameters
|
||||
type DeregisterInstancesFromLoadBalancer struct {
|
||||
LoadBalancerName string
|
||||
Instances []string
|
||||
}
|
||||
|
||||
type DeregisterInstancesFromLoadBalancerResp struct {
|
||||
Instances []Instance `xml:"DeregisterInstancesFromLoadBalancerResult>Instances>member"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
func (elb *ELB) DeregisterInstancesFromLoadBalancer(options *DeregisterInstancesFromLoadBalancer) (resp *DeregisterInstancesFromLoadBalancerResp, err error) {
|
||||
params := makeParams("DeregisterInstancesFromLoadBalancer")
|
||||
|
||||
params["LoadBalancerName"] = options.LoadBalancerName
|
||||
|
||||
for i, v := range options.Instances {
|
||||
params["Instances.member."+strconv.Itoa(i+1)+".InstanceId"] = v
|
||||
}
|
||||
|
||||
resp = &DeregisterInstancesFromLoadBalancerResp{}
|
||||
|
||||
err = elb.query(params, resp)
|
||||
|
||||
if err != nil {
|
||||
resp = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DescribeTags
|
||||
|
||||
type DescribeTags struct {
|
||||
LoadBalancerNames []string
|
||||
}
|
||||
|
||||
type LoadBalancerTag struct {
|
||||
Tags []Tag `xml:"Tags>member"`
|
||||
LoadBalancerName string `xml:"LoadBalancerName"`
|
||||
}
|
||||
|
||||
type DescribeTagsResp struct {
|
||||
LoadBalancerTags []LoadBalancerTag `xml:"DescribeTagsResult>TagDescriptions>member"`
|
||||
NextToken string `xml:"DescribeTagsResult>NextToken"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
func (elb *ELB) DescribeTags(options *DescribeTags) (resp *DescribeTagsResp, err error) {
|
||||
params := makeParams("DescribeTags")
|
||||
|
||||
for i, v := range options.LoadBalancerNames {
|
||||
params["LoadBalancerNames.member."+strconv.Itoa(i+1)] = v
|
||||
}
|
||||
|
||||
resp = &DescribeTagsResp{}
|
||||
|
||||
err = elb.query(params, resp)
|
||||
|
||||
if err != nil {
|
||||
resp = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Health Checks
|
||||
|
||||
type HealthCheck struct {
|
||||
HealthyThreshold int64 `xml:"HealthyThreshold"`
|
||||
UnhealthyThreshold int64 `xml:"UnhealthyThreshold"`
|
||||
Interval int64 `xml:"Interval"`
|
||||
Target string `xml:"Target"`
|
||||
Timeout int64 `xml:"Timeout"`
|
||||
}
|
||||
|
||||
type ConfigureHealthCheck struct {
|
||||
LoadBalancerName string
|
||||
Check HealthCheck
|
||||
}
|
||||
|
||||
type ConfigureHealthCheckResp struct {
|
||||
Check HealthCheck `xml:"ConfigureHealthCheckResult>HealthCheck"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
func (elb *ELB) ConfigureHealthCheck(options *ConfigureHealthCheck) (resp *ConfigureHealthCheckResp, err error) {
|
||||
params := makeParams("ConfigureHealthCheck")
|
||||
|
||||
params["LoadBalancerName"] = options.LoadBalancerName
|
||||
params["HealthCheck.HealthyThreshold"] = strconv.Itoa(int(options.Check.HealthyThreshold))
|
||||
params["HealthCheck.UnhealthyThreshold"] = strconv.Itoa(int(options.Check.UnhealthyThreshold))
|
||||
params["HealthCheck.Interval"] = strconv.Itoa(int(options.Check.Interval))
|
||||
params["HealthCheck.Target"] = options.Check.Target
|
||||
params["HealthCheck.Timeout"] = strconv.Itoa(int(options.Check.Timeout))
|
||||
|
||||
resp = &ConfigureHealthCheckResp{}
|
||||
|
||||
err = elb.query(params, resp)
|
||||
|
||||
if err != nil {
|
||||
resp = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Instance Health
|
||||
|
||||
// The DescribeInstanceHealth request parameters
|
||||
type DescribeInstanceHealth struct {
|
||||
LoadBalancerName string
|
||||
}
|
||||
|
||||
type DescribeInstanceHealthResp struct {
|
||||
InstanceStates []InstanceState `xml:"DescribeInstanceHealthResult>InstanceStates>member"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
func (elb *ELB) DescribeInstanceHealth(options *DescribeInstanceHealth) (resp *DescribeInstanceHealthResp, err error) {
|
||||
params := makeParams("DescribeInstanceHealth")
|
||||
|
||||
params["LoadBalancerName"] = options.LoadBalancerName
|
||||
|
||||
resp = &DescribeInstanceHealthResp{}
|
||||
|
||||
err = elb.query(params, resp)
|
||||
|
||||
if err != nil {
|
||||
resp = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Responses
|
||||
|
||||
type SimpleResp struct {
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
type xmlErrors struct {
|
||||
Errors []Error `xml:"Error"`
|
||||
}
|
||||
|
||||
// Error encapsulates an elb error.
|
||||
type Error struct {
|
||||
// HTTP status code of the error.
|
||||
StatusCode int
|
||||
|
||||
// AWS code of the error.
|
||||
Code string
|
||||
|
||||
// Message explaining the error.
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
var prefix string
|
||||
if e.Code != "" {
|
||||
prefix = e.Code + ": "
|
||||
}
|
||||
if prefix == "" && e.StatusCode > 0 {
|
||||
prefix = strconv.Itoa(e.StatusCode) + ": "
|
||||
}
|
||||
return prefix + e.Message
|
||||
}
|
235
Godeps/_workspace/src/github.com/mitchellh/goamz/elb/elb_test.go
generated
vendored
235
Godeps/_workspace/src/github.com/mitchellh/goamz/elb/elb_test.go
generated
vendored
@@ -1,235 +0,0 @@
|
||||
package elb_test
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/goamz/aws"
|
||||
"github.com/mitchellh/goamz/elb"
|
||||
"github.com/mitchellh/goamz/testutil"
|
||||
. "github.com/motain/gocheck"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
|
||||
type S struct {
|
||||
elb *elb.ELB
|
||||
}
|
||||
|
||||
var _ = Suite(&S{})
|
||||
|
||||
var testServer = testutil.NewHTTPServer()
|
||||
|
||||
func (s *S) SetUpSuite(c *C) {
|
||||
testServer.Start()
|
||||
auth := aws.Auth{"abc", "123", ""}
|
||||
s.elb = elb.NewWithClient(auth, aws.Region{ELBEndpoint: testServer.URL}, testutil.DefaultClient)
|
||||
}
|
||||
|
||||
func (s *S) TearDownTest(c *C) {
|
||||
testServer.Flush()
|
||||
}
|
||||
|
||||
func (s *S) TestAddTags(c *C) {
|
||||
testServer.Response(200, nil, AddTagsExample)
|
||||
|
||||
options := elb.AddTags{
|
||||
LoadBalancerNames: []string{"foobar"},
|
||||
Tags: []elb.Tag{
|
||||
{
|
||||
Key: "hello",
|
||||
Value: "world",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := s.elb.AddTags(&options)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"AddTags"})
|
||||
c.Assert(req.Form["LoadBalancerNames.member.1"], DeepEquals, []string{"foobar"})
|
||||
c.Assert(req.Form["Tags.member.1.Key"], DeepEquals, []string{"hello"})
|
||||
c.Assert(req.Form["Tags.member.1.Value"], DeepEquals, []string{"world"})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "360e81f7-1100-11e4-b6ed-0f30EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestRemoveTags(c *C) {
|
||||
testServer.Response(200, nil, RemoveTagsExample)
|
||||
|
||||
options := elb.RemoveTags{
|
||||
LoadBalancerNames: []string{"foobar"},
|
||||
TagKeys: []string{"hello"},
|
||||
}
|
||||
|
||||
resp, err := s.elb.RemoveTags(&options)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"RemoveTags"})
|
||||
c.Assert(req.Form["LoadBalancerNames.member.1"], DeepEquals, []string{"foobar"})
|
||||
c.Assert(req.Form["Tags.member.1.Key"], DeepEquals, []string{"hello"})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestCreateLoadBalancer(c *C) {
|
||||
testServer.Response(200, nil, CreateLoadBalancerExample)
|
||||
|
||||
options := elb.CreateLoadBalancer{
|
||||
AvailZone: []string{"us-east-1a"},
|
||||
Listeners: []elb.Listener{elb.Listener{
|
||||
InstancePort: 80,
|
||||
InstanceProtocol: "http",
|
||||
SSLCertificateId: "needToAddASSLCertToYourAWSAccount",
|
||||
LoadBalancerPort: 80,
|
||||
Protocol: "http",
|
||||
},
|
||||
},
|
||||
LoadBalancerName: "foobar",
|
||||
Internal: false,
|
||||
SecurityGroups: []string{"sg1"},
|
||||
Subnets: []string{"sn1"},
|
||||
}
|
||||
|
||||
resp, err := s.elb.CreateLoadBalancer(&options)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"CreateLoadBalancer"})
|
||||
c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "1549581b-12b7-11e3-895e-1334aEXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDeleteLoadBalancer(c *C) {
|
||||
testServer.Response(200, nil, DeleteLoadBalancerExample)
|
||||
|
||||
options := elb.DeleteLoadBalancer{
|
||||
LoadBalancerName: "foobar",
|
||||
}
|
||||
|
||||
resp, err := s.elb.DeleteLoadBalancer(&options)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"DeleteLoadBalancer"})
|
||||
c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "1549581b-12b7-11e3-895e-1334aEXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeLoadBalancers(c *C) {
|
||||
testServer.Response(200, nil, DescribeLoadBalancersExample)
|
||||
|
||||
options := elb.DescribeLoadBalancer{
|
||||
Names: []string{"foobar"},
|
||||
}
|
||||
|
||||
resp, err := s.elb.DescribeLoadBalancers(&options)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeLoadBalancers"})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12EXAMPLE")
|
||||
c.Assert(resp.LoadBalancers[0].LoadBalancerName, Equals, "MyLoadBalancer")
|
||||
c.Assert(resp.LoadBalancers[0].Listeners[0].Protocol, Equals, "HTTP")
|
||||
c.Assert(resp.LoadBalancers[0].Instances[0].InstanceId, Equals, "i-e4cbe38d")
|
||||
c.Assert(resp.LoadBalancers[0].AvailabilityZones[0].AvailabilityZone, Equals, "us-east-1a")
|
||||
c.Assert(resp.LoadBalancers[0].Scheme, Equals, "internet-facing")
|
||||
c.Assert(resp.LoadBalancers[0].DNSName, Equals, "MyLoadBalancer-123456789.us-east-1.elb.amazonaws.com")
|
||||
c.Assert(resp.LoadBalancers[0].HealthCheck.HealthyThreshold, Equals, int64(2))
|
||||
c.Assert(resp.LoadBalancers[0].HealthCheck.UnhealthyThreshold, Equals, int64(10))
|
||||
c.Assert(resp.LoadBalancers[0].HealthCheck.Interval, Equals, int64(90))
|
||||
c.Assert(resp.LoadBalancers[0].HealthCheck.Target, Equals, "HTTP:80/")
|
||||
c.Assert(resp.LoadBalancers[0].HealthCheck.Timeout, Equals, int64(60))
|
||||
}
|
||||
|
||||
func (s *S) TestRegisterInstancesWithLoadBalancer(c *C) {
|
||||
testServer.Response(200, nil, RegisterInstancesWithLoadBalancerExample)
|
||||
|
||||
options := elb.RegisterInstancesWithLoadBalancer{
|
||||
LoadBalancerName: "foobar",
|
||||
Instances: []string{"instance-1", "instance-2"},
|
||||
}
|
||||
|
||||
resp, err := s.elb.RegisterInstancesWithLoadBalancer(&options)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"RegisterInstancesWithLoadBalancer"})
|
||||
c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"})
|
||||
c.Assert(req.Form["Instances.member.1.InstanceId"], DeepEquals, []string{"instance-1"})
|
||||
c.Assert(req.Form["Instances.member.2.InstanceId"], DeepEquals, []string{"instance-2"})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
c.Assert(resp.Instances[0].InstanceId, Equals, "i-315b7e51")
|
||||
c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDeregisterInstancesFromLoadBalancer(c *C) {
|
||||
testServer.Response(200, nil, DeregisterInstancesFromLoadBalancerExample)
|
||||
|
||||
options := elb.DeregisterInstancesFromLoadBalancer{
|
||||
LoadBalancerName: "foobar",
|
||||
Instances: []string{"instance-1", "instance-2"},
|
||||
}
|
||||
|
||||
resp, err := s.elb.DeregisterInstancesFromLoadBalancer(&options)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"DeregisterInstancesFromLoadBalancer"})
|
||||
c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"})
|
||||
c.Assert(req.Form["Instances.member.1.InstanceId"], DeepEquals, []string{"instance-1"})
|
||||
c.Assert(req.Form["Instances.member.2.InstanceId"], DeepEquals, []string{"instance-2"})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
c.Assert(resp.Instances[0].InstanceId, Equals, "i-6ec63d59")
|
||||
c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestConfigureHealthCheck(c *C) {
|
||||
testServer.Response(200, nil, ConfigureHealthCheckExample)
|
||||
|
||||
options := elb.ConfigureHealthCheck{
|
||||
LoadBalancerName: "foobar",
|
||||
Check: elb.HealthCheck{
|
||||
HealthyThreshold: 2,
|
||||
UnhealthyThreshold: 2,
|
||||
Interval: 30,
|
||||
Target: "HTTP:80/ping",
|
||||
Timeout: 3,
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := s.elb.ConfigureHealthCheck(&options)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"ConfigureHealthCheck"})
|
||||
c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
c.Assert(resp.Check.HealthyThreshold, Equals, int64(2))
|
||||
c.Assert(resp.Check.UnhealthyThreshold, Equals, int64(2))
|
||||
c.Assert(resp.Check.Interval, Equals, int64(30))
|
||||
c.Assert(resp.Check.Target, Equals, "HTTP:80/ping")
|
||||
c.Assert(resp.Check.Timeout, Equals, int64(3))
|
||||
c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeInstanceHealth(c *C) {
|
||||
testServer.Response(200, nil, DescribeInstanceHealthExample)
|
||||
|
||||
options := elb.DescribeInstanceHealth{
|
||||
LoadBalancerName: "foobar",
|
||||
}
|
||||
|
||||
resp, err := s.elb.DescribeInstanceHealth(&options)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeInstanceHealth"})
|
||||
c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
c.Assert(resp.InstanceStates[0].InstanceId, Equals, "i-90d8c2a5")
|
||||
c.Assert(resp.InstanceStates[0].State, Equals, "InService")
|
||||
c.Assert(resp.InstanceStates[1].InstanceId, Equals, "i-06ea3e60")
|
||||
c.Assert(resp.InstanceStates[1].State, Equals, "OutOfService")
|
||||
c.Assert(resp.RequestId, Equals, "1549581b-12b7-11e3-895e-1334aEXAMPLE")
|
||||
}
|
182
Godeps/_workspace/src/github.com/mitchellh/goamz/elb/responses_test.go
generated
vendored
182
Godeps/_workspace/src/github.com/mitchellh/goamz/elb/responses_test.go
generated
vendored
@@ -1,182 +0,0 @@
|
||||
package elb_test
|
||||
|
||||
var ErrorDump = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Response><Errors><Error><Code>UnsupportedOperation</Code>
|
||||
<Message></Message>
|
||||
</Error></Errors><RequestID>0503f4e9-bbd6-483c-b54f-c4ae9f3b30f4</RequestID></Response>
|
||||
`
|
||||
|
||||
// http://goo.gl/OkMdtJ
|
||||
var AddTagsExample = `
|
||||
<AddTagsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<AddTagsResult/>
|
||||
<ResponseMetadata>
|
||||
<RequestId>360e81f7-1100-11e4-b6ed-0f30EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</AddTagsResponse>
|
||||
`
|
||||
|
||||
// http://goo.gl/nT2E89
|
||||
var RemoveTagsExample = `
|
||||
<RemoveTagsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<RemoveTagsResult/>
|
||||
<ResponseMetadata>
|
||||
<RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</RemoveTagsResponse>
|
||||
`
|
||||
|
||||
// http://goo.gl/gQRD2H
|
||||
var CreateLoadBalancerExample = `
|
||||
<CreateLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<CreateLoadBalancerResult>
|
||||
<DNSName>MyLoadBalancer-1234567890.us-east-1.elb.amazonaws.com</DNSName>
|
||||
</CreateLoadBalancerResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateLoadBalancerResponse>
|
||||
`
|
||||
|
||||
// http://goo.gl/GLZeBN
|
||||
var DeleteLoadBalancerExample = `
|
||||
<DeleteLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteLoadBalancerResponse>
|
||||
`
|
||||
|
||||
// http://goo.gl/8UgpQ8
|
||||
var DescribeLoadBalancersExample = `
|
||||
<DescribeLoadBalancersResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<DescribeLoadBalancersResult>
|
||||
<LoadBalancerDescriptions>
|
||||
<member>
|
||||
<SecurityGroups/>
|
||||
<LoadBalancerName>MyLoadBalancer</LoadBalancerName>
|
||||
<CreatedTime>2013-05-24T21:15:31.280Z</CreatedTime>
|
||||
<HealthCheck>
|
||||
<Interval>90</Interval>
|
||||
<Target>HTTP:80/</Target>
|
||||
<HealthyThreshold>2</HealthyThreshold>
|
||||
<Timeout>60</Timeout>
|
||||
<UnhealthyThreshold>10</UnhealthyThreshold>
|
||||
</HealthCheck>
|
||||
<ListenerDescriptions>
|
||||
<member>
|
||||
<PolicyNames/>
|
||||
<Listener>
|
||||
<Protocol>HTTP</Protocol>
|
||||
<LoadBalancerPort>80</LoadBalancerPort>
|
||||
<InstanceProtocol>HTTP</InstanceProtocol>
|
||||
<SSLCertificateId>needToAddASSLCertToYourAWSAccount</SSLCertificateId>
|
||||
<InstancePort>80</InstancePort>
|
||||
</Listener>
|
||||
</member>
|
||||
</ListenerDescriptions>
|
||||
<Instances>
|
||||
<member>
|
||||
<InstanceId>i-e4cbe38d</InstanceId>
|
||||
</member>
|
||||
</Instances>
|
||||
<Policies>
|
||||
<AppCookieStickinessPolicies/>
|
||||
<OtherPolicies/>
|
||||
<LBCookieStickinessPolicies/>
|
||||
</Policies>
|
||||
<AvailabilityZones>
|
||||
<member>us-east-1a</member>
|
||||
</AvailabilityZones>
|
||||
<CanonicalHostedZoneNameID>ZZZZZZZZZZZ123X</CanonicalHostedZoneNameID>
|
||||
<CanonicalHostedZoneName>MyLoadBalancer-123456789.us-east-1.elb.amazonaws.com</CanonicalHostedZoneName>
|
||||
<Scheme>internet-facing</Scheme>
|
||||
<SourceSecurityGroup>
|
||||
<OwnerAlias>amazon-elb</OwnerAlias>
|
||||
<GroupName>amazon-elb-sg</GroupName>
|
||||
</SourceSecurityGroup>
|
||||
<DNSName>MyLoadBalancer-123456789.us-east-1.elb.amazonaws.com</DNSName>
|
||||
<BackendServerDescriptions/>
|
||||
<Subnets/>
|
||||
</member>
|
||||
</LoadBalancerDescriptions>
|
||||
</DescribeLoadBalancersResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeLoadBalancersResponse>
|
||||
`
|
||||
|
||||
// http://goo.gl/Uz1N66
|
||||
var RegisterInstancesWithLoadBalancerExample = `
|
||||
<RegisterInstancesWithLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<RegisterInstancesWithLoadBalancerResult>
|
||||
<Instances>
|
||||
<member>
|
||||
<InstanceId>i-315b7e51</InstanceId>
|
||||
</member>
|
||||
</Instances>
|
||||
</RegisterInstancesWithLoadBalancerResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</RegisterInstancesWithLoadBalancerResponse>
|
||||
`
|
||||
|
||||
// http://goo.gl/5OMv62
|
||||
var DeregisterInstancesFromLoadBalancerExample = `
|
||||
<DeregisterInstancesFromLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<DeregisterInstancesFromLoadBalancerResult>
|
||||
<Instances>
|
||||
<member>
|
||||
<InstanceId>i-6ec63d59</InstanceId>
|
||||
</member>
|
||||
</Instances>
|
||||
</DeregisterInstancesFromLoadBalancerResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeregisterInstancesFromLoadBalancerResponse>
|
||||
`
|
||||
|
||||
// http://docs.aws.amazon.com/ElasticLoadBalancing/latest/APIReference/API_ConfigureHealthCheck.html
|
||||
var ConfigureHealthCheckExample = `
|
||||
<ConfigureHealthCheckResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<ConfigureHealthCheckResult>
|
||||
<HealthCheck>
|
||||
<Interval>30</Interval>
|
||||
<Target>HTTP:80/ping</Target>
|
||||
<HealthyThreshold>2</HealthyThreshold>
|
||||
<Timeout>3</Timeout>
|
||||
<UnhealthyThreshold>2</UnhealthyThreshold>
|
||||
</HealthCheck>
|
||||
</ConfigureHealthCheckResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>83c88b9d-12b7-11e3-8b82-87b12EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ConfigureHealthCheckResponse>`
|
||||
|
||||
// http://goo.gl/cGNxfj
|
||||
var DescribeInstanceHealthExample = `
|
||||
<DescribeInstanceHealthResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<DescribeInstanceHealthResult>
|
||||
<InstanceStates>
|
||||
<member>
|
||||
<Description>N/A</Description>
|
||||
<InstanceId>i-90d8c2a5</InstanceId>
|
||||
<State>InService</State>
|
||||
<ReasonCode>N/A</ReasonCode>
|
||||
</member>
|
||||
<member>
|
||||
<Description>N/A</Description>
|
||||
<InstanceId>i-06ea3e60</InstanceId>
|
||||
<State>OutOfService</State>
|
||||
<ReasonCode>N/A</ReasonCode>
|
||||
</member>
|
||||
</InstanceStates>
|
||||
</DescribeInstanceHealthResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeInstanceHealthResponse>`
|
38
Godeps/_workspace/src/github.com/mitchellh/goamz/elb/sign.go
generated
vendored
38
Godeps/_workspace/src/github.com/mitchellh/goamz/elb/sign.go
generated
vendored
@@ -1,38 +0,0 @@
|
||||
package elb
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"github.com/mitchellh/goamz/aws"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Version 2 signing (http://goo.gl/RSRp5)
|
||||
|
||||
var b64 = base64.StdEncoding
|
||||
|
||||
func sign(auth aws.Auth, method, path string, params map[string]string, host string) {
|
||||
params["AWSAccessKeyId"] = auth.AccessKey
|
||||
params["SignatureVersion"] = "2"
|
||||
params["SignatureMethod"] = "HmacSHA256"
|
||||
if auth.Token != "" {
|
||||
params["SecurityToken"] = auth.Token
|
||||
}
|
||||
|
||||
var sarray []string
|
||||
for k, v := range params {
|
||||
sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v))
|
||||
}
|
||||
sort.StringSlice(sarray).Sort()
|
||||
joined := strings.Join(sarray, "&")
|
||||
payload := method + "\n" + host + "\n" + path + "\n" + joined
|
||||
hash := hmac.New(sha256.New, []byte(auth.SecretKey))
|
||||
hash.Write([]byte(payload))
|
||||
signature := make([]byte, b64.EncodedLen(hash.Size()))
|
||||
b64.Encode(signature, hash.Sum(nil))
|
||||
|
||||
params["Signature"] = string(signature)
|
||||
}
|
Reference in New Issue
Block a user