diff --git a/api/docker/client.go b/api/docker/client.go index b80c772da..5b55e44dc 100644 --- a/api/docker/client.go +++ b/api/docker/client.go @@ -15,7 +15,7 @@ import ( var errUnsupportedEnvironmentType = errors.New("Environment not supported") const ( - defaultDockerRequestTimeout = 60 + defaultDockerRequestTimeout = 60 * time.Second dockerClientVersion = "1.37" ) @@ -33,22 +33,23 @@ func NewClientFactory(signatureService portainer.DigitalSignatureService, revers } } -// createClient is a generic function to create a Docker client based on +// CreateClient is a generic function to create a Docker client based on // a specific environment(endpoint) configuration. The nodeName parameter can be used // with an agent enabled environment(endpoint) to target a specific node in an agent cluster. -func (factory *ClientFactory) CreateClient(endpoint *portainer.Endpoint, nodeName string) (*client.Client, error) { +// The underlying http client timeout may be specified, a default value is used otherwise. +func (factory *ClientFactory) CreateClient(endpoint *portainer.Endpoint, nodeName string, timeout *time.Duration) (*client.Client, error) { if endpoint.Type == portainer.AzureEnvironment { return nil, errUnsupportedEnvironmentType } else if endpoint.Type == portainer.AgentOnDockerEnvironment { - return createAgentClient(endpoint, factory.signatureService, nodeName) + return createAgentClient(endpoint, factory.signatureService, nodeName, timeout) } else if endpoint.Type == portainer.EdgeAgentOnDockerEnvironment { - return createEdgeClient(endpoint, factory.signatureService, factory.reverseTunnelService, nodeName) + return createEdgeClient(endpoint, factory.signatureService, factory.reverseTunnelService, nodeName, timeout) } if strings.HasPrefix(endpoint.URL, "unix://") || strings.HasPrefix(endpoint.URL, "npipe://") { return createLocalClient(endpoint) } - return createTCPClient(endpoint) + return createTCPClient(endpoint, timeout) } func createLocalClient(endpoint *portainer.Endpoint) (*client.Client, error) { @@ -58,8 +59,8 @@ func createLocalClient(endpoint *portainer.Endpoint) (*client.Client, error) { ) } -func createTCPClient(endpoint *portainer.Endpoint) (*client.Client, error) { - httpCli, err := httpClient(endpoint) +func createTCPClient(endpoint *portainer.Endpoint, timeout *time.Duration) (*client.Client, error) { + httpCli, err := httpClient(endpoint, timeout) if err != nil { return nil, err } @@ -71,8 +72,8 @@ func createTCPClient(endpoint *portainer.Endpoint) (*client.Client, error) { ) } -func createEdgeClient(endpoint *portainer.Endpoint, signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService, nodeName string) (*client.Client, error) { - httpCli, err := httpClient(endpoint) +func createEdgeClient(endpoint *portainer.Endpoint, signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService, nodeName string, timeout *time.Duration) (*client.Client, error) { + httpCli, err := httpClient(endpoint, timeout) if err != nil { return nil, err } @@ -95,7 +96,7 @@ func createEdgeClient(endpoint *portainer.Endpoint, signatureService portainer.D if err != nil { return nil, err } - + endpointURL := fmt.Sprintf("http://127.0.0.1:%d", tunnel.Port) return client.NewClientWithOpts( @@ -106,8 +107,8 @@ func createEdgeClient(endpoint *portainer.Endpoint, signatureService portainer.D ) } -func createAgentClient(endpoint *portainer.Endpoint, signatureService portainer.DigitalSignatureService, nodeName string) (*client.Client, error) { - httpCli, err := httpClient(endpoint) +func createAgentClient(endpoint *portainer.Endpoint, signatureService portainer.DigitalSignatureService, nodeName string, timeout *time.Duration) (*client.Client, error) { + httpCli, err := httpClient(endpoint, timeout) if err != nil { return nil, err } @@ -134,7 +135,7 @@ func createAgentClient(endpoint *portainer.Endpoint, signatureService portainer. ) } -func httpClient(endpoint *portainer.Endpoint) (*http.Client, error) { +func httpClient(endpoint *portainer.Endpoint, timeout *time.Duration) (*http.Client, error) { transport := &http.Transport{} if endpoint.TLSConfig.TLS { @@ -145,8 +146,13 @@ func httpClient(endpoint *portainer.Endpoint) (*http.Client, error) { transport.TLSClientConfig = tlsConfig } + clientTimeout := defaultDockerRequestTimeout + if timeout != nil { + clientTimeout = *timeout + } + return &http.Client{ Transport: transport, - Timeout: defaultDockerRequestTimeout * time.Second, + Timeout: clientTimeout, }, nil } diff --git a/api/docker/snapshot.go b/api/docker/snapshot.go index 553a94847..2d638fb55 100644 --- a/api/docker/snapshot.go +++ b/api/docker/snapshot.go @@ -26,7 +26,7 @@ func NewSnapshotter(clientFactory *ClientFactory) *Snapshotter { // CreateSnapshot creates a snapshot of a specific Docker environment(endpoint) func (snapshotter *Snapshotter) CreateSnapshot(endpoint *portainer.Endpoint) (*portainer.DockerSnapshot, error) { - cli, err := snapshotter.clientFactory.CreateClient(endpoint, "") + cli, err := snapshotter.clientFactory.CreateClient(endpoint, "", nil) if err != nil { return nil, err } diff --git a/api/hostmanagement/openamt/openamt.go b/api/hostmanagement/openamt/openamt.go index f00ecc3cb..2f934bbc6 100644 --- a/api/hostmanagement/openamt/openamt.go +++ b/api/hostmanagement/openamt/openamt.go @@ -19,7 +19,7 @@ const ( defaultWirelessConfigName = "wirelessProfileDefault" DefaultProfileName = "profileAMTDefault" - httpClientTimeout = 30 + httpClientTimeout = 5 * time.Minute powerUpState portainer.PowerState = 2 powerOffState portainer.PowerState = 8 @@ -38,7 +38,7 @@ func NewService(dataStore portainer.DataStore) *Service { } return &Service{ httpsClient: &http.Client{ - Timeout: time.Second * time.Duration(httpClientTimeout), + Timeout: httpClientTimeout, Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, diff --git a/api/http/handler/hostmanagement/openamt/amtactivation.go b/api/http/handler/hostmanagement/openamt/amtactivation.go index 8094bf505..d474007b0 100644 --- a/api/http/handler/hostmanagement/openamt/amtactivation.go +++ b/api/http/handler/hostmanagement/openamt/amtactivation.go @@ -41,8 +41,7 @@ func (handler *Handler) openAMTActivate(w http.ResponseWriter, r *http.Request) errMsg := fmt.Sprintf("%s is not an agent environment", endpoint.Name) return &httperror.HandlerError{http.StatusBadRequest, errMsg, errors.New(errMsg)} } - - + settings, err := handler.DataStore.Settings().Settings() if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve settings from the database", err} diff --git a/api/http/handler/hostmanagement/openamt/amtrpc.go b/api/http/handler/hostmanagement/openamt/amtrpc.go index 9711d07ab..78beefe05 100644 --- a/api/http/handler/hostmanagement/openamt/amtrpc.go +++ b/api/http/handler/hostmanagement/openamt/amtrpc.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "log" "net/http" + "time" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -37,6 +38,7 @@ const ( // TODO: this should get extracted to some configurable - don't assume Docker Hub is everyone's global namespace, or that they're allowed to pull images from the internet rpcGoImageName = "ptrrd/openamt:rpc-go-json" rpcGoContainerName = "openamt-rpc-go" + dockerClientTimeout = 5 * time.Minute ) // @id OpenAMTHostInfo @@ -103,7 +105,8 @@ func (handler *Handler) PullAndRunContainer(ctx context.Context, endpoint *porta // on the Docker standalone node (one per env :) // and later, on the specified node in the swarm, or kube. nodeName := "" - docker, err := handler.DockerClientFactory.CreateClient(endpoint, nodeName) + timeout := dockerClientTimeout + docker, err := handler.DockerClientFactory.CreateClient(endpoint, nodeName, &timeout) if err != nil { return "Unable to create Docker Client connection", err } @@ -239,13 +242,13 @@ func (handler *Handler) activateDevice(endpoint *portainer.Endpoint, settings po cmdLine := []string{ "activate", "-n", + "-v", "-u", fmt.Sprintf("wss://%s/activate", config.MPSServer), "-profile", "profileAMTDefault", "-d", config.DomainConfiguration.DomainName, "-password", config.Credentials.MPSPassword, } - _, err := handler.PullAndRunContainer(ctx, endpoint, rpcGoImageName, rpcGoContainerName, cmdLine) if err != nil { return err @@ -261,6 +264,7 @@ func (handler *Handler) deactivateDevice(endpoint *portainer.Endpoint, settings cmdLine := []string{ "deactivate", "-n", + "-v", "-u", fmt.Sprintf("wss://%s/activate", config.MPSServer), "-password", config.Credentials.MPSPassword, } diff --git a/api/http/handler/stacks/handler.go b/api/http/handler/stacks/handler.go index 4bb72ec8a..1fb16dfb0 100644 --- a/api/http/handler/stacks/handler.go +++ b/api/http/handler/stacks/handler.go @@ -154,7 +154,7 @@ func (handler *Handler) checkUniqueStackNameInDocker(endpoint *portainer.Endpoin return false, err } - dockerClient, err := handler.DockerClientFactory.CreateClient(endpoint, "") + dockerClient, err := handler.DockerClientFactory.CreateClient(endpoint, "", nil) if err != nil { return false, err } diff --git a/api/http/handler/webhooks/webhook_execute.go b/api/http/handler/webhooks/webhook_execute.go index fb1cbdfbd..9f0d1aa58 100644 --- a/api/http/handler/webhooks/webhook_execute.go +++ b/api/http/handler/webhooks/webhook_execute.go @@ -61,7 +61,7 @@ func (handler *Handler) webhookExecute(w http.ResponseWriter, r *http.Request) * } func (handler *Handler) executeServiceWebhook(w http.ResponseWriter, endpoint *portainer.Endpoint, resourceID string, imageTag string) *httperror.HandlerError { - dockerClient, err := handler.DockerClientFactory.CreateClient(endpoint, "") + dockerClient, err := handler.DockerClientFactory.CreateClient(endpoint, "", nil) if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Error creating docker client", err} } diff --git a/api/http/proxy/factory/docker/access_control.go b/api/http/proxy/factory/docker/access_control.go index 5f4c7bbe0..694bddd28 100644 --- a/api/http/proxy/factory/docker/access_control.go +++ b/api/http/proxy/factory/docker/access_control.go @@ -122,7 +122,7 @@ func (transport *Transport) createPrivateResourceControl(resourceIdentifier stri } func (transport *Transport) getInheritedResourceControlFromServiceOrStack(resourceIdentifier, nodeName string, resourceType portainer.ResourceControlType, resourceControls []portainer.ResourceControl) (*portainer.ResourceControl, error) { - client, err := transport.dockerClientFactory.CreateClient(transport.endpoint, nodeName) + client, err := transport.dockerClientFactory.CreateClient(transport.endpoint, nodeName, nil) if err != nil { return nil, err } diff --git a/api/http/proxy/factory/docker/volumes.go b/api/http/proxy/factory/docker/volumes.go index fcc272c22..c4b44c985 100644 --- a/api/http/proxy/factory/docker/volumes.go +++ b/api/http/proxy/factory/docker/volumes.go @@ -133,7 +133,7 @@ func (transport *Transport) decorateVolumeResourceCreationOperation(request *htt if volumeID != "" { agentTargetHeader := request.Header.Get(portainer.PortainerAgentTargetHeader) - cli, err := transport.dockerClientFactory.CreateClient(transport.endpoint, agentTargetHeader) + cli, err := transport.dockerClientFactory.CreateClient(transport.endpoint, agentTargetHeader, nil) if err != nil { return nil, err } @@ -219,7 +219,7 @@ func (transport *Transport) getDockerID() (string, error) { } } - client, err := transport.dockerClientFactory.CreateClient(transport.endpoint, "") + client, err := transport.dockerClientFactory.CreateClient(transport.endpoint, "", nil) if err != nil { return "", err } diff --git a/app/portainer/components/datatables/endpoints-datatable/endpointsDatatableController.js b/app/portainer/components/datatables/endpoints-datatable/endpointsDatatableController.js index 8a48d6d09..c2aac2a59 100644 --- a/app/portainer/components/datatables/endpoints-datatable/endpointsDatatableController.js +++ b/app/portainer/components/datatables/endpoints-datatable/endpointsDatatableController.js @@ -124,20 +124,6 @@ angular.module('portainer.app').controller('EndpointsDatatableController', [ }); }; - this.associateOpenAMTAsync = async function (endpoints, deviceId) { - for (let endpoint of endpoints) { - try { - await OpenAMTService.associateEndpoint(endpoint.Id, deviceId); - - Notifications.success('Successfully associated with OpenAMT', endpoint.Name); - } catch (err) { - Notifications.error('Failure', err, 'Unable to associate with OpenAMT'); - } - } - - $state.reload(); - }; - /** * Overridden */