fix(endpoints): add the ability to update TLS for an existing endpoint (#784)

pull/794/head
Anthony Lapenna 2017-04-09 19:38:41 +01:00 committed by GitHub
parent 44e48423ed
commit abc929824c
7 changed files with 90 additions and 45 deletions

View File

@ -7,11 +7,9 @@ import (
"log"
"net/http"
"net/url"
"os"
"github.com/gorilla/mux"
"github.com/orcaman/concurrent-map"
)
// DockerHandler represents an HTTP API handler for proxying requests to the Docker API.
@ -19,8 +17,7 @@ type DockerHandler struct {
*mux.Router
Logger *log.Logger
EndpointService portainer.EndpointService
ProxyFactory ProxyFactory
proxies cmap.ConcurrentMap
ProxyService *ProxyService
}
// NewDockerHandler returns a new instance of DockerHandler.
@ -28,10 +25,6 @@ func NewDockerHandler(mw *middleWareService, resourceControlService portainer.Re
h := &DockerHandler{
Router: mux.NewRouter(),
Logger: log.New(os.Stderr, "", log.LstdFlags),
ProxyFactory: ProxyFactory{
ResourceControlService: resourceControlService,
},
proxies: cmap.New(),
}
h.PathPrefix("/{id}/").Handler(
mw.authenticated(http.HandlerFunc(h.proxyRequestsToDockerAPI)))
@ -74,41 +67,14 @@ func (handler *DockerHandler) proxyRequestsToDockerAPI(w http.ResponseWriter, r
}
var proxy http.Handler
item, ok := handler.proxies.Get(string(endpointID))
if !ok {
proxy, err = handler.createAndRegisterEndpointProxy(endpoint)
proxy = handler.ProxyService.GetProxy(string(endpointID))
if proxy == nil {
proxy, err = handler.ProxyService.CreateAndRegisterProxy(endpoint)
if err != nil {
Error(w, err, http.StatusBadRequest, handler.Logger)
return
}
} else {
proxy = item.(http.Handler)
}
http.StripPrefix("/"+id, proxy).ServeHTTP(w, r)
}
func (handler *DockerHandler) createAndRegisterEndpointProxy(endpoint *portainer.Endpoint) (http.Handler, error) {
var proxy http.Handler
endpointURL, err := url.Parse(endpoint.URL)
if err != nil {
return nil, err
}
if endpointURL.Scheme == "tcp" {
if endpoint.TLS {
proxy, err = handler.ProxyFactory.newHTTPSProxy(endpointURL, endpoint)
if err != nil {
return nil, err
}
} else {
proxy = handler.ProxyFactory.newHTTPProxy(endpointURL)
}
} else {
// Assume unix:// scheme
proxy = handler.ProxyFactory.newSocketProxy(endpointURL.Path)
}
handler.proxies.Set(string(endpoint.ID), proxy)
return proxy, nil
}

View File

@ -20,6 +20,7 @@ type EndpointHandler struct {
authorizeEndpointManagement bool
EndpointService portainer.EndpointService
FileService portainer.FileService
ProxyService *ProxyService
}
const (
@ -281,6 +282,12 @@ func (handler *EndpointHandler) handlePutEndpoint(w http.ResponseWriter, r *http
}
}
_, err = handler.ProxyService.CreateAndRegisterProxy(endpoint)
if err != nil {
Error(w, err, http.StatusInternalServerError, handler.Logger)
return
}
err = handler.EndpointService.UpdateEndpoint(endpoint.ID, endpoint)
if err != nil {
Error(w, err, http.StatusInternalServerError, handler.Logger)
@ -320,6 +327,8 @@ func (handler *EndpointHandler) handleDeleteEndpoint(w http.ResponseWriter, r *h
return
}
handler.ProxyService.DeleteProxy(string(endpointID))
err = handler.EndpointService.DeleteEndpoint(portainer.EndpointID(endpointID))
if err != nil {
Error(w, err, http.StatusInternalServerError, handler.Logger)

67
api/http/proxy.go Normal file
View File

@ -0,0 +1,67 @@
package http
import (
"net/http"
"net/url"
"github.com/orcaman/concurrent-map"
"github.com/portainer/portainer"
)
// ProxyService represents a service used to manage Docker proxies.
type ProxyService struct {
proxyFactory *ProxyFactory
proxies cmap.ConcurrentMap
}
// NewProxyService initializes a new ProxyService
func NewProxyService(resourceControlService portainer.ResourceControlService) *ProxyService {
return &ProxyService{
proxies: cmap.New(),
proxyFactory: &ProxyFactory{
ResourceControlService: resourceControlService,
},
}
}
// CreateAndRegisterProxy creates a new HTTP reverse proxy and adds it to the registered proxies.
// It can also be used to create a new HTTP reverse proxy and replace an already registered proxy.
func (service *ProxyService) CreateAndRegisterProxy(endpoint *portainer.Endpoint) (http.Handler, error) {
var proxy http.Handler
endpointURL, err := url.Parse(endpoint.URL)
if err != nil {
return nil, err
}
if endpointURL.Scheme == "tcp" {
if endpoint.TLS {
proxy, err = service.proxyFactory.newHTTPSProxy(endpointURL, endpoint)
if err != nil {
return nil, err
}
} else {
proxy = service.proxyFactory.newHTTPProxy(endpointURL)
}
} else {
// Assume unix:// scheme
proxy = service.proxyFactory.newSocketProxy(endpointURL.Path)
}
service.proxies.Set(string(endpoint.ID), proxy)
return proxy, nil
}
// GetProxy returns the proxy associated to a key
func (service *ProxyService) GetProxy(key string) http.Handler {
proxy, ok := service.proxies.Get(key)
if !ok {
return nil
}
return proxy.(http.Handler)
}
// DeleteProxy deletes the proxy associated to a key
func (service *ProxyService) DeleteProxy(key string) {
service.proxies.Remove(key)
}

View File

@ -29,6 +29,7 @@ func (server *Server) Start() error {
jwtService: server.JWTService,
authDisabled: server.AuthDisabled,
}
proxyService := NewProxyService(server.ResourceControlService)
var authHandler = NewAuthHandler(middleWareService)
authHandler.UserService = server.UserService
@ -45,12 +46,14 @@ func (server *Server) Start() error {
templatesHandler.containerTemplatesURL = server.TemplatesURL
var dockerHandler = NewDockerHandler(middleWareService, server.ResourceControlService)
dockerHandler.EndpointService = server.EndpointService
dockerHandler.ProxyService = proxyService
var websocketHandler = NewWebSocketHandler()
websocketHandler.EndpointService = server.EndpointService
var endpointHandler = NewEndpointHandler(middleWareService)
endpointHandler.authorizeEndpointManagement = server.EndpointManagement
endpointHandler.EndpointService = server.EndpointService
endpointHandler.FileService = server.FileService
endpointHandler.ProxyService = proxyService
var uploadHandler = NewUploadHandler(middleWareService)
uploadHandler.FileService = server.FileService
var fileHandler = newFileHandler(server.AssetsPath)

View File

@ -1,8 +1,6 @@
package portainer
import (
"io"
)
import "io"
type (
// Pair defines a key/value string pair

View File

@ -10,6 +10,7 @@ function ($scope, $state, $stateParams, $filter, EndpointService, Messages) {
error: '',
uploadInProgress: false
};
$scope.formValues = {
TLSCACert: null,
TLSCert: null,

View File

@ -24,13 +24,14 @@ angular.module('portainer.services')
if (endpointParams.type && endpointParams.URL) {
query.URL = endpointParams.type === 'local' ? ("unix://" + endpointParams.URL) : ("tcp://" + endpointParams.URL);
}
var deferred = $q.defer();
Endpoints.update({id: id}, query).$promise
FileUploadService.uploadTLSFilesForEndpoint(id, endpointParams.TLSCACert, endpointParams.TLSCert, endpointParams.TLSKey)
.then(function success() {
return FileUploadService.uploadTLSFilesForEndpoint(id, endpointParams.TLSCAFile, endpointParams.TLSCertFile, endpointParams.TLSKeyFile);
deferred.notify({upload: false});
return Endpoints.update({id: id}, query).$promise;
})
.then(function success(data) {
deferred.notify({upload: false});
deferred.resolve(data);
})
.catch(function error(err) {