diff --git a/api/docker/snapshot.go b/api/docker/snapshot.go index d8c481fb7..342465b1f 100644 --- a/api/docker/snapshot.go +++ b/api/docker/snapshot.go @@ -52,6 +52,16 @@ func snapshot(cli *client.Client) (*portainer.Snapshot, error) { return nil, err } + err = snapshotNetworks(snapshot, cli) + if err != nil { + return nil, err + } + + err = snapshotVersion(snapshot, cli) + if err != nil { + return nil, err + } + snapshot.Time = time.Now().Unix() return snapshot, nil } @@ -66,6 +76,7 @@ func snapshotInfo(snapshot *portainer.Snapshot, cli *client.Client) error { snapshot.DockerVersion = info.ServerVersion snapshot.TotalCPU = info.NCPU snapshot.TotalMemory = info.MemTotal + snapshot.SnapshotRaw.Info = info return nil } @@ -132,6 +143,7 @@ func snapshotContainers(snapshot *portainer.Snapshot, cli *client.Client) error snapshot.RunningContainerCount = runningContainers snapshot.StoppedContainerCount = stoppedContainers snapshot.StackCount += len(stacks) + snapshot.SnapshotRaw.Containers = containers return nil } @@ -142,6 +154,7 @@ func snapshotImages(snapshot *portainer.Snapshot, cli *client.Client) error { } snapshot.ImageCount = len(images) + snapshot.SnapshotRaw.Images = images return nil } @@ -152,5 +165,24 @@ func snapshotVolumes(snapshot *portainer.Snapshot, cli *client.Client) error { } snapshot.VolumeCount = len(volumes.Volumes) + snapshot.SnapshotRaw.Volumes = volumes + return nil +} + +func snapshotNetworks(snapshot *portainer.Snapshot, cli *client.Client) error { + networks, err := cli.NetworkList(context.Background(), types.NetworkListOptions{}) + if err != nil { + return err + } + snapshot.SnapshotRaw.Networks = networks + return nil +} + +func snapshotVersion(snapshot *portainer.Snapshot, cli *client.Client) error { + version, err := cli.ServerVersion(context.Background()) + if err != nil { + return err + } + snapshot.SnapshotRaw.Version = version return nil } diff --git a/api/http/handler/endpoints/endpoint_update.go b/api/http/handler/endpoints/endpoint_update.go index 5244cdc98..405f6ce38 100644 --- a/api/http/handler/endpoints/endpoint_update.go +++ b/api/http/handler/endpoints/endpoint_update.go @@ -12,16 +12,17 @@ import ( ) type endpointUpdatePayload struct { - Name string - URL string - PublicURL string - GroupID int - TLS bool - TLSSkipVerify bool - TLSSkipClientVerify bool - AzureApplicationID string - AzureTenantID string - AzureAuthenticationKey string + Name *string + URL *string + PublicURL *string + GroupID *int + TLS *bool + TLSSkipVerify *bool + TLSSkipClientVerify *bool + Status *int + AzureApplicationID *string + AzureTenantID *string + AzureAuthenticationKey *string Tags []string } @@ -53,36 +54,49 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) * return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} } - if payload.Name != "" { - endpoint.Name = payload.Name + if payload.Name != nil { + endpoint.Name = *payload.Name } - if payload.URL != "" { - endpoint.URL = payload.URL + if payload.URL != nil { + endpoint.URL = *payload.URL } - if payload.PublicURL != "" { - endpoint.PublicURL = payload.PublicURL + if payload.PublicURL != nil { + endpoint.PublicURL = *payload.PublicURL } - if payload.GroupID != 0 { - endpoint.GroupID = portainer.EndpointGroupID(payload.GroupID) + if payload.GroupID != nil { + endpoint.GroupID = portainer.EndpointGroupID(*payload.GroupID) } if payload.Tags != nil { endpoint.Tags = payload.Tags } + if payload.Status != nil { + switch *payload.Status { + case 1: + endpoint.Status = portainer.EndpointStatusUp + break + case 2: + endpoint.Status = portainer.EndpointStatusDown + break + default: + break + } + } + if endpoint.Type == portainer.AzureEnvironment { credentials := endpoint.AzureCredentials - if payload.AzureApplicationID != "" { - credentials.ApplicationID = payload.AzureApplicationID + if payload.AzureApplicationID != nil { + credentials.ApplicationID = *payload.AzureApplicationID } - if payload.AzureTenantID != "" { - credentials.TenantID = payload.AzureTenantID + if payload.AzureTenantID != nil { + credentials.TenantID = *payload.AzureTenantID } - if payload.AzureAuthenticationKey != "" { - credentials.AuthenticationKey = payload.AzureAuthenticationKey + if payload.AzureAuthenticationKey != nil { + credentials.AuthenticationKey = *payload.AzureAuthenticationKey } httpClient := client.NewHTTPClient() @@ -93,44 +107,55 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) * endpoint.AzureCredentials = credentials } - folder := strconv.Itoa(endpointID) - if payload.TLS { - endpoint.TLSConfig.TLS = true - endpoint.TLSConfig.TLSSkipVerify = payload.TLSSkipVerify - if !payload.TLSSkipVerify { - caCertPath, _ := handler.FileService.GetPathForTLSFile(folder, portainer.TLSFileCA) - endpoint.TLSConfig.TLSCACertPath = caCertPath - } else { - endpoint.TLSConfig.TLSCACertPath = "" - handler.FileService.DeleteTLSFile(folder, portainer.TLSFileCA) - } + if payload.TLS != nil { + folder := strconv.Itoa(endpointID) + + if *payload.TLS { + endpoint.TLSConfig.TLS = true + if payload.TLSSkipVerify != nil { + endpoint.TLSConfig.TLSSkipVerify = *payload.TLSSkipVerify + + if !*payload.TLSSkipVerify { + caCertPath, _ := handler.FileService.GetPathForTLSFile(folder, portainer.TLSFileCA) + endpoint.TLSConfig.TLSCACertPath = caCertPath + } else { + endpoint.TLSConfig.TLSCACertPath = "" + handler.FileService.DeleteTLSFile(folder, portainer.TLSFileCA) + } + } + + if payload.TLSSkipClientVerify != nil { + if !*payload.TLSSkipClientVerify { + certPath, _ := handler.FileService.GetPathForTLSFile(folder, portainer.TLSFileCert) + endpoint.TLSConfig.TLSCertPath = certPath + keyPath, _ := handler.FileService.GetPathForTLSFile(folder, portainer.TLSFileKey) + endpoint.TLSConfig.TLSKeyPath = keyPath + } else { + endpoint.TLSConfig.TLSCertPath = "" + handler.FileService.DeleteTLSFile(folder, portainer.TLSFileCert) + endpoint.TLSConfig.TLSKeyPath = "" + handler.FileService.DeleteTLSFile(folder, portainer.TLSFileKey) + } + } - if !payload.TLSSkipClientVerify { - certPath, _ := handler.FileService.GetPathForTLSFile(folder, portainer.TLSFileCert) - endpoint.TLSConfig.TLSCertPath = certPath - keyPath, _ := handler.FileService.GetPathForTLSFile(folder, portainer.TLSFileKey) - endpoint.TLSConfig.TLSKeyPath = keyPath } else { + endpoint.TLSConfig.TLS = false + endpoint.TLSConfig.TLSSkipVerify = false + endpoint.TLSConfig.TLSCACertPath = "" endpoint.TLSConfig.TLSCertPath = "" - handler.FileService.DeleteTLSFile(folder, portainer.TLSFileCert) endpoint.TLSConfig.TLSKeyPath = "" - handler.FileService.DeleteTLSFile(folder, portainer.TLSFileKey) - } - } else { - endpoint.TLSConfig.TLS = false - endpoint.TLSConfig.TLSSkipVerify = false - endpoint.TLSConfig.TLSCACertPath = "" - endpoint.TLSConfig.TLSCertPath = "" - endpoint.TLSConfig.TLSKeyPath = "" - err = handler.FileService.DeleteTLSFiles(folder) - if err != nil { - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to remove TLS files from disk", err} + err = handler.FileService.DeleteTLSFiles(folder) + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to remove TLS files from disk", err} + } } } - _, err = handler.ProxyManager.CreateAndRegisterProxy(endpoint) - if err != nil { - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to register HTTP proxy for the endpoint", err} + if payload.URL != nil || payload.TLS != nil || endpoint.Type == portainer.AzureEnvironment { + _, err = handler.ProxyManager.CreateAndRegisterProxy(endpoint) + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to register HTTP proxy for the endpoint", err} + } } err = handler.EndpointService.UpdateEndpoint(endpoint.ID, endpoint) diff --git a/api/portainer.go b/api/portainer.go index c1cdbab4f..2c866f3da 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -245,17 +245,28 @@ type ( // Snapshot represents a snapshot of a specific endpoint at a specific time Snapshot struct { - Time int64 `json:"Time"` - DockerVersion string `json:"DockerVersion"` - Swarm bool `json:"Swarm"` - TotalCPU int `json:"TotalCPU"` - TotalMemory int64 `json:"TotalMemory"` - RunningContainerCount int `json:"RunningContainerCount"` - StoppedContainerCount int `json:"StoppedContainerCount"` - VolumeCount int `json:"VolumeCount"` - ImageCount int `json:"ImageCount"` - ServiceCount int `json:"ServiceCount"` - StackCount int `json:"StackCount"` + Time int64 `json:"Time"` + DockerVersion string `json:"DockerVersion"` + Swarm bool `json:"Swarm"` + TotalCPU int `json:"TotalCPU"` + TotalMemory int64 `json:"TotalMemory"` + RunningContainerCount int `json:"RunningContainerCount"` + StoppedContainerCount int `json:"StoppedContainerCount"` + VolumeCount int `json:"VolumeCount"` + ImageCount int `json:"ImageCount"` + ServiceCount int `json:"ServiceCount"` + StackCount int `json:"StackCount"` + SnapshotRaw SnapshotRaw `json:"SnapshotRaw"` + } + + // SnapshotRaw represents all the information related to a snapshot as returned by the Docker API + SnapshotRaw struct { + Containers interface{} `json:"Containers"` + Volumes interface{} `json:"Volumes"` + Networks interface{} `json:"Networks"` + Images interface{} `json:"Images"` + Info interface{} `json:"Info"` + Version interface{} `json:"Version"` } // EndpointGroupID represents an endpoint group identifier diff --git a/api/swagger.yaml b/api/swagger.yaml index 51d8a9f68..ee24b528d 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -274,6 +274,10 @@ paths: in: "formData" type: "string" description: "Skip server verification when using TLS (example: false)" + - name: "TLSSkipClientVerify" + in: "formData" + type: "string" + description: "Skip client verification when using TLS (example: false)" - name: "TLSCACertFile" in: "formData" type: "file" @@ -541,7 +545,7 @@ paths: in: "query" description: "Optional. Hostname of a node when targeting a Portainer agent cluster." required: true - type: "string" + type: "string" - in: "body" name: "body" description: "Job details. Required when method equals string." diff --git a/app/config.js b/app/config.js index 196c8c491..5d6fc6033 100644 --- a/app/config.js +++ b/app/config.js @@ -22,6 +22,7 @@ angular.module('portainer') }] }); $httpProvider.interceptors.push('jwtInterceptor'); + $httpProvider.interceptors.push('EndpointStatusInterceptor'); $httpProvider.defaults.headers.post['Content-Type'] = 'application/json'; $httpProvider.defaults.headers.put['Content-Type'] = 'application/json'; $httpProvider.defaults.headers.patch['Content-Type'] = 'application/json'; @@ -53,6 +54,7 @@ angular.module('portainer') cfpLoadingBarProvider.includeSpinner = false; cfpLoadingBarProvider.parentSelector = '#loadingbar-placeholder'; + cfpLoadingBarProvider.latencyThreshold = 600; $urlRouterProvider.otherwise('/auth'); }]); diff --git a/app/docker/components/container-quick-actions/containerQuickActions.html b/app/docker/components/container-quick-actions/containerQuickActions.html new file mode 100644 index 000000000..0befd7e8d --- /dev/null +++ b/app/docker/components/container-quick-actions/containerQuickActions.html @@ -0,0 +1,44 @@ +
+ + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/app/docker/components/container-quick-actions/containerQuickActions.js b/app/docker/components/container-quick-actions/containerQuickActions.js new file mode 100644 index 000000000..3fb616d7c --- /dev/null +++ b/app/docker/components/container-quick-actions/containerQuickActions.js @@ -0,0 +1,10 @@ +angular.module('portainer.docker').component('containerQuickActions', { + templateUrl: 'app/docker/components/container-quick-actions/containerQuickActions.html', + bindings: { + containerId: '<', + nodeName: '<', + status: '<', + state: '<', + taskId: '<' + } +}); diff --git a/app/docker/components/datatables/containers-datatable/containersDatatable.html b/app/docker/components/datatables/containers-datatable/containersDatatable.html index 19cd2a0c0..2ade1ff08 100644 --- a/app/docker/components/datatables/containers-datatable/containersDatatable.html +++ b/app/docker/components/datatables/containers-datatable/containersDatatable.html @@ -99,7 +99,7 @@ - - + @@ -210,26 +210,27 @@ - + - {{ item | containername | truncate: $ctrl.settings.containerNameTruncateSize }} + {{ item | containername | truncate: $ctrl.settings.containerNameTruncateSize }} + {{ item | containername | truncate: $ctrl.settings.containerNameTruncateSize }} {{ item.Status }} {{ item.Status }} - -
- - - - -
+ + + + {{ item.StackName ? item.StackName : '-' }} - {{ item.Image | trimshasum }} + + {{ item.Image | trimshasum }} + {{ item.Image | trimshasum }} + {{item.Created | getisodatefromtimestamp}} diff --git a/app/docker/components/datatables/containers-datatable/containersDatatable.js b/app/docker/components/datatables/containers-datatable/containersDatatable.js index 4f6fb0b4f..92bd83f8e 100644 --- a/app/docker/components/datatables/containers-datatable/containersDatatable.js +++ b/app/docker/components/datatables/containers-datatable/containersDatatable.js @@ -12,6 +12,7 @@ angular.module('portainer.docker').component('containersDatatable', { reverseOrder: '<', showOwnershipColumn: '<', showHostColumn: '<', - showAddAction: '<' + showAddAction: '<', + offlineMode: '<' } }); diff --git a/app/docker/components/datatables/containers-datatable/containersDatatableController.js b/app/docker/components/datatables/containers-datatable/containersDatatableController.js index 6fe707e23..f41eb06bb 100644 --- a/app/docker/components/datatables/containers-datatable/containersDatatableController.js +++ b/app/docker/components/datatables/containers-datatable/containersDatatableController.js @@ -208,7 +208,6 @@ function (PaginationService, DatatableService, EndpointProvider) { this.$onInit = function() { setDefaults(this); this.prepareTableFromDataset(); - var storedOrder = DatatableService.getDataTableOrder(this.tableKey); if (storedOrder !== null) { this.state.reverseOrder = storedOrder.reverse; diff --git a/app/docker/components/datatables/images-datatable/imagesDatatable.html b/app/docker/components/datatables/images-datatable/imagesDatatable.html index 87cba5507..a509ebd75 100644 --- a/app/docker/components/datatables/images-datatable/imagesDatatable.html +++ b/app/docker/components/datatables/images-datatable/imagesDatatable.html @@ -6,7 +6,7 @@ {{ $ctrl.titleText }} -
+
-
+