Skip to content

Commit abc9298

Browse files
authored
fix(endpoints): add the ability to update TLS for an existing endpoint (portainer#784)
1 parent 44e4842 commit abc9298

File tree

7 files changed

+90
-45
lines changed

7 files changed

+90
-45
lines changed

Diff for: api/http/docker_handler.go

+5-39
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,24 @@ import (
77

88
"log"
99
"net/http"
10-
"net/url"
1110
"os"
1211

1312
"github.com/gorilla/mux"
14-
"github.com/orcaman/concurrent-map"
1513
)
1614

1715
// DockerHandler represents an HTTP API handler for proxying requests to the Docker API.
1816
type DockerHandler struct {
1917
*mux.Router
2018
Logger *log.Logger
2119
EndpointService portainer.EndpointService
22-
ProxyFactory ProxyFactory
23-
proxies cmap.ConcurrentMap
20+
ProxyService *ProxyService
2421
}
2522

2623
// NewDockerHandler returns a new instance of DockerHandler.
2724
func NewDockerHandler(mw *middleWareService, resourceControlService portainer.ResourceControlService) *DockerHandler {
2825
h := &DockerHandler{
2926
Router: mux.NewRouter(),
3027
Logger: log.New(os.Stderr, "", log.LstdFlags),
31-
ProxyFactory: ProxyFactory{
32-
ResourceControlService: resourceControlService,
33-
},
34-
proxies: cmap.New(),
3528
}
3629
h.PathPrefix("/{id}/").Handler(
3730
mw.authenticated(http.HandlerFunc(h.proxyRequestsToDockerAPI)))
@@ -74,41 +67,14 @@ func (handler *DockerHandler) proxyRequestsToDockerAPI(w http.ResponseWriter, r
7467
}
7568

7669
var proxy http.Handler
77-
item, ok := handler.proxies.Get(string(endpointID))
78-
if !ok {
79-
proxy, err = handler.createAndRegisterEndpointProxy(endpoint)
70+
proxy = handler.ProxyService.GetProxy(string(endpointID))
71+
if proxy == nil {
72+
proxy, err = handler.ProxyService.CreateAndRegisterProxy(endpoint)
8073
if err != nil {
8174
Error(w, err, http.StatusBadRequest, handler.Logger)
8275
return
8376
}
84-
} else {
85-
proxy = item.(http.Handler)
8677
}
87-
http.StripPrefix("/"+id, proxy).ServeHTTP(w, r)
88-
}
89-
90-
func (handler *DockerHandler) createAndRegisterEndpointProxy(endpoint *portainer.Endpoint) (http.Handler, error) {
91-
var proxy http.Handler
9278

93-
endpointURL, err := url.Parse(endpoint.URL)
94-
if err != nil {
95-
return nil, err
96-
}
97-
98-
if endpointURL.Scheme == "tcp" {
99-
if endpoint.TLS {
100-
proxy, err = handler.ProxyFactory.newHTTPSProxy(endpointURL, endpoint)
101-
if err != nil {
102-
return nil, err
103-
}
104-
} else {
105-
proxy = handler.ProxyFactory.newHTTPProxy(endpointURL)
106-
}
107-
} else {
108-
// Assume unix:// scheme
109-
proxy = handler.ProxyFactory.newSocketProxy(endpointURL.Path)
110-
}
111-
112-
handler.proxies.Set(string(endpoint.ID), proxy)
113-
return proxy, nil
79+
http.StripPrefix("/"+id, proxy).ServeHTTP(w, r)
11480
}

Diff for: api/http/endpoint_handler.go

+9
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type EndpointHandler struct {
2020
authorizeEndpointManagement bool
2121
EndpointService portainer.EndpointService
2222
FileService portainer.FileService
23+
ProxyService *ProxyService
2324
}
2425

2526
const (
@@ -281,6 +282,12 @@ func (handler *EndpointHandler) handlePutEndpoint(w http.ResponseWriter, r *http
281282
}
282283
}
283284

285+
_, err = handler.ProxyService.CreateAndRegisterProxy(endpoint)
286+
if err != nil {
287+
Error(w, err, http.StatusInternalServerError, handler.Logger)
288+
return
289+
}
290+
284291
err = handler.EndpointService.UpdateEndpoint(endpoint.ID, endpoint)
285292
if err != nil {
286293
Error(w, err, http.StatusInternalServerError, handler.Logger)
@@ -320,6 +327,8 @@ func (handler *EndpointHandler) handleDeleteEndpoint(w http.ResponseWriter, r *h
320327
return
321328
}
322329

330+
handler.ProxyService.DeleteProxy(string(endpointID))
331+
323332
err = handler.EndpointService.DeleteEndpoint(portainer.EndpointID(endpointID))
324333
if err != nil {
325334
Error(w, err, http.StatusInternalServerError, handler.Logger)

Diff for: api/http/proxy.go

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package http
2+
3+
import (
4+
"net/http"
5+
"net/url"
6+
7+
"github.com/orcaman/concurrent-map"
8+
"github.com/portainer/portainer"
9+
)
10+
11+
// ProxyService represents a service used to manage Docker proxies.
12+
type ProxyService struct {
13+
proxyFactory *ProxyFactory
14+
proxies cmap.ConcurrentMap
15+
}
16+
17+
// NewProxyService initializes a new ProxyService
18+
func NewProxyService(resourceControlService portainer.ResourceControlService) *ProxyService {
19+
return &ProxyService{
20+
proxies: cmap.New(),
21+
proxyFactory: &ProxyFactory{
22+
ResourceControlService: resourceControlService,
23+
},
24+
}
25+
}
26+
27+
// CreateAndRegisterProxy creates a new HTTP reverse proxy and adds it to the registered proxies.
28+
// It can also be used to create a new HTTP reverse proxy and replace an already registered proxy.
29+
func (service *ProxyService) CreateAndRegisterProxy(endpoint *portainer.Endpoint) (http.Handler, error) {
30+
var proxy http.Handler
31+
32+
endpointURL, err := url.Parse(endpoint.URL)
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
if endpointURL.Scheme == "tcp" {
38+
if endpoint.TLS {
39+
proxy, err = service.proxyFactory.newHTTPSProxy(endpointURL, endpoint)
40+
if err != nil {
41+
return nil, err
42+
}
43+
} else {
44+
proxy = service.proxyFactory.newHTTPProxy(endpointURL)
45+
}
46+
} else {
47+
// Assume unix:// scheme
48+
proxy = service.proxyFactory.newSocketProxy(endpointURL.Path)
49+
}
50+
51+
service.proxies.Set(string(endpoint.ID), proxy)
52+
return proxy, nil
53+
}
54+
55+
// GetProxy returns the proxy associated to a key
56+
func (service *ProxyService) GetProxy(key string) http.Handler {
57+
proxy, ok := service.proxies.Get(key)
58+
if !ok {
59+
return nil
60+
}
61+
return proxy.(http.Handler)
62+
}
63+
64+
// DeleteProxy deletes the proxy associated to a key
65+
func (service *ProxyService) DeleteProxy(key string) {
66+
service.proxies.Remove(key)
67+
}

Diff for: api/http/server.go

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func (server *Server) Start() error {
2929
jwtService: server.JWTService,
3030
authDisabled: server.AuthDisabled,
3131
}
32+
proxyService := NewProxyService(server.ResourceControlService)
3233

3334
var authHandler = NewAuthHandler(middleWareService)
3435
authHandler.UserService = server.UserService
@@ -45,12 +46,14 @@ func (server *Server) Start() error {
4546
templatesHandler.containerTemplatesURL = server.TemplatesURL
4647
var dockerHandler = NewDockerHandler(middleWareService, server.ResourceControlService)
4748
dockerHandler.EndpointService = server.EndpointService
49+
dockerHandler.ProxyService = proxyService
4850
var websocketHandler = NewWebSocketHandler()
4951
websocketHandler.EndpointService = server.EndpointService
5052
var endpointHandler = NewEndpointHandler(middleWareService)
5153
endpointHandler.authorizeEndpointManagement = server.EndpointManagement
5254
endpointHandler.EndpointService = server.EndpointService
5355
endpointHandler.FileService = server.FileService
56+
endpointHandler.ProxyService = proxyService
5457
var uploadHandler = NewUploadHandler(middleWareService)
5558
uploadHandler.FileService = server.FileService
5659
var fileHandler = newFileHandler(server.AssetsPath)

Diff for: api/portainer.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package portainer
22

3-
import (
4-
"io"
5-
)
3+
import "io"
64

75
type (
86
// Pair defines a key/value string pair

Diff for: app/components/endpoint/endpointController.js

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ function ($scope, $state, $stateParams, $filter, EndpointService, Messages) {
1010
error: '',
1111
uploadInProgress: false
1212
};
13+
1314
$scope.formValues = {
1415
TLSCACert: null,
1516
TLSCert: null,

Diff for: app/services/endpointService.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ angular.module('portainer.services')
2424
if (endpointParams.type && endpointParams.URL) {
2525
query.URL = endpointParams.type === 'local' ? ("unix://" + endpointParams.URL) : ("tcp://" + endpointParams.URL);
2626
}
27+
2728
var deferred = $q.defer();
28-
Endpoints.update({id: id}, query).$promise
29+
FileUploadService.uploadTLSFilesForEndpoint(id, endpointParams.TLSCACert, endpointParams.TLSCert, endpointParams.TLSKey)
2930
.then(function success() {
30-
return FileUploadService.uploadTLSFilesForEndpoint(id, endpointParams.TLSCAFile, endpointParams.TLSCertFile, endpointParams.TLSKeyFile);
31+
deferred.notify({upload: false});
32+
return Endpoints.update({id: id}, query).$promise;
3133
})
3234
.then(function success(data) {
33-
deferred.notify({upload: false});
3435
deferred.resolve(data);
3536
})
3637
.catch(function error(err) {

0 commit comments

Comments
 (0)