mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
Merge pull request #12770 from bparees/master
bump(fsouza/go-dockerclient): 42d06e2b125654477366c320dcea99107a86e9c2
This commit is contained in:
commit
03d961a8b3
2
Godeps/Godeps.json
generated
2
Godeps/Godeps.json
generated
@ -232,7 +232,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/fsouza/go-dockerclient",
|
"ImportPath": "github.com/fsouza/go-dockerclient",
|
||||||
"Rev": "933433faa3e1c0bbc825b251143f8e77affbf797"
|
"Rev": "42d06e2b125654477366c320dcea99107a86e9c2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/garyburd/redigo/internal",
|
"ImportPath": "github.com/garyburd/redigo/internal",
|
||||||
|
5
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS
generated
vendored
5
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS
generated
vendored
@ -8,8 +8,9 @@ Artem Sidorenko <artem@2realities.com>
|
|||||||
Andy Goldstein <andy.goldstein@redhat.com>
|
Andy Goldstein <andy.goldstein@redhat.com>
|
||||||
Ben Marini <ben@remind101.com>
|
Ben Marini <ben@remind101.com>
|
||||||
Ben McCann <benmccann.com>
|
Ben McCann <benmccann.com>
|
||||||
Brian Lalor <blalor@bravo5.org>
|
|
||||||
Brendan Fosberry <brendan@codeship.com>
|
Brendan Fosberry <brendan@codeship.com>
|
||||||
|
Brian Lalor <blalor@bravo5.org>
|
||||||
|
Brian Palmer <brianp@instructure.com>
|
||||||
Burke Libbey <burke@libbey.me>
|
Burke Libbey <burke@libbey.me>
|
||||||
Carlos Diaz-Padron <cpadron@mozilla.com>
|
Carlos Diaz-Padron <cpadron@mozilla.com>
|
||||||
Cezar Sa Espinola <cezar.sa@corp.globo.com>
|
Cezar Sa Espinola <cezar.sa@corp.globo.com>
|
||||||
@ -17,6 +18,7 @@ Cheah Chu Yeow <chuyeow@gmail.com>
|
|||||||
cheneydeng <cheneydeng@qq.com>
|
cheneydeng <cheneydeng@qq.com>
|
||||||
CMGS <ilskdw@gmail.com>
|
CMGS <ilskdw@gmail.com>
|
||||||
Craig Jellick <craig@rancher.com>
|
Craig Jellick <craig@rancher.com>
|
||||||
|
Dan Williams <dcbw@redhat.com>
|
||||||
Daniel, Dao Quang Minh <dqminh89@gmail.com>
|
Daniel, Dao Quang Minh <dqminh89@gmail.com>
|
||||||
Daniel Garcia <daniel@danielgarcia.info>
|
Daniel Garcia <daniel@danielgarcia.info>
|
||||||
Darren Shepherd <darren@rancher.com>
|
Darren Shepherd <darren@rancher.com>
|
||||||
@ -80,6 +82,7 @@ Sunjin Lee <styner32@gmail.com>
|
|||||||
Tarsis Azevedo <tarsis@corp.globo.com>
|
Tarsis Azevedo <tarsis@corp.globo.com>
|
||||||
Tim Schindler <tim@catalyst-zero.com>
|
Tim Schindler <tim@catalyst-zero.com>
|
||||||
Tobi Knaup <tobi@mesosphere.io>
|
Tobi Knaup <tobi@mesosphere.io>
|
||||||
|
Tonic <tonicbupt@gmail.com>
|
||||||
ttyh061 <ttyh061@gmail.com>
|
ttyh061 <ttyh061@gmail.com>
|
||||||
Victor Marmol <vmarmol@google.com>
|
Victor Marmol <vmarmol@google.com>
|
||||||
Vincenzo Prignano <vincenzo.prignano@gmail.com>
|
Vincenzo Prignano <vincenzo.prignano@gmail.com>
|
||||||
|
12
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/README.markdown
generated
vendored
12
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/README.markdown
generated
vendored
@ -7,8 +7,20 @@
|
|||||||
This package presents a client for the Docker remote API. It also provides
|
This package presents a client for the Docker remote API. It also provides
|
||||||
support for the extensions in the [Swarm API](https://docs.docker.com/swarm/API/).
|
support for the extensions in the [Swarm API](https://docs.docker.com/swarm/API/).
|
||||||
|
|
||||||
|
This package also provides support for docker's network API, which is a simple
|
||||||
|
passthrough to the libnetwork remote API. Note that docker's network API is
|
||||||
|
only available in docker 1.8 and above, and only enabled in docker if
|
||||||
|
DOCKER_EXPERIMENTAL is defined during the docker build process.
|
||||||
|
|
||||||
For more details, check the [remote API documentation](http://docs.docker.com/en/latest/reference/api/docker_remote_api/).
|
For more details, check the [remote API documentation](http://docs.docker.com/en/latest/reference/api/docker_remote_api/).
|
||||||
|
|
||||||
|
## Vendoring
|
||||||
|
|
||||||
|
If you are having issues with Go 1.5 and have `GO15VENDOREXPERIMENT` set with an application that has go-dockerclient vendored,
|
||||||
|
please update your vendoring of go-dockerclient :) We recently moved the `vendor` directory to `external` so that go-dockerclient
|
||||||
|
is compatible with this configuration. See [338](https://github.com/fsouza/go-dockerclient/issues/338) and [339](https://github.com/fsouza/go-dockerclient/pull/339)
|
||||||
|
for details.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
6
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth.go
generated
vendored
6
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth.go
generated
vendored
@ -8,6 +8,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@ -15,6 +16,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var AuthParseError error = errors.New("Failed to read authentication from dockercfg")
|
||||||
|
|
||||||
// AuthConfiguration represents authentication options to use in the PushImage
|
// AuthConfiguration represents authentication options to use in the PushImage
|
||||||
// method. It represents the authentication in the Docker index server.
|
// method. It represents the authentication in the Docker index server.
|
||||||
type AuthConfiguration struct {
|
type AuthConfiguration struct {
|
||||||
@ -99,6 +102,9 @@ func authConfigs(confs map[string]dockerConfig) (*AuthConfigurations, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
userpass := strings.Split(string(data), ":")
|
userpass := strings.Split(string(data), ":")
|
||||||
|
if len(userpass) != 2 {
|
||||||
|
return nil, AuthParseError
|
||||||
|
}
|
||||||
c.Configs[reg] = AuthConfiguration{
|
c.Configs[reg] = AuthConfiguration{
|
||||||
Email: conf.Email,
|
Email: conf.Email,
|
||||||
Username: userpass[0],
|
Username: userpass[0],
|
||||||
|
12
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth_test.go
generated
vendored
12
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth_test.go
generated
vendored
@ -37,6 +37,18 @@ func TestAuthLegacyConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAuthBadConfig(t *testing.T) {
|
||||||
|
auth := base64.StdEncoding.EncodeToString([]byte("userpass"))
|
||||||
|
read := strings.NewReader(fmt.Sprintf(`{"docker.io":{"auth":"%s","email":"user@example.com"}}`, auth))
|
||||||
|
ac, err := NewAuthConfigurations(read)
|
||||||
|
if err != AuthParseError {
|
||||||
|
t.Errorf("Incorrect error returned %v\n", err)
|
||||||
|
}
|
||||||
|
if ac != nil {
|
||||||
|
t.Errorf("Invalid auth configuration returned, should be nil %v\n", ac)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAuthConfig(t *testing.T) {
|
func TestAuthConfig(t *testing.T) {
|
||||||
auth := base64.StdEncoding.EncodeToString([]byte("user:pass"))
|
auth := base64.StdEncoding.EncodeToString([]byte("user:pass"))
|
||||||
read := strings.NewReader(fmt.Sprintf(`{"auths":{"docker.io":{"auth":"%s","email":"user@example.com"}}}`, auth))
|
read := strings.NewReader(fmt.Sprintf(`{"auths":{"docker.io":{"auth":"%s","email":"user@example.com"}}}`, auth))
|
||||||
|
15
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go
generated
vendored
15
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go
generated
vendored
@ -28,10 +28,11 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts"
|
"github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts"
|
||||||
"github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/homedir"
|
"github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/homedir"
|
||||||
"github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/stdcopy"
|
"github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/stdcopy"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const userAgent = "go-dockerclient"
|
const userAgent = "go-dockerclient"
|
||||||
@ -628,18 +629,20 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) error
|
|||||||
if hijackOptions.in != nil {
|
if hijackOptions.in != nil {
|
||||||
_, err := io.Copy(rwc, hijackOptions.in)
|
_, err := io.Copy(rwc, hijackOptions.in)
|
||||||
errChanIn <- err
|
errChanIn <- err
|
||||||
|
} else {
|
||||||
|
errChanIn <- nil
|
||||||
}
|
}
|
||||||
rwc.(interface {
|
rwc.(interface {
|
||||||
CloseWrite() error
|
CloseWrite() error
|
||||||
}).CloseWrite()
|
}).CloseWrite()
|
||||||
}()
|
}()
|
||||||
<-exit
|
<-exit
|
||||||
select {
|
errIn := <-errChanIn
|
||||||
case err = <-errChanIn:
|
errOut := <-errChanOut
|
||||||
return err
|
if errIn != nil {
|
||||||
case err = <-errChanOut:
|
return errIn
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
return errOut
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) getURL(path string) string {
|
func (c *Client) getURL(path string) string {
|
||||||
|
5
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go
generated
vendored
5
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go
generated
vendored
@ -329,16 +329,17 @@ func TestPingFailingWrongStatus(t *testing.T) {
|
|||||||
func TestPingErrorWithUnixSocket(t *testing.T) {
|
func TestPingErrorWithUnixSocket(t *testing.T) {
|
||||||
go func() {
|
go func() {
|
||||||
li, err := net.Listen("unix", "/tmp/echo.sock")
|
li, err := net.Listen("unix", "/tmp/echo.sock")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
defer li.Close()
|
defer li.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected to get listner, but failed: %#v", err)
|
t.Fatalf("Expected to get listner, but failed: %#v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fd, err := li.Accept()
|
fd, err := li.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected to accept connection, but failed: %#v", err)
|
t.Fatalf("Expected to accept connection, but failed: %#v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, 512)
|
buf := make([]byte, 512)
|
||||||
|
28
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go
generated
vendored
28
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go
generated
vendored
@ -135,6 +135,16 @@ type NetworkSettings struct {
|
|||||||
Bridge string `json:"Bridge,omitempty" yaml:"Bridge,omitempty"`
|
Bridge string `json:"Bridge,omitempty" yaml:"Bridge,omitempty"`
|
||||||
PortMapping map[string]PortMapping `json:"PortMapping,omitempty" yaml:"PortMapping,omitempty"`
|
PortMapping map[string]PortMapping `json:"PortMapping,omitempty" yaml:"PortMapping,omitempty"`
|
||||||
Ports map[Port][]PortBinding `json:"Ports,omitempty" yaml:"Ports,omitempty"`
|
Ports map[Port][]PortBinding `json:"Ports,omitempty" yaml:"Ports,omitempty"`
|
||||||
|
NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty"`
|
||||||
|
EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty"`
|
||||||
|
SandboxKey string `json:"SandboxKey,omitempty" yaml:"SandboxKey,omitempty"`
|
||||||
|
GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty"`
|
||||||
|
GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty"`
|
||||||
|
IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty"`
|
||||||
|
LinkLocalIPv6Address string `json:"LinkLocalIPv6Address,omitempty" yaml:"LinkLocalIPv6Address,omitempty"`
|
||||||
|
LinkLocalIPv6PrefixLen int `json:"LinkLocalIPv6PrefixLen,omitempty" yaml:"LinkLocalIPv6PrefixLen,omitempty"`
|
||||||
|
SecondaryIPAddresses []string `json:"SecondaryIPAddresses,omitempty" yaml:"SecondaryIPAddresses,omitempty"`
|
||||||
|
SecondaryIPv6Addresses []string `json:"SecondaryIPv6Addresses,omitempty" yaml:"SecondaryIPv6Addresses,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PortMappingAPI translates the port mappings as contained in NetworkSettings
|
// PortMappingAPI translates the port mappings as contained in NetworkSettings
|
||||||
@ -726,6 +736,18 @@ func (c *Client) Stats(opts StatsOptions) (retErr error) {
|
|||||||
close(errC)
|
close(errC)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
quit := make(chan struct{})
|
||||||
|
defer close(quit)
|
||||||
|
go func() {
|
||||||
|
// block here waiting for the signal to stop function
|
||||||
|
select {
|
||||||
|
case <-opts.Done:
|
||||||
|
readCloser.Close()
|
||||||
|
case <-quit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
decoder := json.NewDecoder(readCloser)
|
decoder := json.NewDecoder(readCloser)
|
||||||
stats := new(Stats)
|
stats := new(Stats)
|
||||||
for err := decoder.Decode(&stats); err != io.EOF; err = decoder.Decode(stats) {
|
for err := decoder.Decode(&stats); err != io.EOF; err = decoder.Decode(stats) {
|
||||||
@ -734,12 +756,6 @@ func (c *Client) Stats(opts StatsOptions) (retErr error) {
|
|||||||
}
|
}
|
||||||
opts.Stats <- stats
|
opts.Stats <- stats
|
||||||
stats = new(Stats)
|
stats = new(Stats)
|
||||||
select {
|
|
||||||
case <-opts.Done:
|
|
||||||
readCloser.Close()
|
|
||||||
default:
|
|
||||||
// Continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
11
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy_unix.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy_unix.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func normalizePath(path string) string {
|
||||||
|
return filepath.ToSlash(path)
|
||||||
|
}
|
9
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy_windows.go
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy_windows.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func normalizePath(path string) string {
|
||||||
|
return filepath.FromSlash(path)
|
||||||
|
}
|
@ -1,7 +1,235 @@
|
|||||||
mux
|
mux
|
||||||
===
|
===
|
||||||
|
[](https://godoc.org/github.com/gorilla/securecookie)
|
||||||
[](https://travis-ci.org/gorilla/mux)
|
[](https://travis-ci.org/gorilla/mux)
|
||||||
|
|
||||||
gorilla/mux is a powerful URL router and dispatcher.
|
Package gorilla/mux implements a request router and dispatcher.
|
||||||
|
|
||||||
Read the full documentation here: http://www.gorillatoolkit.org/pkg/mux
|
The name mux stands for "HTTP request multiplexer". Like the standard
|
||||||
|
http.ServeMux, mux.Router matches incoming requests against a list of
|
||||||
|
registered routes and calls a handler for the route that matches the URL
|
||||||
|
or other conditions. The main features are:
|
||||||
|
|
||||||
|
* Requests can be matched based on URL host, path, path prefix, schemes,
|
||||||
|
header and query values, HTTP methods or using custom matchers.
|
||||||
|
* URL hosts and paths can have variables with an optional regular
|
||||||
|
expression.
|
||||||
|
* Registered URLs can be built, or "reversed", which helps maintaining
|
||||||
|
references to resources.
|
||||||
|
* Routes can be used as subrouters: nested routes are only tested if the
|
||||||
|
parent route matches. This is useful to define groups of routes that
|
||||||
|
share common conditions like a host, a path prefix or other repeated
|
||||||
|
attributes. As a bonus, this optimizes request matching.
|
||||||
|
* It implements the http.Handler interface so it is compatible with the
|
||||||
|
standard http.ServeMux.
|
||||||
|
|
||||||
|
Let's start registering a couple of URL paths and handlers:
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/", HomeHandler)
|
||||||
|
r.HandleFunc("/products", ProductsHandler)
|
||||||
|
r.HandleFunc("/articles", ArticlesHandler)
|
||||||
|
http.Handle("/", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
Here we register three routes mapping URL paths to handlers. This is
|
||||||
|
equivalent to how http.HandleFunc() works: if an incoming request URL matches
|
||||||
|
one of the paths, the corresponding handler is called passing
|
||||||
|
(http.ResponseWriter, *http.Request) as parameters.
|
||||||
|
|
||||||
|
Paths can have variables. They are defined using the format {name} or
|
||||||
|
{name:pattern}. If a regular expression pattern is not defined, the matched
|
||||||
|
variable will be anything until the next slash. For example:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/products/{key}", ProductHandler)
|
||||||
|
r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
|
||||||
|
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
|
||||||
|
|
||||||
|
The names are used to create a map of route variables which can be retrieved
|
||||||
|
calling mux.Vars():
|
||||||
|
|
||||||
|
vars := mux.Vars(request)
|
||||||
|
category := vars["category"]
|
||||||
|
|
||||||
|
And this is all you need to know about the basic usage. More advanced options
|
||||||
|
are explained below.
|
||||||
|
|
||||||
|
Routes can also be restricted to a domain or subdomain. Just define a host
|
||||||
|
pattern to be matched. They can also have variables:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
// Only matches if domain is "www.example.com".
|
||||||
|
r.Host("www.example.com")
|
||||||
|
// Matches a dynamic subdomain.
|
||||||
|
r.Host("{subdomain:[a-z]+}.domain.com")
|
||||||
|
|
||||||
|
There are several other matchers that can be added. To match path prefixes:
|
||||||
|
|
||||||
|
r.PathPrefix("/products/")
|
||||||
|
|
||||||
|
...or HTTP methods:
|
||||||
|
|
||||||
|
r.Methods("GET", "POST")
|
||||||
|
|
||||||
|
...or URL schemes:
|
||||||
|
|
||||||
|
r.Schemes("https")
|
||||||
|
|
||||||
|
...or header values:
|
||||||
|
|
||||||
|
r.Headers("X-Requested-With", "XMLHttpRequest")
|
||||||
|
|
||||||
|
...or query values:
|
||||||
|
|
||||||
|
r.Queries("key", "value")
|
||||||
|
|
||||||
|
...or to use a custom matcher function:
|
||||||
|
|
||||||
|
r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
|
||||||
|
return r.ProtoMajor == 0
|
||||||
|
})
|
||||||
|
|
||||||
|
...and finally, it is possible to combine several matchers in a single route:
|
||||||
|
|
||||||
|
r.HandleFunc("/products", ProductsHandler).
|
||||||
|
Host("www.example.com").
|
||||||
|
Methods("GET").
|
||||||
|
Schemes("http")
|
||||||
|
|
||||||
|
Setting the same matching conditions again and again can be boring, so we have
|
||||||
|
a way to group several routes that share the same requirements.
|
||||||
|
We call it "subrouting".
|
||||||
|
|
||||||
|
For example, let's say we have several URLs that should only match when the
|
||||||
|
host is `www.example.com`. Create a route for that host and get a "subrouter"
|
||||||
|
from it:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
s := r.Host("www.example.com").Subrouter()
|
||||||
|
|
||||||
|
Then register routes in the subrouter:
|
||||||
|
|
||||||
|
s.HandleFunc("/products/", ProductsHandler)
|
||||||
|
s.HandleFunc("/products/{key}", ProductHandler)
|
||||||
|
s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
||||||
|
|
||||||
|
The three URL paths we registered above will only be tested if the domain is
|
||||||
|
`www.example.com`, because the subrouter is tested first. This is not
|
||||||
|
only convenient, but also optimizes request matching. You can create
|
||||||
|
subrouters combining any attribute matchers accepted by a route.
|
||||||
|
|
||||||
|
Subrouters can be used to create domain or path "namespaces": you define
|
||||||
|
subrouters in a central place and then parts of the app can register its
|
||||||
|
paths relatively to a given subrouter.
|
||||||
|
|
||||||
|
There's one more thing about subroutes. When a subrouter has a path prefix,
|
||||||
|
the inner routes use it as base for their paths:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
s := r.PathPrefix("/products").Subrouter()
|
||||||
|
// "/products/"
|
||||||
|
s.HandleFunc("/", ProductsHandler)
|
||||||
|
// "/products/{key}/"
|
||||||
|
s.HandleFunc("/{key}/", ProductHandler)
|
||||||
|
// "/products/{key}/details"
|
||||||
|
s.HandleFunc("/{key}/details", ProductDetailsHandler)
|
||||||
|
|
||||||
|
Now let's see how to build registered URLs.
|
||||||
|
|
||||||
|
Routes can be named. All routes that define a name can have their URLs built,
|
||||||
|
or "reversed". We define a name calling Name() on a route. For example:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||||
|
Name("article")
|
||||||
|
|
||||||
|
To build a URL, get the route and call the URL() method, passing a sequence of
|
||||||
|
key/value pairs for the route variables. For the previous route, we would do:
|
||||||
|
|
||||||
|
url, err := r.Get("article").URL("category", "technology", "id", "42")
|
||||||
|
|
||||||
|
...and the result will be a url.URL with the following path:
|
||||||
|
|
||||||
|
"/articles/technology/42"
|
||||||
|
|
||||||
|
This also works for host variables:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.Host("{subdomain}.domain.com").
|
||||||
|
Path("/articles/{category}/{id:[0-9]+}").
|
||||||
|
HandlerFunc(ArticleHandler).
|
||||||
|
Name("article")
|
||||||
|
|
||||||
|
// url.String() will be "http://news.domain.com/articles/technology/42"
|
||||||
|
url, err := r.Get("article").URL("subdomain", "news",
|
||||||
|
"category", "technology",
|
||||||
|
"id", "42")
|
||||||
|
|
||||||
|
All variables defined in the route are required, and their values must
|
||||||
|
conform to the corresponding patterns. These requirements guarantee that a
|
||||||
|
generated URL will always match a registered route -- the only exception is
|
||||||
|
for explicitly defined "build-only" routes which never match.
|
||||||
|
|
||||||
|
Regex support also exists for matching Headers within a route. For example, we could do:
|
||||||
|
|
||||||
|
r.HeadersRegexp("Content-Type", "application/(text|json)")
|
||||||
|
|
||||||
|
...and the route will match both requests with a Content-Type of `application/json` as well as
|
||||||
|
`application/text`
|
||||||
|
|
||||||
|
There's also a way to build only the URL host or path for a route:
|
||||||
|
use the methods URLHost() or URLPath() instead. For the previous route,
|
||||||
|
we would do:
|
||||||
|
|
||||||
|
// "http://news.domain.com/"
|
||||||
|
host, err := r.Get("article").URLHost("subdomain", "news")
|
||||||
|
|
||||||
|
// "/articles/technology/42"
|
||||||
|
path, err := r.Get("article").URLPath("category", "technology", "id", "42")
|
||||||
|
|
||||||
|
And if you use subrouters, host and path defined separately can be built
|
||||||
|
as well:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
s := r.Host("{subdomain}.domain.com").Subrouter()
|
||||||
|
s.Path("/articles/{category}/{id:[0-9]+}").
|
||||||
|
HandlerFunc(ArticleHandler).
|
||||||
|
Name("article")
|
||||||
|
|
||||||
|
// "http://news.domain.com/articles/technology/42"
|
||||||
|
url, err := r.Get("article").URL("subdomain", "news",
|
||||||
|
"category", "technology",
|
||||||
|
"id", "42")
|
||||||
|
|
||||||
|
## Full Example
|
||||||
|
|
||||||
|
Here's a complete, runnable example of a small mux based server:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func YourHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte("Gorilla!\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := mux.NewRouter()
|
||||||
|
// Routes consist of a path and a handler function.
|
||||||
|
r.HandleFunc("/", YourHandler)
|
||||||
|
|
||||||
|
// Bind to a port and pass our router in
|
||||||
|
http.ListenAndServe(":8000", r)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
BSD licensed. See the LICENSE file for details.
|
||||||
|
@ -60,8 +60,8 @@ Routes can also be restricted to a domain or subdomain. Just define a host
|
|||||||
pattern to be matched. They can also have variables:
|
pattern to be matched. They can also have variables:
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
// Only matches if domain is "www.domain.com".
|
// Only matches if domain is "www.example.com".
|
||||||
r.Host("www.domain.com")
|
r.Host("www.example.com")
|
||||||
// Matches a dynamic subdomain.
|
// Matches a dynamic subdomain.
|
||||||
r.Host("{subdomain:[a-z]+}.domain.com")
|
r.Host("{subdomain:[a-z]+}.domain.com")
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ There are several other matchers that can be added. To match path prefixes:
|
|||||||
...and finally, it is possible to combine several matchers in a single route:
|
...and finally, it is possible to combine several matchers in a single route:
|
||||||
|
|
||||||
r.HandleFunc("/products", ProductsHandler).
|
r.HandleFunc("/products", ProductsHandler).
|
||||||
Host("www.domain.com").
|
Host("www.example.com").
|
||||||
Methods("GET").
|
Methods("GET").
|
||||||
Schemes("http")
|
Schemes("http")
|
||||||
|
|
||||||
@ -103,11 +103,11 @@ a way to group several routes that share the same requirements.
|
|||||||
We call it "subrouting".
|
We call it "subrouting".
|
||||||
|
|
||||||
For example, let's say we have several URLs that should only match when the
|
For example, let's say we have several URLs that should only match when the
|
||||||
host is "www.domain.com". Create a route for that host and get a "subrouter"
|
host is "www.example.com". Create a route for that host and get a "subrouter"
|
||||||
from it:
|
from it:
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
s := r.Host("www.domain.com").Subrouter()
|
s := r.Host("www.example.com").Subrouter()
|
||||||
|
|
||||||
Then register routes in the subrouter:
|
Then register routes in the subrouter:
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ Then register routes in the subrouter:
|
|||||||
s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
||||||
|
|
||||||
The three URL paths we registered above will only be tested if the domain is
|
The three URL paths we registered above will only be tested if the domain is
|
||||||
"www.domain.com", because the subrouter is tested first. This is not
|
"www.example.com", because the subrouter is tested first. This is not
|
||||||
only convenient, but also optimizes request matching. You can create
|
only convenient, but also optimizes request matching. You can create
|
||||||
subrouters combining any attribute matchers accepted by a route.
|
subrouters combining any attribute matchers accepted by a route.
|
||||||
|
|
||||||
|
@ -312,6 +312,10 @@ func Vars(r *http.Request) map[string]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CurrentRoute returns the matched route for the current request, if any.
|
// CurrentRoute returns the matched route for the current request, if any.
|
||||||
|
// This only works when called inside the handler of the matched route
|
||||||
|
// because the matched route is stored in the request context which is cleared
|
||||||
|
// after the handler returns, unless the KeepContext option is set on the
|
||||||
|
// Router.
|
||||||
func CurrentRoute(r *http.Request) *Route {
|
func CurrentRoute(r *http.Request) *Route {
|
||||||
if rv := context.Get(r, routeKey); rv != nil {
|
if rv := context.Get(r, routeKey); rv != nil {
|
||||||
return rv.(*Route)
|
return rv.(*Route)
|
||||||
|
@ -7,11 +7,24 @@ package mux
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/fsouza/go-dockerclient/external/github.com/gorilla/context"
|
"github.com/fsouza/go-dockerclient/external/github.com/gorilla/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (r *Route) GoString() string {
|
||||||
|
matchers := make([]string, len(r.matchers))
|
||||||
|
for i, m := range r.matchers {
|
||||||
|
matchers[i] = fmt.Sprintf("%#v", m)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("&Route{matchers:[]matcher{%s}}", strings.Join(matchers, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *routeRegexp) GoString() string {
|
||||||
|
return fmt.Sprintf("&routeRegexp{template: %q, matchHost: %t, matchQuery: %t, strictSlash: %t, regexp: regexp.MustCompile(%q), reverse: %q, varsN: %v, varsR: %v", r.template, r.matchHost, r.matchQuery, r.strictSlash, r.regexp.String(), r.reverse, r.varsN, r.varsR)
|
||||||
|
}
|
||||||
|
|
||||||
type routeTest struct {
|
type routeTest struct {
|
||||||
title string // title of the test
|
title string // title of the test
|
||||||
route *Route // the route being tested
|
route *Route // the route being tested
|
||||||
@ -108,6 +121,15 @@ func TestHost(t *testing.T) {
|
|||||||
path: "",
|
path: "",
|
||||||
shouldMatch: true,
|
shouldMatch: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Host route with pattern, additional capturing group, match",
|
||||||
|
route: new(Route).Host("aaa.{v1:[a-z]{2}(b|c)}.ccc"),
|
||||||
|
request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
|
||||||
|
vars: map[string]string{"v1": "bbb"},
|
||||||
|
host: "aaa.bbb.ccc",
|
||||||
|
path: "",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Host route with pattern, wrong host in request URL",
|
title: "Host route with pattern, wrong host in request URL",
|
||||||
route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
|
route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
|
||||||
@ -135,6 +157,33 @@ func TestHost(t *testing.T) {
|
|||||||
path: "",
|
path: "",
|
||||||
shouldMatch: false,
|
shouldMatch: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Host route with hyphenated name and pattern, match",
|
||||||
|
route: new(Route).Host("aaa.{v-1:[a-z]{3}}.ccc"),
|
||||||
|
request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
|
||||||
|
vars: map[string]string{"v-1": "bbb"},
|
||||||
|
host: "aaa.bbb.ccc",
|
||||||
|
path: "",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Host route with hyphenated name and pattern, additional capturing group, match",
|
||||||
|
route: new(Route).Host("aaa.{v-1:[a-z]{2}(b|c)}.ccc"),
|
||||||
|
request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
|
||||||
|
vars: map[string]string{"v-1": "bbb"},
|
||||||
|
host: "aaa.bbb.ccc",
|
||||||
|
path: "",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Host route with multiple hyphenated names and patterns, match",
|
||||||
|
route: new(Route).Host("{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}"),
|
||||||
|
request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
|
||||||
|
vars: map[string]string{"v-1": "aaa", "v-2": "bbb", "v-3": "ccc"},
|
||||||
|
host: "aaa.bbb.ccc",
|
||||||
|
path: "",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Path route with single pattern with pipe, match",
|
title: "Path route with single pattern with pipe, match",
|
||||||
route: new(Route).Path("/{category:a|b/c}"),
|
route: new(Route).Path("/{category:a|b/c}"),
|
||||||
@ -260,6 +309,42 @@ func TestPath(t *testing.T) {
|
|||||||
path: "/111/222/333",
|
path: "/111/222/333",
|
||||||
shouldMatch: false,
|
shouldMatch: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Path route with multiple patterns with pipe, match",
|
||||||
|
route: new(Route).Path("/{category:a|(b/c)}/{product}/{id:[0-9]+}"),
|
||||||
|
request: newRequest("GET", "http://localhost/a/product_name/1"),
|
||||||
|
vars: map[string]string{"category": "a", "product": "product_name", "id": "1"},
|
||||||
|
host: "",
|
||||||
|
path: "/a/product_name/1",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Path route with hyphenated name and pattern, match",
|
||||||
|
route: new(Route).Path("/111/{v-1:[0-9]{3}}/333"),
|
||||||
|
request: newRequest("GET", "http://localhost/111/222/333"),
|
||||||
|
vars: map[string]string{"v-1": "222"},
|
||||||
|
host: "",
|
||||||
|
path: "/111/222/333",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Path route with multiple hyphenated names and patterns, match",
|
||||||
|
route: new(Route).Path("/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}"),
|
||||||
|
request: newRequest("GET", "http://localhost/111/222/333"),
|
||||||
|
vars: map[string]string{"v-1": "111", "v-2": "222", "v-3": "333"},
|
||||||
|
host: "",
|
||||||
|
path: "/111/222/333",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Path route with multiple hyphenated names and patterns with pipe, match",
|
||||||
|
route: new(Route).Path("/{product-category:a|(b/c)}/{product-name}/{product-id:[0-9]+}"),
|
||||||
|
request: newRequest("GET", "http://localhost/a/product_name/1"),
|
||||||
|
vars: map[string]string{"product-category": "a", "product-name": "product_name", "product-id": "1"},
|
||||||
|
host: "",
|
||||||
|
path: "/a/product_name/1",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -597,6 +682,15 @@ func TestQueries(t *testing.T) {
|
|||||||
path: "",
|
path: "",
|
||||||
shouldMatch: false,
|
shouldMatch: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Queries route with regexp pattern with quantifier, additional capturing group",
|
||||||
|
route: new(Route).Queries("foo", "{v1:[0-9]{1}(a|b)}"),
|
||||||
|
request: newRequest("GET", "http://localhost?foo=1a"),
|
||||||
|
vars: map[string]string{"v1": "1a"},
|
||||||
|
host: "",
|
||||||
|
path: "",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match",
|
title: "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match",
|
||||||
route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
|
route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
|
||||||
@ -606,6 +700,42 @@ func TestQueries(t *testing.T) {
|
|||||||
path: "",
|
path: "",
|
||||||
shouldMatch: false,
|
shouldMatch: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Queries route with hyphenated name, match",
|
||||||
|
route: new(Route).Queries("foo", "{v-1}"),
|
||||||
|
request: newRequest("GET", "http://localhost?foo=bar"),
|
||||||
|
vars: map[string]string{"v-1": "bar"},
|
||||||
|
host: "",
|
||||||
|
path: "",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Queries route with multiple hyphenated names, match",
|
||||||
|
route: new(Route).Queries("foo", "{v-1}", "baz", "{v-2}"),
|
||||||
|
request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
|
||||||
|
vars: map[string]string{"v-1": "bar", "v-2": "ding"},
|
||||||
|
host: "",
|
||||||
|
path: "",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Queries route with hyphenate name and pattern, match",
|
||||||
|
route: new(Route).Queries("foo", "{v-1:[0-9]+}"),
|
||||||
|
request: newRequest("GET", "http://localhost?foo=10"),
|
||||||
|
vars: map[string]string{"v-1": "10"},
|
||||||
|
host: "",
|
||||||
|
path: "",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Queries route with hyphenated name and pattern with quantifier, additional capturing group",
|
||||||
|
route: new(Route).Queries("foo", "{v-1:[0-9]{1}(a|b)}"),
|
||||||
|
request: newRequest("GET", "http://localhost?foo=1a"),
|
||||||
|
vars: map[string]string{"v-1": "1a"},
|
||||||
|
host: "",
|
||||||
|
path: "",
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Queries route with empty value, should match",
|
title: "Queries route with empty value, should match",
|
||||||
route: new(Route).Queries("foo", ""),
|
route: new(Route).Queries("foo", ""),
|
||||||
@ -660,6 +790,15 @@ func TestQueries(t *testing.T) {
|
|||||||
path: "",
|
path: "",
|
||||||
shouldMatch: true,
|
shouldMatch: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Queries route, bad submatch",
|
||||||
|
route: new(Route).Queries("foo", "bar", "baz", "ding"),
|
||||||
|
request: newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"),
|
||||||
|
vars: map[string]string{},
|
||||||
|
host: "",
|
||||||
|
path: "",
|
||||||
|
shouldMatch: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -545,7 +545,7 @@ func TestMatchedRouteName(t *testing.T) {
|
|||||||
router := NewRouter()
|
router := NewRouter()
|
||||||
route := router.NewRoute().Path("/products/").Name(routeName)
|
route := router.NewRoute().Path("/products/").Name(routeName)
|
||||||
|
|
||||||
url := "http://www.domain.com/products/"
|
url := "http://www.example.com/products/"
|
||||||
request, _ := http.NewRequest("GET", url, nil)
|
request, _ := http.NewRequest("GET", url, nil)
|
||||||
var rv RouteMatch
|
var rv RouteMatch
|
||||||
ok := router.Match(request, &rv)
|
ok := router.Match(request, &rv)
|
||||||
@ -563,10 +563,10 @@ func TestMatchedRouteName(t *testing.T) {
|
|||||||
func TestSubRouting(t *testing.T) {
|
func TestSubRouting(t *testing.T) {
|
||||||
// Example from docs.
|
// Example from docs.
|
||||||
router := NewRouter()
|
router := NewRouter()
|
||||||
subrouter := router.NewRoute().Host("www.domain.com").Subrouter()
|
subrouter := router.NewRoute().Host("www.example.com").Subrouter()
|
||||||
route := subrouter.NewRoute().Path("/products/").Name("products")
|
route := subrouter.NewRoute().Path("/products/").Name("products")
|
||||||
|
|
||||||
url := "http://www.domain.com/products/"
|
url := "http://www.example.com/products/"
|
||||||
request, _ := http.NewRequest("GET", url, nil)
|
request, _ := http.NewRequest("GET", url, nil)
|
||||||
var rv RouteMatch
|
var rv RouteMatch
|
||||||
ok := router.Match(request, &rv)
|
ok := router.Match(request, &rv)
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -72,13 +73,14 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash
|
|||||||
tpl[idxs[i]:end])
|
tpl[idxs[i]:end])
|
||||||
}
|
}
|
||||||
// Build the regexp pattern.
|
// Build the regexp pattern.
|
||||||
fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt)
|
varIdx := i / 2
|
||||||
|
fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(varIdx), patt)
|
||||||
// Build the reverse template.
|
// Build the reverse template.
|
||||||
fmt.Fprintf(reverse, "%s%%s", raw)
|
fmt.Fprintf(reverse, "%s%%s", raw)
|
||||||
|
|
||||||
// Append variable name and compiled pattern.
|
// Append variable name and compiled pattern.
|
||||||
varsN[i/2] = name
|
varsN[varIdx] = name
|
||||||
varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
|
varsR[varIdx], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -224,6 +226,11 @@ func braceIndices(s string) ([]int, error) {
|
|||||||
return idxs, nil
|
return idxs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// varGroupName builds a capturing group name for the indexed variable.
|
||||||
|
func varGroupName(idx int) string {
|
||||||
|
return "v" + strconv.Itoa(idx)
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// routeRegexpGroup
|
// routeRegexpGroup
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -241,8 +248,13 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
|
|||||||
if v.host != nil {
|
if v.host != nil {
|
||||||
hostVars := v.host.regexp.FindStringSubmatch(getHost(req))
|
hostVars := v.host.regexp.FindStringSubmatch(getHost(req))
|
||||||
if hostVars != nil {
|
if hostVars != nil {
|
||||||
for k, v := range v.host.varsN {
|
subexpNames := v.host.regexp.SubexpNames()
|
||||||
m.Vars[v] = hostVars[k+1]
|
varName := 0
|
||||||
|
for i, name := range subexpNames[1:] {
|
||||||
|
if name != "" && name == varGroupName(varName) {
|
||||||
|
m.Vars[v.host.varsN[varName]] = hostVars[i+1]
|
||||||
|
varName++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -250,8 +262,13 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
|
|||||||
if v.path != nil {
|
if v.path != nil {
|
||||||
pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path)
|
pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path)
|
||||||
if pathVars != nil {
|
if pathVars != nil {
|
||||||
for k, v := range v.path.varsN {
|
subexpNames := v.path.regexp.SubexpNames()
|
||||||
m.Vars[v] = pathVars[k+1]
|
varName := 0
|
||||||
|
for i, name := range subexpNames[1:] {
|
||||||
|
if name != "" && name == varGroupName(varName) {
|
||||||
|
m.Vars[v.path.varsN[varName]] = pathVars[i+1]
|
||||||
|
varName++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Check if we should redirect.
|
// Check if we should redirect.
|
||||||
if v.path.strictSlash {
|
if v.path.strictSlash {
|
||||||
@ -273,8 +290,13 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
|
|||||||
for _, q := range v.queries {
|
for _, q := range v.queries {
|
||||||
queryVars := q.regexp.FindStringSubmatch(q.getUrlQuery(req))
|
queryVars := q.regexp.FindStringSubmatch(q.getUrlQuery(req))
|
||||||
if queryVars != nil {
|
if queryVars != nil {
|
||||||
for k, v := range q.varsN {
|
subexpNames := q.regexp.SubexpNames()
|
||||||
m.Vars[v] = queryVars[k+1]
|
varName := 0
|
||||||
|
for i, name := range subexpNames[1:] {
|
||||||
|
if name != "" && name == varGroupName(varName) {
|
||||||
|
m.Vars[q.varsN[varName]] = queryVars[i+1]
|
||||||
|
varName++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,7 +255,7 @@ func (r *Route) HeadersRegexp(pairs ...string) *Route {
|
|||||||
// For example:
|
// For example:
|
||||||
//
|
//
|
||||||
// r := mux.NewRouter()
|
// r := mux.NewRouter()
|
||||||
// r.Host("www.domain.com")
|
// r.Host("www.example.com")
|
||||||
// r.Host("{subdomain}.domain.com")
|
// r.Host("{subdomain}.domain.com")
|
||||||
// r.Host("{subdomain:[a-z]+}.domain.com")
|
// r.Host("{subdomain:[a-z]+}.domain.com")
|
||||||
//
|
//
|
||||||
@ -414,7 +414,7 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
|
|||||||
// It will test the inner routes only if the parent route matched. For example:
|
// It will test the inner routes only if the parent route matched. For example:
|
||||||
//
|
//
|
||||||
// r := mux.NewRouter()
|
// r := mux.NewRouter()
|
||||||
// s := r.Host("www.domain.com").Subrouter()
|
// s := r.Host("www.example.com").Subrouter()
|
||||||
// s.HandleFunc("/products/", ProductsHandler)
|
// s.HandleFunc("/products/", ProductsHandler)
|
||||||
// s.HandleFunc("/products/{key}", ProductHandler)
|
// s.HandleFunc("/products/{key}", ProductHandler)
|
||||||
// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
||||||
|
127
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/network.go
generated
vendored
Normal file
127
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/network.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// Copyright 2015 go-dockerclient authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrNetworkAlreadyExists is the error returned by CreateNetwork when the
|
||||||
|
// network already exists.
|
||||||
|
var ErrNetworkAlreadyExists = errors.New("network already exists")
|
||||||
|
|
||||||
|
// Network represents a network.
|
||||||
|
//
|
||||||
|
// See https://goo.gl/FDkCdQ for more details.
|
||||||
|
type Network struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Endpoints []*Endpoint `json:"endpoints"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoint represents an endpoint.
|
||||||
|
//
|
||||||
|
// See https://goo.gl/FDkCdQ for more details.
|
||||||
|
type Endpoint struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Network string `json:"network"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListNetworks returns all networks.
|
||||||
|
//
|
||||||
|
// See https://goo.gl/4hCNtZ for more details.
|
||||||
|
func (c *Client) ListNetworks() ([]Network, error) {
|
||||||
|
body, _, err := c.do("GET", "/networks", doOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var networks []Network
|
||||||
|
if err := json.Unmarshal(body, &networks); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return networks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkInfo returns information about a network by its ID.
|
||||||
|
//
|
||||||
|
// See https://goo.gl/4hCNtZ for more details.
|
||||||
|
func (c *Client) NetworkInfo(id string) (*Network, error) {
|
||||||
|
path := "/networks/" + id
|
||||||
|
body, status, err := c.do("GET", path, doOptions{})
|
||||||
|
if status == http.StatusNotFound {
|
||||||
|
return nil, &NoSuchNetwork{ID: id}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var network Network
|
||||||
|
if err := json.Unmarshal(body, &network); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &network, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNetworkOptions specify parameters to the CreateNetwork function and
|
||||||
|
// (for now) is the expected body of the "create network" http request message
|
||||||
|
//
|
||||||
|
// See https://goo.gl/FDkCdQ for more details.
|
||||||
|
type CreateNetworkOptions struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
NetworkType string `json:"network_type"`
|
||||||
|
Options map[string]interface{} `json:"options"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNetwork creates a new network, returning the network instance,
|
||||||
|
// or an error in case of failure.
|
||||||
|
//
|
||||||
|
// See http://goo.gl/mErxNp for more details.
|
||||||
|
func (c *Client) CreateNetwork(opts CreateNetworkOptions) (*Network, error) {
|
||||||
|
body, status, err := c.do(
|
||||||
|
"POST",
|
||||||
|
"/networks",
|
||||||
|
doOptions{
|
||||||
|
data: opts,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if status == http.StatusConflict {
|
||||||
|
return nil, ErrNetworkAlreadyExists
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type createNetworkResponse struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
network Network
|
||||||
|
resp createNetworkResponse
|
||||||
|
)
|
||||||
|
err = json.Unmarshal(body, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
network.Name = opts.Name
|
||||||
|
network.ID = resp.ID
|
||||||
|
network.Type = opts.NetworkType
|
||||||
|
|
||||||
|
return &network, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoSuchNetwork is the error returned when a given network does not exist.
|
||||||
|
type NoSuchNetwork struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *NoSuchNetwork) Error() string {
|
||||||
|
return fmt.Sprintf("No such network: %s", err.ID)
|
||||||
|
}
|
96
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/network_test.go
generated
vendored
Normal file
96
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/network_test.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2015 go-dockerclient authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListNetworks(t *testing.T) {
|
||||||
|
jsonNetworks := `[
|
||||||
|
{
|
||||||
|
"ID": "8dfafdbc3a40",
|
||||||
|
"Name": "blah",
|
||||||
|
"Type": "bridge",
|
||||||
|
"Endpoints":[{"ID": "918c11c8288a", "Name": "dsafdsaf", "Network": "8dfafdbc3a40"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": "9fb1e39c",
|
||||||
|
"Name": "foo",
|
||||||
|
"Type": "bridge",
|
||||||
|
"Endpoints":[{"ID": "c080be979dda", "Name": "lllll2222", "Network": "9fb1e39c"}]
|
||||||
|
}
|
||||||
|
]`
|
||||||
|
var expected []Network
|
||||||
|
err := json.Unmarshal([]byte(jsonNetworks), &expected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
client := newTestClient(&FakeRoundTripper{message: jsonNetworks, status: http.StatusOK})
|
||||||
|
containers, err := client.ListNetworks()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(containers, expected) {
|
||||||
|
t.Errorf("ListNetworks: Expected %#v. Got %#v.", expected, containers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkInfo(t *testing.T) {
|
||||||
|
jsonNetwork := `{
|
||||||
|
"ID": "8dfafdbc3a40",
|
||||||
|
"Name": "blah",
|
||||||
|
"Type": "bridge",
|
||||||
|
"Endpoints":[{"ID": "918c11c8288a", "Name": "dsafdsaf", "Network": "8dfafdbc3a40"}]
|
||||||
|
}`
|
||||||
|
var expected Network
|
||||||
|
err := json.Unmarshal([]byte(jsonNetwork), &expected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fakeRT := &FakeRoundTripper{message: jsonNetwork, status: http.StatusOK}
|
||||||
|
client := newTestClient(fakeRT)
|
||||||
|
id := "8dfafdbc3a40"
|
||||||
|
network, err := client.NetworkInfo(id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(*network, expected) {
|
||||||
|
t.Errorf("NetworkInfo(%q): Expected %#v. Got %#v.", id, expected, network)
|
||||||
|
}
|
||||||
|
expectedURL, _ := url.Parse(client.getURL("/networks/8dfafdbc3a40"))
|
||||||
|
if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
|
||||||
|
t.Errorf("NetworkInfo(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkCreate(t *testing.T) {
|
||||||
|
jsonID := `{"ID": "8dfafdbc3a40"}`
|
||||||
|
jsonNetwork := `{
|
||||||
|
"ID": "8dfafdbc3a40",
|
||||||
|
"Name": "foobar",
|
||||||
|
"Type": "bridge"
|
||||||
|
}`
|
||||||
|
var expected Network
|
||||||
|
err := json.Unmarshal([]byte(jsonNetwork), &expected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := newTestClient(&FakeRoundTripper{message: jsonID, status: http.StatusOK})
|
||||||
|
opts := CreateNetworkOptions{"foobar", "bridge", nil}
|
||||||
|
network, err := client.CreateNetwork(opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(*network, expected) {
|
||||||
|
t.Errorf("CreateNetwork: Expected %#v. Got %#v.", expected, network)
|
||||||
|
}
|
||||||
|
}
|
79
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go
generated
vendored
79
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go
generated
vendored
@ -43,6 +43,8 @@ type DockerServer struct {
|
|||||||
images []docker.Image
|
images []docker.Image
|
||||||
iMut sync.RWMutex
|
iMut sync.RWMutex
|
||||||
imgIDs map[string]string
|
imgIDs map[string]string
|
||||||
|
networks []*docker.Network
|
||||||
|
netMut sync.RWMutex
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
mux *mux.Router
|
mux *mux.Router
|
||||||
hook func(*http.Request)
|
hook func(*http.Request)
|
||||||
@ -124,6 +126,9 @@ func (s *DockerServer) buildMuxer() {
|
|||||||
s.mux.Path("/_ping").Methods("GET").HandlerFunc(s.handlerWrapper(s.pingDocker))
|
s.mux.Path("/_ping").Methods("GET").HandlerFunc(s.handlerWrapper(s.pingDocker))
|
||||||
s.mux.Path("/images/load").Methods("POST").HandlerFunc(s.handlerWrapper(s.loadImage))
|
s.mux.Path("/images/load").Methods("POST").HandlerFunc(s.handlerWrapper(s.loadImage))
|
||||||
s.mux.Path("/images/{id:.*}/get").Methods("GET").HandlerFunc(s.handlerWrapper(s.getImage))
|
s.mux.Path("/images/{id:.*}/get").Methods("GET").HandlerFunc(s.handlerWrapper(s.getImage))
|
||||||
|
s.mux.Path("/networks").Methods("GET").HandlerFunc(s.handlerWrapper(s.listNetworks))
|
||||||
|
s.mux.Path("/networks/{id:.*}").Methods("GET").HandlerFunc(s.handlerWrapper(s.networkInfo))
|
||||||
|
s.mux.Path("/networks").Methods("POST").HandlerFunc(s.handlerWrapper(s.createNetwork))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetHook changes the hook function used by the server.
|
// SetHook changes the hook function used by the server.
|
||||||
@ -981,3 +986,77 @@ func (s *DockerServer) getExec(id string) (*docker.ExecInspect, error) {
|
|||||||
}
|
}
|
||||||
return nil, errors.New("exec not found")
|
return nil, errors.New("exec not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerServer) findNetwork(idOrName string) (*docker.Network, int, error) {
|
||||||
|
s.netMut.RLock()
|
||||||
|
defer s.netMut.RUnlock()
|
||||||
|
for i, network := range s.networks {
|
||||||
|
if network.ID == idOrName || network.Name == idOrName {
|
||||||
|
return network, i, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, -1, errors.New("No such network")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerServer) listNetworks(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.netMut.RLock()
|
||||||
|
result := make([]docker.Network, 0, len(s.networks))
|
||||||
|
for _, network := range s.networks {
|
||||||
|
result = append(result, *network)
|
||||||
|
}
|
||||||
|
s.netMut.RUnlock()
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
json.NewEncoder(w).Encode(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerServer) networkInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
network, _, err := s.findNetwork(id)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
json.NewEncoder(w).Encode(network)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidName validates configuration objects supported by libnetwork
|
||||||
|
func isValidName(name string) bool {
|
||||||
|
if name == "" || strings.Contains(name, ".") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerServer) createNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var config *docker.CreateNetworkOptions
|
||||||
|
defer r.Body.Close()
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&config)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !isValidName(config.Name) {
|
||||||
|
http.Error(w, "Invalid network name", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n, _, _ := s.findNetwork(config.Name); n != nil {
|
||||||
|
http.Error(w, "network already exists", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedID := s.generateID()
|
||||||
|
network := docker.Network{
|
||||||
|
Name: config.Name,
|
||||||
|
ID: generatedID,
|
||||||
|
Type: config.NetworkType,
|
||||||
|
}
|
||||||
|
s.netMut.Lock()
|
||||||
|
s.networks = append(s.networks, &network)
|
||||||
|
s.netMut.Unlock()
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
var c = struct{ ID string }{ID: network.ID}
|
||||||
|
json.NewEncoder(w).Encode(c)
|
||||||
|
}
|
||||||
|
103
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
103
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
@ -1679,3 +1679,106 @@ func TestStatsContainerStream(t *testing.T) {
|
|||||||
t.Errorf("StatsContainer: wrong value. Want %#v. Got %#v.", expected, got)
|
t.Errorf("StatsContainer: wrong value. Want %#v. Got %#v.", expected, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addNetworks(server *DockerServer, n int) {
|
||||||
|
server.netMut.Lock()
|
||||||
|
defer server.netMut.Unlock()
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
netid := fmt.Sprintf("%x", rand.Int()%10000)
|
||||||
|
network := docker.Network{
|
||||||
|
Name: netid,
|
||||||
|
ID: fmt.Sprintf("%x", rand.Int()%10000),
|
||||||
|
Type: "bridge",
|
||||||
|
Endpoints: []*docker.Endpoint{
|
||||||
|
&docker.Endpoint{
|
||||||
|
Name: "blah",
|
||||||
|
ID: fmt.Sprintf("%x", rand.Int()%10000),
|
||||||
|
Network: netid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
server.networks = append(server.networks, &network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListNetworks(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
addNetworks(&server, 2)
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
request, _ := http.NewRequest("GET", "/networks", nil)
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusOK {
|
||||||
|
t.Errorf("ListNetworks: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
||||||
|
}
|
||||||
|
expected := make([]docker.Network, 2)
|
||||||
|
for i, network := range server.networks {
|
||||||
|
expected[i] = docker.Network{
|
||||||
|
ID: network.ID,
|
||||||
|
Name: network.Name,
|
||||||
|
Type: network.Type,
|
||||||
|
Endpoints: network.Endpoints,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var got []docker.Network
|
||||||
|
err := json.NewDecoder(recorder.Body).Decode(&got)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, expected) {
|
||||||
|
t.Errorf("ListNetworks. Want %#v. Got %#v.", expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type createNetworkResponse struct {
|
||||||
|
ID string `json:"ID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateNetwork(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
netid := fmt.Sprintf("%x", rand.Int()%10000)
|
||||||
|
netname := fmt.Sprintf("%x", rand.Int()%10000)
|
||||||
|
body := fmt.Sprintf(`{"ID": "%s", "Name": "%s", "Type": "bridge" }`, netid, netname)
|
||||||
|
request, _ := http.NewRequest("POST", "/networks", strings.NewReader(body))
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusCreated {
|
||||||
|
t.Errorf("CreateNetwork: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
var returned createNetworkResponse
|
||||||
|
err := json.NewDecoder(recorder.Body).Decode(&returned)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
stored := server.networks[0]
|
||||||
|
if returned.ID != stored.ID {
|
||||||
|
t.Errorf("CreateNetwork: ID mismatch. Stored: %q. Returned: %q.", stored.ID, returned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateNetworkInvalidBody(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
request, _ := http.NewRequest("POST", "/networks", strings.NewReader("whaaaaaat---"))
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusBadRequest {
|
||||||
|
t.Errorf("CreateNetwork: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateNetworkDuplicateName(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
server.buildMuxer()
|
||||||
|
addNetworks(&server, 1)
|
||||||
|
server.networks[0].Name = "mynetwork"
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
body := fmt.Sprintf(`{"ID": "%s", "Name": "mynetwork", "Type": "bridge" }`, fmt.Sprintf("%x", rand.Int()%10000))
|
||||||
|
request, _ := http.NewRequest("POST", "/networks", strings.NewReader(body))
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusForbidden {
|
||||||
|
t.Errorf("CreateNetwork: wrong status. Want %d. Got %d.", http.StatusForbidden, recorder.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user