diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go index 4c9ddfea6..01a73d41f 100644 --- a/api/cmd/portainer/main.go +++ b/api/cmd/portainer/main.go @@ -106,8 +106,12 @@ func initClientFactory(signatureService portainer.DigitalSignatureService) *dock return docker.NewClientFactory(signatureService) } -func initJobScheduler(endpointService portainer.EndpointService, clientFactory *docker.ClientFactory, flags *portainer.CLIFlags) (portainer.JobScheduler, error) { - jobScheduler := cron.NewJobScheduler(endpointService, clientFactory) +func initSnapshotter(clientFactory *docker.ClientFactory) portainer.Snapshotter { + return docker.NewSnapshotter(clientFactory) +} + +func initJobScheduler(endpointService portainer.EndpointService, snapshotter portainer.Snapshotter, flags *portainer.CLIFlags) (portainer.JobScheduler, error) { + jobScheduler := cron.NewJobScheduler(endpointService, snapshotter) if *flags.ExternalEndpoints != "" { log.Println("Using external endpoint definition. Endpoint management via the API will be disabled.") @@ -394,7 +398,9 @@ func main() { clientFactory := initClientFactory(digitalSignatureService) - jobScheduler, err := initJobScheduler(store.EndpointService, clientFactory, flags) + snapshotter := initSnapshotter(clientFactory) + + jobScheduler, err := initJobScheduler(store.EndpointService, snapshotter, flags) if err != nil { log.Fatal(err) } @@ -498,6 +504,7 @@ func main() { GitService: gitService, SignatureService: digitalSignatureService, JobScheduler: jobScheduler, + Snapshotter: snapshotter, SSL: *flags.SSL, SSLCert: *flags.SSLCert, SSLKey: *flags.SSLKey, diff --git a/api/cron/scheduler.go b/api/cron/scheduler.go index 3847e6bbd..9f65b6d5a 100644 --- a/api/cron/scheduler.go +++ b/api/cron/scheduler.go @@ -4,7 +4,6 @@ import ( "log" "github.com/portainer/portainer" - "github.com/portainer/portainer/docker" "github.com/robfig/cron" ) @@ -19,11 +18,11 @@ type JobScheduler struct { } // NewJobScheduler initializes a new service. -func NewJobScheduler(endpointService portainer.EndpointService, clientFactory *docker.ClientFactory) *JobScheduler { +func NewJobScheduler(endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) *JobScheduler { return &JobScheduler{ cron: cron.New(), endpointService: endpointService, - snapshotter: docker.NewSnapshotter(clientFactory), + snapshotter: snapshotter, } } diff --git a/api/http/handler/endpoints/endpoint_create.go b/api/http/handler/endpoints/endpoint_create.go index 6c1e1f894..9ac29b649 100644 --- a/api/http/handler/endpoints/endpoint_create.go +++ b/api/http/handler/endpoints/endpoint_create.go @@ -1,6 +1,7 @@ package endpoints import ( + "log" "net/http" "runtime" "strconv" @@ -224,9 +225,9 @@ func (handler *Handler) createUnsecuredEndpoint(payload *endpointCreatePayload) Snapshots: []portainer.Snapshot{}, } - err := handler.EndpointService.CreateEndpoint(endpoint) + err := handler.snapshotAndPersistEndpoint(endpoint) if err != nil { - return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint inside the database", err} + return nil, err } return endpoint, nil @@ -266,9 +267,9 @@ func (handler *Handler) createTLSSecuredEndpoint(payload *endpointCreatePayload) Snapshots: []portainer.Snapshot{}, } - err = handler.EndpointService.CreateEndpoint(endpoint) - if err != nil { - return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint inside the database", err} + endpointCreationError := handler.snapshotAndPersistEndpoint(endpoint) + if endpointCreationError != nil { + return nil, endpointCreationError } filesystemError := handler.storeTLSFiles(endpoint, payload) @@ -285,6 +286,26 @@ func (handler *Handler) createTLSSecuredEndpoint(payload *endpointCreatePayload) return endpoint, nil } +func (handler *Handler) snapshotAndPersistEndpoint(endpoint *portainer.Endpoint) *httperror.HandlerError { + snapshot, err := handler.Snapshotter.CreateSnapshot(endpoint) + endpoint.Status = portainer.EndpointStatusUp + if err != nil { + log.Printf("http error: endpoint snapshot error (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err) + endpoint.Status = portainer.EndpointStatusDown + } + + if snapshot != nil { + endpoint.Snapshots = []portainer.Snapshot{*snapshot} + } + + err = handler.EndpointService.CreateEndpoint(endpoint) + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint inside the database", err} + } + + return nil +} + func (handler *Handler) storeTLSFiles(endpoint *portainer.Endpoint, payload *endpointCreatePayload) *httperror.HandlerError { folder := strconv.Itoa(int(endpoint.ID)) diff --git a/api/http/handler/endpoints/handler.go b/api/http/handler/endpoints/handler.go index 6a593479c..69440037e 100644 --- a/api/http/handler/endpoints/handler.go +++ b/api/http/handler/endpoints/handler.go @@ -29,6 +29,7 @@ type Handler struct { EndpointGroupService portainer.EndpointGroupService FileService portainer.FileService ProxyManager *proxy.Manager + Snapshotter portainer.Snapshotter } // NewHandler creates a handler to manage endpoint operations. diff --git a/api/http/server.go b/api/http/server.go index 564e24e3b..4babc109b 100644 --- a/api/http/server.go +++ b/api/http/server.go @@ -41,6 +41,7 @@ type Server struct { CryptoService portainer.CryptoService SignatureService portainer.DigitalSignatureService JobScheduler portainer.JobScheduler + Snapshotter portainer.Snapshotter DockerHubService portainer.DockerHubService EndpointService portainer.EndpointService EndpointGroupService portainer.EndpointGroupService @@ -102,6 +103,7 @@ func (server *Server) Start() error { endpointHandler.EndpointGroupService = server.EndpointGroupService endpointHandler.FileService = server.FileService endpointHandler.ProxyManager = proxyManager + endpointHandler.Snapshotter = server.Snapshotter var endpointGroupHandler = endpointgroups.NewHandler(requestBouncer) endpointGroupHandler.EndpointGroupService = server.EndpointGroupService diff --git a/app/portainer/components/datatables/endpoints-snapshot-datatable/endpointsSnapshotDatatable.html b/app/portainer/components/datatables/endpoints-snapshot-datatable/endpointsSnapshotDatatable.html deleted file mode 100644 index a94ca0b37..000000000 --- a/app/portainer/components/datatables/endpoints-snapshot-datatable/endpointsSnapshotDatatable.html +++ /dev/null @@ -1,113 +0,0 @@ -
- - -
-
- {{ $ctrl.titleText }} -
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Name - - - - - - Group - - - - - - Status - - - - - - Type - - - - - - Last snapshot - - - -
- {{ item.Name }} - {{ item.GroupName }} - {{ item.Status === 1 ? 'up' : 'down' }} - - - - {{ item.Type | endpointtypename }} - - - - {{ item.Snapshots[0].Time | getisodatefromtimestamp }} - - - -
- -
Loading...
No endpoint available.
-
- -
-
-
diff --git a/app/portainer/components/datatables/endpoints-snapshot-datatable/endpointsSnapshotDatatable.js b/app/portainer/components/datatables/endpoints-snapshot-datatable/endpointsSnapshotDatatable.js deleted file mode 100644 index a7d9c7711..000000000 --- a/app/portainer/components/datatables/endpoints-snapshot-datatable/endpointsSnapshotDatatable.js +++ /dev/null @@ -1,13 +0,0 @@ -angular.module('portainer.app').component('endpointsSnapshotDatatable', { - templateUrl: 'app/portainer/components/datatables/endpoints-snapshot-datatable/endpointsSnapshotDatatable.html', - controller: 'GenericDatatableController', - bindings: { - titleText: '@', - titleIcon: '@', - dataset: '<', - tableKey: '@', - orderBy: '@', - reverseOrder: '<', - dashboardAction: '<' - } -}); diff --git a/app/portainer/components/datatables/endpoints-snapshot-datatable/snapshot-details/snapshotDetails.html b/app/portainer/components/datatables/endpoints-snapshot-datatable/snapshot-details/snapshotDetails.html deleted file mode 100644 index 00efe0ae9..000000000 --- a/app/portainer/components/datatables/endpoints-snapshot-datatable/snapshot-details/snapshotDetails.html +++ /dev/null @@ -1,29 +0,0 @@ - - - {{ $ctrl.snapshot.StackCount }} stacks - - - {{ $ctrl.snapshot.ServiceCount }} services - - - {{ $ctrl.snapshot.RunningContainerCount + $ctrl.snapshot.StoppedContainerCount }} containers - - - - {{ $ctrl.snapshot.RunningContainerCount }} - {{ $ctrl.snapshot.StoppedContainerCount }} - - - - {{ $ctrl.snapshot.VolumeCount }} volumes - - - {{ $ctrl.snapshot.ImageCount }} images - - - {{ $ctrl.snapshot.TotalMemory | humansize }} - {{ $ctrl.snapshot.TotalCPU }} - - - {{ $ctrl.snapshot.Swarm ? 'Swarm' : 'Standalone' }} {{ $ctrl.snapshot.DockerVersion }} - - diff --git a/app/portainer/components/datatables/endpoints-snapshot-datatable/snapshot-details/snapshotDetails.js b/app/portainer/components/datatables/endpoints-snapshot-datatable/snapshot-details/snapshotDetails.js deleted file mode 100644 index 1190be07e..000000000 --- a/app/portainer/components/datatables/endpoints-snapshot-datatable/snapshot-details/snapshotDetails.js +++ /dev/null @@ -1,6 +0,0 @@ -angular.module('portainer.app').component('snapshotDetails', { - templateUrl: 'app/portainer/components/datatables/endpoints-snapshot-datatable/snapshot-details/snapshotDetails.html', - bindings: { - snapshot: '<' - } -}); diff --git a/app/portainer/components/endpoint-list/endpoint-item/endpointItem.html b/app/portainer/components/endpoint-list/endpoint-item/endpointItem.html new file mode 100644 index 000000000..fd50950ce --- /dev/null +++ b/app/portainer/components/endpoint-list/endpoint-item/endpointItem.html @@ -0,0 +1,75 @@ +
+
+ + + + + + +
+ + + {{ $ctrl.model.Name }} + + + + {{ $ctrl.model.Status === 1 ? 'up' : 'down' }} + + + {{ $ctrl.model.Snapshots[0].Time | getisodatefromtimestamp }} + + + + + + {{ $ctrl.model.GroupName }} + +
+ +
+ + + + {{ $ctrl.model.Snapshots[0].StackCount }} stacks + + + {{ $ctrl.model.Snapshots[0].ServiceCount }} services + + + {{ $ctrl.model.Snapshots[0].RunningContainerCount + $ctrl.model.Snapshots[0].StoppedContainerCount }} containers + + - + {{ $ctrl.model.Snapshots[0].RunningContainerCount }} + {{ $ctrl.model.Snapshots[0].StoppedContainerCount }} + + + + {{ $ctrl.model.Snapshots[0].VolumeCount }} volumes + + + {{ $ctrl.model.Snapshots[0].ImageCount }} images + + + + + {{ $ctrl.model.Snapshots[0].Swarm ? 'Swarm' : 'Standalone' }} {{ $ctrl.model.Snapshots[0].DockerVersion }} + Agent + +
+ +
+ + + No tags + + + + + {{ tag }}{{ $last? '' : ', ' }} + + + +
+ +
+
+
diff --git a/app/portainer/components/endpoint-list/endpoint-item/endpointItem.js b/app/portainer/components/endpoint-list/endpoint-item/endpointItem.js new file mode 100644 index 000000000..d04fb25cf --- /dev/null +++ b/app/portainer/components/endpoint-list/endpoint-item/endpointItem.js @@ -0,0 +1,7 @@ +angular.module('portainer.app').component('endpointItem', { + templateUrl: 'app/portainer/components/endpoint-list/endpoint-item/endpointItem.html', + bindings: { + model: '<', + onSelect: '<' + } +}); diff --git a/app/portainer/components/endpoint-list/endpoint-list.js b/app/portainer/components/endpoint-list/endpoint-list.js new file mode 100644 index 000000000..9d551591f --- /dev/null +++ b/app/portainer/components/endpoint-list/endpoint-list.js @@ -0,0 +1,16 @@ +angular.module('portainer.app').component('endpointList', { + templateUrl: 'app/portainer/components/endpoint-list/endpointList.html', + controller: function() { + var ctrl = this; + + this.state = { + textFilter: '' + }; + }, + bindings: { + titleText: '@', + titleIcon: '@', + endpoints: '<', + dashboardAction: '<' + } +}); diff --git a/app/portainer/components/endpoint-list/endpointList.html b/app/portainer/components/endpoint-list/endpointList.html new file mode 100644 index 000000000..f6b904408 --- /dev/null +++ b/app/portainer/components/endpoint-list/endpointList.html @@ -0,0 +1,32 @@ +
+ + + +
+
+ {{ $ctrl.titleText }} +
+
+ + + +
+ +
+ Loading... +
+
+ No endpoint available. +
+
+ +
+
+
diff --git a/app/portainer/components/template-list/template-item/templateItem.html b/app/portainer/components/template-list/template-item/templateItem.html index 32983f372..e287c36c9 100644 --- a/app/portainer/components/template-list/template-item/templateItem.html +++ b/app/portainer/components/template-list/template-item/templateItem.html @@ -1,23 +1,23 @@ -
-
+
+
- + -