diff --git a/api/client/idresolver/idresolver.go b/api/client/idresolver/idresolver.go index 05c4c9c366..b958bd0672 100644 --- a/api/client/idresolver/idresolver.go +++ b/api/client/idresolver/idresolver.go @@ -40,7 +40,7 @@ func (r *IDResolver) get(ctx context.Context, t interface{}, id string) (string, } return id, nil case swarm.Service: - service, err := r.client.ServiceInspect(ctx, id) + service, _, err := r.client.ServiceInspectWithRaw(ctx, id) if err != nil { return id, nil } diff --git a/api/client/plugin/install.go b/api/client/plugin/install.go index ef890b1f90..486dc4f622 100644 --- a/api/client/plugin/install.go +++ b/api/client/plugin/install.go @@ -3,12 +3,15 @@ package plugin import ( + "bufio" "fmt" + "strings" "github.com/docker/docker/api/client" "github.com/docker/docker/cli" "github.com/docker/docker/reference" "github.com/docker/docker/registry" + "github.com/docker/engine-api/types" "github.com/spf13/cobra" "golang.org/x/net/context" ) @@ -36,8 +39,8 @@ func newInstallCommand(dockerCli *client.DockerCli) *cobra.Command { return cmd } -func runInstall(dockerCli *client.DockerCli, options pluginOptions) error { - named, err := reference.ParseNamed(options.name) // FIXME: validate +func runInstall(dockerCli *client.DockerCli, opts pluginOptions) error { + named, err := reference.ParseNamed(opts.name) // FIXME: validate if err != nil { return err } @@ -56,6 +59,34 @@ func runInstall(dockerCli *client.DockerCli, options pluginOptions) error { if err != nil { return err } - // TODO: pass noEnable flag - return dockerCli.Client().PluginInstall(ctx, ref.String(), encodedAuth, options.grantPerms, false, dockerCli.In(), dockerCli.Out()) + + requestPrivilege := dockerCli.RegistryAuthenticationPrivilegedFunc(repoInfo.Index, "plugin install") + + // TODO: pass acceptAllPermissions and noEnable flag + options := types.PluginInstallOptions{ + RegistryAuth: encodedAuth, + Disabled: false, + AcceptAllPermissions: opts.grantPerms, + AcceptPermissionsFunc: acceptPrivileges(dockerCli, opts.name), + PrivilegeFunc: requestPrivilege, + } + + return dockerCli.Client().PluginInstall(ctx, ref.String(), options) +} + +func acceptPrivileges(dockerCli *client.DockerCli, name string) func(privileges types.PluginPrivileges) (bool, error) { + return func(privileges types.PluginPrivileges) (bool, error) { + fmt.Fprintf(dockerCli.Out(), "Plugin %q requested the following privileges:\n", name) + for _, privilege := range privileges { + fmt.Fprintf(dockerCli.Out(), " - %s: %v\n", privilege.Name, privilege.Value) + } + + fmt.Fprint(dockerCli.Out(), "Do you grant the above permissions? [y/N] ") + reader := bufio.NewReader(dockerCli.In()) + line, _, err := reader.ReadLine() + if err != nil { + return false, err + } + return strings.ToLower(string(line)) == "y", nil + } } diff --git a/api/client/service/inspect.go b/api/client/service/inspect.go index bd36d3ed06..1c2edb051a 100644 --- a/api/client/service/inspect.go +++ b/api/client/service/inspect.go @@ -50,7 +50,7 @@ func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error { ctx := context.Background() getRef := func(ref string) (interface{}, []byte, error) { - service, err := client.ServiceInspect(ctx, ref) + service, _, err := client.ServiceInspectWithRaw(ctx, ref) if err == nil || !apiclient.IsErrServiceNotFound(err) { return service, nil, err } diff --git a/api/client/service/scale.go b/api/client/service/scale.go index 2962f29091..e313948aca 100644 --- a/api/client/service/scale.go +++ b/api/client/service/scale.go @@ -61,7 +61,8 @@ func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string client := dockerCli.Client() ctx := context.Background() - service, err := client.ServiceInspect(ctx, serviceID) + service, _, err := client.ServiceInspectWithRaw(ctx, serviceID) + if err != nil { return err } diff --git a/api/client/service/tasks.go b/api/client/service/tasks.go index 6169d8bdb7..7520b1f16d 100644 --- a/api/client/service/tasks.go +++ b/api/client/service/tasks.go @@ -44,7 +44,7 @@ func runTasks(dockerCli *client.DockerCli, opts tasksOptions) error { client := dockerCli.Client() ctx := context.Background() - service, err := client.ServiceInspect(ctx, opts.serviceID) + service, _, err := client.ServiceInspectWithRaw(ctx, opts.serviceID) if err != nil { return err } diff --git a/api/client/service/update.go b/api/client/service/update.go index 20bc386ba3..c8e3a6d7a5 100644 --- a/api/client/service/update.go +++ b/api/client/service/update.go @@ -41,7 +41,7 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID stri client := dockerCli.Client() ctx := context.Background() - service, err := client.ServiceInspect(ctx, serviceID) + service, _, err := client.ServiceInspectWithRaw(ctx, serviceID) if err != nil { return err } diff --git a/hack/vendor.sh b/hack/vendor.sh index dc7913981e..93cc52cd6c 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -60,7 +60,7 @@ clone git golang.org/x/net 2beffdc2e92c8a3027590f898fe88f69af48a3f8 https://gith clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3 clone git github.com/docker/go-connections fa2850ff103453a9ad190da0df0af134f0314b3d -clone git github.com/docker/engine-api f50fbe5f9c4c8eeed591549d2c8187f4076f3717 +clone git github.com/docker/engine-api c57d0447ea1ae71f6dad83c8d8a1215a89869a0c clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837 clone git github.com/imdario/mergo 0.2.1 diff --git a/vendor/src/github.com/docker/engine-api/client/interface.go b/vendor/src/github.com/docker/engine-api/client/interface.go index 7fb04aced3..929a6bc72a 100644 --- a/vendor/src/github.com/docker/engine-api/client/interface.go +++ b/vendor/src/github.com/docker/engine-api/client/interface.go @@ -101,7 +101,7 @@ type NodeAPIClient interface { // ServiceAPIClient defines API client methods for the services type ServiceAPIClient interface { ServiceCreate(ctx context.Context, service swarm.ServiceSpec) (types.ServiceCreateResponse, error) - ServiceInspect(ctx context.Context, serviceID string) (swarm.Service, error) + ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) ServiceRemove(ctx context.Context, serviceID string) error ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec) error diff --git a/vendor/src/github.com/docker/engine-api/client/interface_experimental.go b/vendor/src/github.com/docker/engine-api/client/interface_experimental.go index a63ae70bb6..eb0cd7bf14 100644 --- a/vendor/src/github.com/docker/engine-api/client/interface_experimental.go +++ b/vendor/src/github.com/docker/engine-api/client/interface_experimental.go @@ -3,8 +3,6 @@ package client import ( - "io" - "github.com/docker/engine-api/types" "golang.org/x/net/context" ) @@ -29,7 +27,7 @@ type PluginAPIClient interface { PluginRemove(ctx context.Context, name string) error PluginEnable(ctx context.Context, name string) error PluginDisable(ctx context.Context, name string) error - PluginInstall(ctx context.Context, name, registryAuth string, acceptAllPermissions, noEnable bool, in io.ReadCloser, out io.Writer) error + PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) error PluginPush(ctx context.Context, name string, registryAuth string) error PluginSet(ctx context.Context, name string, args []string) error PluginInspect(ctx context.Context, name string) (*types.Plugin, error) diff --git a/vendor/src/github.com/docker/engine-api/client/plugin_install.go b/vendor/src/github.com/docker/engine-api/client/plugin_install.go index 9005e81a56..914376f512 100644 --- a/vendor/src/github.com/docker/engine-api/client/plugin_install.go +++ b/vendor/src/github.com/docker/engine-api/client/plugin_install.go @@ -3,21 +3,28 @@ package client import ( - "bufio" "encoding/json" - "fmt" - "io" + "net/http" "net/url" - "strings" "github.com/docker/engine-api/types" "golang.org/x/net/context" ) // PluginInstall installs a plugin -func (cli *Client) PluginInstall(ctx context.Context, name, registryAuth string, acceptAllPermissions, noEnable bool, in io.ReadCloser, out io.Writer) error { - headers := map[string][]string{"X-Registry-Auth": {registryAuth}} - resp, err := cli.post(ctx, "/plugins/pull", url.Values{"name": []string{name}}, nil, headers) +func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) error { + // FIXME(vdemeester) name is a ref, we might want to parse/validate it here. + query := url.Values{} + query.Set("name", name) + resp, err := cli.tryPluginPull(ctx, query, options.RegistryAuth) + if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil { + newAuthHeader, privilegeErr := options.PrivilegeFunc() + if privilegeErr != nil { + ensureReaderClosed(resp) + return privilegeErr + } + resp, err = cli.tryPluginPull(ctx, query, newAuthHeader) + } if err != nil { ensureReaderClosed(resp) return err @@ -28,27 +35,24 @@ func (cli *Client) PluginInstall(ctx context.Context, name, registryAuth string, } ensureReaderClosed(resp) - if !acceptAllPermissions && len(privileges) > 0 { - - fmt.Fprintf(out, "Plugin %q requested the following privileges:\n", name) - for _, privilege := range privileges { - fmt.Fprintf(out, " - %s: %v\n", privilege.Name, privilege.Value) - } - - fmt.Fprint(out, "Do you grant the above permissions? [y/N] ") - reader := bufio.NewReader(in) - line, _, err := reader.ReadLine() + if !options.AcceptAllPermissions && options.AcceptPermissionsFunc != nil && len(privileges) > 0 { + accept, err := options.AcceptPermissionsFunc(privileges) if err != nil { return err } - if strings.ToLower(string(line)) != "y" { + if !accept { resp, _ := cli.delete(ctx, "/plugins/"+name, nil, nil) ensureReaderClosed(resp) return pluginPermissionDenied{name} } } - if noEnable { + if options.Disabled { return nil } return cli.PluginEnable(ctx, name) } + +func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, registryAuth string) (*serverResponse, error) { + headers := map[string][]string{"X-Registry-Auth": {registryAuth}} + return cli.post(ctx, "/plugins/pull", query, nil, headers) +} diff --git a/vendor/src/github.com/docker/engine-api/client/service_inspect.go b/vendor/src/github.com/docker/engine-api/client/service_inspect.go index 3dbb8cd560..958cd662ea 100644 --- a/vendor/src/github.com/docker/engine-api/client/service_inspect.go +++ b/vendor/src/github.com/docker/engine-api/client/service_inspect.go @@ -1,25 +1,33 @@ package client import ( + "bytes" "encoding/json" + "io/ioutil" "net/http" "github.com/docker/engine-api/types/swarm" "golang.org/x/net/context" ) -// ServiceInspect returns the service information. -func (cli *Client) ServiceInspect(ctx context.Context, serviceID string) (swarm.Service, error) { +// ServiceInspectWithRaw returns the service information and the raw data. +func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error) { serverResp, err := cli.get(ctx, "/services/"+serviceID, nil, nil) if err != nil { if serverResp.statusCode == http.StatusNotFound { - return swarm.Service{}, serviceNotFoundError{serviceID} + return swarm.Service{}, nil, serviceNotFoundError{serviceID} } - return swarm.Service{}, err + return swarm.Service{}, nil, err + } + defer ensureReaderClosed(serverResp) + + body, err := ioutil.ReadAll(serverResp.body) + if err != nil { + return swarm.Service{}, nil, err } var response swarm.Service - err = json.NewDecoder(serverResp.body).Decode(&response) - ensureReaderClosed(serverResp) - return response, err + rdr := bytes.NewReader(body) + err = json.NewDecoder(rdr).Decode(&response) + return response, body, err } diff --git a/vendor/src/github.com/docker/engine-api/types/plugin.go b/vendor/src/github.com/docker/engine-api/types/plugin.go index f9df9efe05..05030ff3de 100644 --- a/vendor/src/github.com/docker/engine-api/types/plugin.go +++ b/vendor/src/github.com/docker/engine-api/types/plugin.go @@ -7,6 +7,15 @@ import ( "fmt" ) +// PluginInstallOptions holds parameters to install a plugin. +type PluginInstallOptions struct { + Disabled bool + AcceptAllPermissions bool + RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry + PrivilegeFunc RequestPrivilegeFunc + AcceptPermissionsFunc func(PluginPrivileges) (bool, error) +} + // PluginConfig represents the values of settings potentially modifiable by a user type PluginConfig struct { Mounts []PluginMount