Add HTTP client timeout.
Signed-off-by: Anusha Ragunathan <anusha@docker.com>
(cherry picked from commit 83ca993c15
)
Signed-off-by: Victor Vieux <victorvieux@gmail.com>
This commit is contained in:
parent
abc0eea899
commit
0403addc5f
16 changed files with 96 additions and 23 deletions
|
@ -11,7 +11,7 @@ import (
|
||||||
// Backend for Plugin
|
// Backend for Plugin
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
Disable(name string) error
|
Disable(name string) error
|
||||||
Enable(name string) error
|
Enable(name string, config *enginetypes.PluginEnableConfig) error
|
||||||
List() ([]enginetypes.Plugin, error)
|
List() ([]enginetypes.Plugin, error)
|
||||||
Inspect(name string) (enginetypes.Plugin, error)
|
Inspect(name string) (enginetypes.Plugin, error)
|
||||||
Remove(name string, config *enginetypes.PluginRmConfig) error
|
Remove(name string, config *enginetypes.PluginRmConfig) error
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
|
@ -56,7 +57,18 @@ func (pr *pluginRouter) createPlugin(ctx context.Context, w http.ResponseWriter,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
return pr.backend.Enable(vars["name"])
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
name := vars["name"]
|
||||||
|
timeout, err := strconv.Atoi(r.Form.Get("timeout"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config := &types.PluginEnableConfig{Timeout: timeout}
|
||||||
|
|
||||||
|
return pr.backend.Enable(name, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pr *pluginRouter) disablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (pr *pluginRouter) disablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
|
|
@ -6307,7 +6307,7 @@ paths:
|
||||||
summary: "Install a plugin"
|
summary: "Install a plugin"
|
||||||
operationId: "PostPluginsPull"
|
operationId: "PostPluginsPull"
|
||||||
description: |
|
description: |
|
||||||
Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginEnable).
|
Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable).
|
||||||
produces:
|
produces:
|
||||||
- "application/json"
|
- "application/json"
|
||||||
responses:
|
responses:
|
||||||
|
@ -6430,6 +6430,11 @@ paths:
|
||||||
description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted."
|
description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted."
|
||||||
required: true
|
required: true
|
||||||
type: "string"
|
type: "string"
|
||||||
|
- name: "timeout"
|
||||||
|
in: "query"
|
||||||
|
description: "Set the HTTP client timeout (in seconds)"
|
||||||
|
type: "integer"
|
||||||
|
default: 0
|
||||||
tags:
|
tags:
|
||||||
- "Plugins"
|
- "Plugins"
|
||||||
/plugins/{name}/disable:
|
/plugins/{name}/disable:
|
||||||
|
|
|
@ -332,6 +332,11 @@ type PluginRemoveOptions struct {
|
||||||
Force bool
|
Force bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PluginEnableOptions holds parameters to enable plugins.
|
||||||
|
type PluginEnableOptions struct {
|
||||||
|
Timeout int
|
||||||
|
}
|
||||||
|
|
||||||
// PluginInstallOptions holds parameters to install a plugin.
|
// PluginInstallOptions holds parameters to install a plugin.
|
||||||
type PluginInstallOptions struct {
|
type PluginInstallOptions struct {
|
||||||
Disabled bool
|
Disabled bool
|
||||||
|
|
|
@ -59,3 +59,8 @@ type ExecConfig struct {
|
||||||
type PluginRmConfig struct {
|
type PluginRmConfig struct {
|
||||||
ForceRemove bool
|
ForceRemove bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PluginEnableConfig holds arguments for the plugin enable
|
||||||
|
type PluginEnableConfig struct {
|
||||||
|
Timeout int
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package plugin
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/cli"
|
"github.com/docker/docker/cli"
|
||||||
"github.com/docker/docker/cli/command"
|
"github.com/docker/docker/cli/command"
|
||||||
"github.com/docker/docker/reference"
|
"github.com/docker/docker/reference"
|
||||||
|
@ -10,20 +11,32 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type enableOpts struct {
|
||||||
|
timeout int
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
func newEnableCommand(dockerCli *command.DockerCli) *cobra.Command {
|
func newEnableCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
|
var opts enableOpts
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "enable PLUGIN",
|
Use: "enable PLUGIN",
|
||||||
Short: "Enable a plugin",
|
Short: "Enable a plugin",
|
||||||
Args: cli.ExactArgs(1),
|
Args: cli.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return runEnable(dockerCli, args[0])
|
opts.name = args[0]
|
||||||
|
return runEnable(dockerCli, &opts)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.IntVar(&opts.timeout, "timeout", 0, "HTTP client timeout (in seconds)")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runEnable(dockerCli *command.DockerCli, name string) error {
|
func runEnable(dockerCli *command.DockerCli, opts *enableOpts) error {
|
||||||
|
name := opts.name
|
||||||
|
|
||||||
named, err := reference.ParseNamed(name) // FIXME: validate
|
named, err := reference.ParseNamed(name) // FIXME: validate
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -35,7 +48,11 @@ func runEnable(dockerCli *command.DockerCli, name string) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("invalid name: %s", named.String())
|
return fmt.Errorf("invalid name: %s", named.String())
|
||||||
}
|
}
|
||||||
if err := dockerCli.Client().PluginEnable(context.Background(), ref.String()); err != nil {
|
if opts.timeout < 0 {
|
||||||
|
return fmt.Errorf("negative timeout %d is invalid", opts.timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dockerCli.Client().PluginEnable(context.Background(), ref.String(), types.PluginEnableOptions{Timeout: opts.timeout}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintln(dockerCli.Out(), name)
|
fmt.Fprintln(dockerCli.Out(), name)
|
||||||
|
|
|
@ -109,7 +109,7 @@ type NodeAPIClient interface {
|
||||||
type PluginAPIClient interface {
|
type PluginAPIClient interface {
|
||||||
PluginList(ctx context.Context) (types.PluginsListResponse, error)
|
PluginList(ctx context.Context) (types.PluginsListResponse, error)
|
||||||
PluginRemove(ctx context.Context, name string, options types.PluginRemoveOptions) error
|
PluginRemove(ctx context.Context, name string, options types.PluginRemoveOptions) error
|
||||||
PluginEnable(ctx context.Context, name string) error
|
PluginEnable(ctx context.Context, name string, options types.PluginEnableOptions) error
|
||||||
PluginDisable(ctx context.Context, name string) error
|
PluginDisable(ctx context.Context, name string) error
|
||||||
PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) error
|
PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) error
|
||||||
PluginPush(ctx context.Context, name string, registryAuth string) error
|
PluginPush(ctx context.Context, name string, registryAuth string) error
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PluginEnable enables a plugin
|
// PluginEnable enables a plugin
|
||||||
func (cli *Client) PluginEnable(ctx context.Context, name string) error {
|
func (cli *Client) PluginEnable(ctx context.Context, name string, options types.PluginEnableOptions) error {
|
||||||
resp, err := cli.post(ctx, "/plugins/"+name+"/enable", nil, nil, nil)
|
query := url.Values{}
|
||||||
|
query.Set("timeout", strconv.Itoa(options.Timeout))
|
||||||
|
|
||||||
|
resp, err := cli.post(ctx, "/plugins/"+name+"/enable", query, nil, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ func TestPluginEnableError(t *testing.T) {
|
||||||
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.PluginEnable(context.Background(), "plugin_name")
|
err := client.PluginEnable(context.Background(), "plugin_name", types.PluginEnableOptions{})
|
||||||
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
||||||
t.Fatalf("expected a Server Error, got %v", err)
|
t.Fatalf("expected a Server Error, got %v", err)
|
||||||
}
|
}
|
||||||
|
@ -40,7 +41,7 @@ func TestPluginEnable(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.PluginEnable(context.Background(), "plugin_name")
|
err := client.PluginEnable(context.Background(), "plugin_name", types.PluginEnableOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return cli.PluginEnable(ctx, name)
|
return cli.PluginEnable(ctx, name, types.PluginEnableOptions{Timeout: 0})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
|
func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
|
||||||
|
|
|
@ -14,12 +14,12 @@ if [ ${#files[@]} -gt 0 ]; then
|
||||||
diffs="$(git status --porcelain -- api/types/ 2>/dev/null)"
|
diffs="$(git status --porcelain -- api/types/ 2>/dev/null)"
|
||||||
if [ "$diffs" ]; then
|
if [ "$diffs" ]; then
|
||||||
{
|
{
|
||||||
echo 'The result of hack/geneate-swagger-api.sh differs'
|
echo 'The result of hack/generate-swagger-api.sh differs'
|
||||||
echo
|
echo
|
||||||
echo "$diffs"
|
echo "$diffs"
|
||||||
echo
|
echo
|
||||||
echo 'Please update api/swagger.yaml with any api changes, then '
|
echo 'Please update api/swagger.yaml with any api changes, then '
|
||||||
echo 'run `hack/geneate-swagger-api.sh`.'
|
echo 'run `hack/generate-swagger-api.sh`.'
|
||||||
} >&2
|
} >&2
|
||||||
false
|
false
|
||||||
else
|
else
|
||||||
|
|
|
@ -19,8 +19,7 @@ const (
|
||||||
defaultTimeOut = 30
|
defaultTimeOut = 30
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewClient creates a new plugin client (http).
|
func newTransport(addr string, tlsConfig *tlsconfig.Options) (transport.Transport, error) {
|
||||||
func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) {
|
|
||||||
tr := &http.Transport{}
|
tr := &http.Transport{}
|
||||||
|
|
||||||
if tlsConfig != nil {
|
if tlsConfig != nil {
|
||||||
|
@ -45,15 +44,33 @@ func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) {
|
||||||
}
|
}
|
||||||
scheme := httpScheme(u)
|
scheme := httpScheme(u)
|
||||||
|
|
||||||
clientTransport := transport.NewHTTPTransport(tr, scheme, socket)
|
return transport.NewHTTPTransport(tr, scheme, socket), nil
|
||||||
return NewClientWithTransport(clientTransport), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClientWithTransport creates a new plugin client with a given transport.
|
// NewClient creates a new plugin client (http).
|
||||||
func NewClientWithTransport(tr transport.Transport) *Client {
|
func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) {
|
||||||
|
clientTransport, err := newTransport(addr, tlsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newClientWithTransport(clientTransport, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientWithTimeout creates a new plugin client (http).
|
||||||
|
func NewClientWithTimeout(addr string, tlsConfig *tlsconfig.Options, timeoutInSecs int) (*Client, error) {
|
||||||
|
clientTransport, err := newTransport(addr, tlsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newClientWithTransport(clientTransport, timeoutInSecs), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newClientWithTransport creates a new plugin client with a given transport.
|
||||||
|
func newClientWithTransport(tr transport.Transport, timeoutInSecs int) *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
http: &http.Client{
|
http: &http.Client{
|
||||||
Transport: tr,
|
Transport: tr,
|
||||||
|
Timeout: time.Duration(timeoutInSecs) * time.Second,
|
||||||
},
|
},
|
||||||
requestFactory: tr,
|
requestFactory: tr,
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,14 @@ func (pm *Manager) Disable(name string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable activates a plugin, which implies that they are ready to be used by containers.
|
// Enable activates a plugin, which implies that they are ready to be used by containers.
|
||||||
func (pm *Manager) Enable(name string) error {
|
func (pm *Manager) Enable(name string, config *types.PluginEnableConfig) error {
|
||||||
|
|
||||||
p, err := pm.pluginStore.GetByName(name)
|
p, err := pm.pluginStore.GetByName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.TimeoutInSecs = config.Timeout
|
||||||
if err := pm.enable(p, false); err != nil {
|
if err := pm.enable(p, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ func (pm *Manager) Disable(name string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable activates a plugin, which implies that they are ready to be used by containers.
|
// Enable activates a plugin, which implies that they are ready to be used by containers.
|
||||||
func (pm *Manager) Enable(name string) error {
|
func (pm *Manager) Enable(name string, config *types.PluginEnableConfig) error {
|
||||||
return errNotSupported
|
return errNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ func (pm *Manager) enable(p *v2.Plugin, force bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p.PClient, err = plugins.NewClient("unix://"+filepath.Join(p.RuntimeSourcePath, p.GetSocket()), nil)
|
p.PClient, err = plugins.NewClientWithTimeout("unix://"+filepath.Join(p.RuntimeSourcePath, p.GetSocket()), nil, p.TimeoutInSecs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.Lock()
|
p.Lock()
|
||||||
p.Restart = false
|
p.Restart = false
|
||||||
|
|
|
@ -24,6 +24,7 @@ type Plugin struct {
|
||||||
Restart bool `json:"-"`
|
Restart bool `json:"-"`
|
||||||
ExitChan chan bool `json:"-"`
|
ExitChan chan bool `json:"-"`
|
||||||
LibRoot string `json:"-"`
|
LibRoot string `json:"-"`
|
||||||
|
TimeoutInSecs int `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPluginRuntimeDestination = "/run/docker/plugins"
|
const defaultPluginRuntimeDestination = "/run/docker/plugins"
|
||||||
|
|
Loading…
Reference in a new issue