// Copyright © 2024 Ory Corp // SPDX-License-Identifier: Apache-2.0 // Copyright 2018 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 ( "context" "encoding/json" "io" "net/http" ) // PluginPrivilege represents a privilege for a plugin. type PluginPrivilege struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"` Value []string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"` } // InstallPluginOptions specify parameters to the InstallPlugins function. // // See https://goo.gl/C4t7Tz for more details. type InstallPluginOptions struct { Remote string Name string Plugins []PluginPrivilege `qs:"-"` Auth AuthConfiguration Context context.Context } // InstallPlugins installs a plugin or returns an error in case of failure. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) InstallPlugins(opts InstallPluginOptions) error { path := "/plugins/pull?" + queryString(opts) resp, err := c.do("POST", path, doOptions{ data: opts.Plugins, context: opts.Context, }) if err != nil { return err } defer resp.Body.Close() return nil } // PluginSettings stores plugin settings. // // See https://goo.gl/C4t7Tz for more details. type PluginSettings struct { Env []string `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"` Args []string `json:"Args,omitempty" yaml:"Args,omitempty" toml:"Args,omitempty"` Devices []string `json:"Devices,omitempty" yaml:"Devices,omitempty" toml:"Devices,omitempty"` } // PluginInterface stores plugin interface. // // See https://goo.gl/C4t7Tz for more details. type PluginInterface struct { Types []string `json:"Types,omitempty" yaml:"Types,omitempty" toml:"Types,omitempty"` Socket string `json:"Socket,omitempty" yaml:"Socket,omitempty" toml:"Socket,omitempty"` } // PluginNetwork stores plugin network type. // // See https://goo.gl/C4t7Tz for more details. type PluginNetwork struct { Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"` } // PluginLinux stores plugin linux setting. // // See https://goo.gl/C4t7Tz for more details. type PluginLinux struct { Capabilities []string `json:"Capabilities,omitempty" yaml:"Capabilities,omitempty" toml:"Capabilities,omitempty"` AllowAllDevices bool `json:"AllowAllDevices,omitempty" yaml:"AllowAllDevices,omitempty" toml:"AllowAllDevices,omitempty"` Devices []PluginLinuxDevices `json:"Devices,omitempty" yaml:"Devices,omitempty" toml:"Devices,omitempty"` } // PluginLinuxDevices stores plugin linux device setting. // // See https://goo.gl/C4t7Tz for more details. type PluginLinuxDevices struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Description string `json:"Documentation,omitempty" yaml:"Documentation,omitempty" toml:"Documentation,omitempty"` Settable []string `json:"Settable,omitempty" yaml:"Settable,omitempty" toml:"Settable,omitempty"` Path string `json:"Path,omitempty" yaml:"Path,omitempty" toml:"Path,omitempty"` } // PluginEnv stores plugin environment. // // See https://goo.gl/C4t7Tz for more details. type PluginEnv struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"` Settable []string `json:"Settable,omitempty" yaml:"Settable,omitempty" toml:"Settable,omitempty"` Value string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"` } // PluginArgs stores plugin arguments. // // See https://goo.gl/C4t7Tz for more details. type PluginArgs struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"` Settable []string `json:"Settable,omitempty" yaml:"Settable,omitempty" toml:"Settable,omitempty"` Value []string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"` } // PluginUser stores plugin user. // // See https://goo.gl/C4t7Tz for more details. type PluginUser struct { UID int32 `json:"UID,omitempty" yaml:"UID,omitempty" toml:"UID,omitempty"` GID int32 `json:"GID,omitempty" yaml:"GID,omitempty" toml:"GID,omitempty"` } // PluginConfig stores plugin config. // // See https://goo.gl/C4t7Tz for more details. type PluginConfig struct { Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"` Documentation string Interface PluginInterface `json:"Interface,omitempty" yaml:"Interface,omitempty" toml:"Interface,omitempty"` Entrypoint []string `json:"Entrypoint,omitempty" yaml:"Entrypoint,omitempty" toml:"Entrypoint,omitempty"` WorkDir string `json:"WorkDir,omitempty" yaml:"WorkDir,omitempty" toml:"WorkDir,omitempty"` User PluginUser `json:"User,omitempty" yaml:"User,omitempty" toml:"User,omitempty"` Network PluginNetwork `json:"Network,omitempty" yaml:"Network,omitempty" toml:"Network,omitempty"` Linux PluginLinux `json:"Linux,omitempty" yaml:"Linux,omitempty" toml:"Linux,omitempty"` PropagatedMount string `json:"PropagatedMount,omitempty" yaml:"PropagatedMount,omitempty" toml:"PropagatedMount,omitempty"` Mounts []Mount `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"` Env []PluginEnv `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"` Args PluginArgs `json:"Args,omitempty" yaml:"Args,omitempty" toml:"Args,omitempty"` } // PluginDetail specify results from the ListPlugins function. // // See https://goo.gl/C4t7Tz for more details. type PluginDetail struct { ID string `json:"Id,omitempty" yaml:"Id,omitempty" toml:"Id,omitempty"` Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Tag string `json:"Tag,omitempty" yaml:"Tag,omitempty" toml:"Tag,omitempty"` Active bool `json:"Active,omitempty" yaml:"Active,omitempty" toml:"Active,omitempty"` Settings PluginSettings `json:"Settings,omitempty" yaml:"Settings,omitempty" toml:"Settings,omitempty"` Config PluginConfig `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"` } // ListPlugins returns pluginDetails or an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) ListPlugins(ctx context.Context) ([]PluginDetail, error) { resp, err := c.do("GET", "/plugins", doOptions{ context: ctx, }) if err != nil { return nil, err } defer resp.Body.Close() pluginDetails := make([]PluginDetail, 0) if err := json.NewDecoder(resp.Body).Decode(&pluginDetails); err != nil { return nil, err } return pluginDetails, nil } // ListFilteredPluginsOptions specify parameters to the ListFilteredPlugins function. // // See https://goo.gl/C4t7Tz for more details. type ListFilteredPluginsOptions struct { Filters map[string][]string Context context.Context } // ListFilteredPlugins returns pluginDetails or an error. // // See https://goo.gl/rmdmWg for more details. func (c *Client) ListFilteredPlugins(opts ListFilteredPluginsOptions) ([]PluginDetail, error) { path := "/plugins/json?" + queryString(opts) resp, err := c.do("GET", path, doOptions{ context: opts.Context, }) if err != nil { return nil, err } defer resp.Body.Close() pluginDetails := make([]PluginDetail, 0) if err := json.NewDecoder(resp.Body).Decode(&pluginDetails); err != nil { return nil, err } return pluginDetails, nil } // GetPluginPrivileges returns pulginPrivileges or an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) GetPluginPrivileges(name string, ctx context.Context) ([]PluginPrivilege, error) { resp, err := c.do("GET", "/plugins/privileges?remote="+name, doOptions{ context: ctx, }) if err != nil { return nil, err } defer resp.Body.Close() var pluginPrivileges []PluginPrivilege if err := json.NewDecoder(resp.Body).Decode(&pluginPrivileges); err != nil { return nil, err } return pluginPrivileges, nil } // InspectPlugins returns a pluginDetail or an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) InspectPlugins(name string, ctx context.Context) (*PluginDetail, error) { resp, err := c.do("GET", "/plugins/"+name+"/json", doOptions{ context: ctx, }) if err != nil { return nil, err } defer resp.Body.Close() if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return nil, &NoSuchPlugin{ID: name} } return nil, err } resp.Body.Close() var pluginDetail PluginDetail if err := json.NewDecoder(resp.Body).Decode(&pluginDetail); err != nil { return nil, err } return &pluginDetail, nil } // RemovePluginOptions specify parameters to the RemovePlugin function. // // See https://goo.gl/C4t7Tz for more details. type RemovePluginOptions struct { // The Name of the plugin. Name string `qs:"-"` Force bool `qs:"force"` Context context.Context } // RemovePlugin returns a PluginDetail or an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) RemovePlugin(opts RemovePluginOptions) (*PluginDetail, error) { path := "/plugins/" + opts.Name + "?" + queryString(opts) resp, err := c.do("DELETE", path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return nil, &NoSuchPlugin{ID: opts.Name} } return nil, err } resp.Body.Close() var pluginDetail PluginDetail if err := json.NewDecoder(resp.Body).Decode(&pluginDetail); err != nil { return nil, err } return &pluginDetail, nil } // EnablePluginOptions specify parameters to the EnablePlugin function. // // See https://goo.gl/C4t7Tz for more details. type EnablePluginOptions struct { // The Name of the plugin. Name string `qs:"-"` Timeout int64 `qs:"timeout"` Context context.Context } // EnablePlugin enables plugin that opts point or returns an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) EnablePlugin(opts EnablePluginOptions) error { path := "/plugins/" + opts.Name + "/enable?" + queryString(opts) resp, err := c.do("POST", path, doOptions{context: opts.Context}) if err != nil { return err } defer resp.Body.Close() return nil } // DisablePluginOptions specify parameters to the DisablePlugin function. // // See https://goo.gl/C4t7Tz for more details. type DisablePluginOptions struct { // The Name of the plugin. Name string `qs:"-"` Context context.Context } // DisablePlugin disables plugin that opts point or returns an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) DisablePlugin(opts DisablePluginOptions) error { path := "/plugins/" + opts.Name + "/disable" resp, err := c.do("POST", path, doOptions{context: opts.Context}) if err != nil { return err } defer resp.Body.Close() return nil } // CreatePluginOptions specify parameters to the CreatePlugin function. // // See https://goo.gl/C4t7Tz for more details. type CreatePluginOptions struct { // The Name of the plugin. Name string `qs:"name"` // Path to tar containing plugin Path string `qs:"-"` Context context.Context } // CreatePlugin creates plugin that opts point or returns an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) CreatePlugin(opts CreatePluginOptions) (string, error) { path := "/plugins/create?" + queryString(opts) resp, err := c.do("POST", path, doOptions{ data: opts.Path, context: opts.Context}) if err != nil { return "", err } defer resp.Body.Close() containerNameBytes, err := io.ReadAll(resp.Body) if err != nil { return "", err } return string(containerNameBytes), nil } // PushPluginOptions specify parameters to PushPlugin function. // // See https://goo.gl/C4t7Tz for more details. type PushPluginOptions struct { // The Name of the plugin. Name string Context context.Context } // PushPlugin pushes plugin that opts point or returns an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) PushPlugin(opts PushPluginOptions) error { path := "/plugins/" + opts.Name + "/push" resp, err := c.do("POST", path, doOptions{context: opts.Context}) if err != nil { return err } defer resp.Body.Close() return nil } // ConfigurePluginOptions specify parameters to the ConfigurePlugin // // See https://goo.gl/C4t7Tz for more details. type ConfigurePluginOptions struct { // The Name of the plugin. Name string `qs:"name"` Envs []string Context context.Context } // ConfigurePlugin configures plugin that opts point or returns an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) ConfigurePlugin(opts ConfigurePluginOptions) error { path := "/plugins/" + opts.Name + "/set" resp, err := c.do("POST", path, doOptions{ data: opts.Envs, context: opts.Context, }) if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return &NoSuchPlugin{ID: opts.Name} } return err } defer resp.Body.Close() return nil } // NoSuchPlugin is the error returned when a given plugin does not exist. type NoSuchPlugin struct { ID string Err error } func (err *NoSuchPlugin) Error() string { if err.Err != nil { return err.Err.Error() } return "No such plugin: " + err.ID }