mirror of https://github.com/portainer/portainer
feat(snapshots): separate snapshots from endpoint DB struct EE-4099 (#7614)
parent
4fe2a7c750
commit
6e0f83b99e
|
@ -40,6 +40,7 @@ type (
|
||||||
Role() RoleService
|
Role() RoleService
|
||||||
APIKeyRepository() APIKeyRepository
|
APIKeyRepository() APIKeyRepository
|
||||||
Settings() SettingsService
|
Settings() SettingsService
|
||||||
|
Snapshot() SnapshotService
|
||||||
SSLSettings() SSLSettingsService
|
SSLSettings() SSLSettingsService
|
||||||
Stack() StackService
|
Stack() StackService
|
||||||
Tag() TagService
|
Tag() TagService
|
||||||
|
@ -214,6 +215,15 @@ type (
|
||||||
BucketName() string
|
BucketName() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SnapshotService interface {
|
||||||
|
Snapshot(endpointID portainer.EndpointID) (*portainer.Snapshot, error)
|
||||||
|
Snapshots() ([]portainer.Snapshot, error)
|
||||||
|
UpdateSnapshot(snapshot *portainer.Snapshot) error
|
||||||
|
DeleteSnapshot(endpointID portainer.EndpointID) error
|
||||||
|
Create(snapshot *portainer.Snapshot) error
|
||||||
|
BucketName() string
|
||||||
|
}
|
||||||
|
|
||||||
// SSLSettingsService represents a service for managing application settings
|
// SSLSettingsService represents a service for managing application settings
|
||||||
SSLSettingsService interface {
|
SSLSettingsService interface {
|
||||||
Settings() (*portainer.SSLSettings, error)
|
Settings() (*portainer.SSLSettings, error)
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package snapshot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BucketName = "snapshots"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
connection portainer.Connection
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) BucketName() string {
|
||||||
|
return BucketName
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(connection portainer.Connection) (*Service, error) {
|
||||||
|
err := connection.SetServiceName(BucketName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Service{
|
||||||
|
connection: connection,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) Snapshot(endpointID portainer.EndpointID) (*portainer.Snapshot, error) {
|
||||||
|
var snapshot portainer.Snapshot
|
||||||
|
identifier := service.connection.ConvertToKey(int(endpointID))
|
||||||
|
|
||||||
|
err := service.connection.GetObject(BucketName, identifier, &snapshot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &snapshot, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) Snapshots() ([]portainer.Snapshot, error) {
|
||||||
|
var snapshots = make([]portainer.Snapshot, 0)
|
||||||
|
|
||||||
|
err := service.connection.GetAllWithJsoniter(
|
||||||
|
BucketName,
|
||||||
|
&portainer.Snapshot{},
|
||||||
|
func(obj interface{}) (interface{}, error) {
|
||||||
|
snapshot, ok := obj.(*portainer.Snapshot)
|
||||||
|
if !ok {
|
||||||
|
logrus.WithField("obj", obj).Errorf("Failed to convert to Snapshot object")
|
||||||
|
return nil, fmt.Errorf("failed to convert to Snapshot object: %s", obj)
|
||||||
|
}
|
||||||
|
snapshots = append(snapshots, *snapshot)
|
||||||
|
return &portainer.Snapshot{}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return snapshots, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) UpdateSnapshot(snapshot *portainer.Snapshot) error {
|
||||||
|
identifier := service.connection.ConvertToKey(int(snapshot.EndpointID))
|
||||||
|
return service.connection.UpdateObject(BucketName, identifier, snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) DeleteSnapshot(endpointID portainer.EndpointID) error {
|
||||||
|
identifier := service.connection.ConvertToKey(int(endpointID))
|
||||||
|
return service.connection.DeleteObject(BucketName, identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) Create(snapshot *portainer.Snapshot) error {
|
||||||
|
return service.connection.CreateObjectWithId(BucketName, int(snapshot.EndpointID), snapshot)
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ func (store *Store) MigrateData() error {
|
||||||
RoleService: store.RoleService,
|
RoleService: store.RoleService,
|
||||||
ScheduleService: store.ScheduleService,
|
ScheduleService: store.ScheduleService,
|
||||||
SettingsService: store.SettingsService,
|
SettingsService: store.SettingsService,
|
||||||
|
SnapshotService: store.SnapshotService,
|
||||||
StackService: store.StackService,
|
StackService: store.StackService,
|
||||||
TagService: store.TagService,
|
TagService: store.TagService,
|
||||||
TeamMembershipService: store.TeamMembershipService,
|
TeamMembershipService: store.TeamMembershipService,
|
||||||
|
|
|
@ -108,6 +108,9 @@ func (m *Migrator) Migrate() error {
|
||||||
|
|
||||||
// Portainer 2.15
|
// Portainer 2.15
|
||||||
newMigration(60, m.migrateDBVersionToDB60),
|
newMigration(60, m.migrateDBVersionToDB60),
|
||||||
|
|
||||||
|
// Portainer 2.16
|
||||||
|
newMigration(70, m.migrateDBVersionToDB70),
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastDbVersion int
|
var lastDbVersion int
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package migrator
|
||||||
|
|
||||||
|
import (
|
||||||
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *Migrator) migrateDBVersionToDB70() error {
|
||||||
|
// foreach endpoint
|
||||||
|
endpoints, err := m.endpointService.Endpoints()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
|
// copy snapshots to new object
|
||||||
|
migrateLog.Info("- moving snapshots from endpoint to new object")
|
||||||
|
snapshot := portainer.Snapshot{EndpointID: endpoint.ID}
|
||||||
|
|
||||||
|
if len(endpoint.Snapshots) > 0 {
|
||||||
|
snapshot.Docker = &endpoint.Snapshots[len(endpoint.Snapshots)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(endpoint.Kubernetes.Snapshots) > 0 {
|
||||||
|
snapshot.Kubernetes = &endpoint.Kubernetes.Snapshots[len(endpoint.Kubernetes.Snapshots)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// save new object
|
||||||
|
err = m.snapshotService.Create(&snapshot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set to nil old fields
|
||||||
|
migrateLog.Info("- deleting snapshot from endpoint")
|
||||||
|
endpoint.Snapshots = []portainer.DockerSnapshot{}
|
||||||
|
endpoint.Kubernetes.Snapshots = []portainer.KubernetesSnapshot{}
|
||||||
|
|
||||||
|
// update endpoint
|
||||||
|
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/portainer/portainer/api/dataservices/role"
|
"github.com/portainer/portainer/api/dataservices/role"
|
||||||
"github.com/portainer/portainer/api/dataservices/schedule"
|
"github.com/portainer/portainer/api/dataservices/schedule"
|
||||||
"github.com/portainer/portainer/api/dataservices/settings"
|
"github.com/portainer/portainer/api/dataservices/settings"
|
||||||
|
"github.com/portainer/portainer/api/dataservices/snapshot"
|
||||||
"github.com/portainer/portainer/api/dataservices/stack"
|
"github.com/portainer/portainer/api/dataservices/stack"
|
||||||
"github.com/portainer/portainer/api/dataservices/tag"
|
"github.com/portainer/portainer/api/dataservices/tag"
|
||||||
"github.com/portainer/portainer/api/dataservices/teammembership"
|
"github.com/portainer/portainer/api/dataservices/teammembership"
|
||||||
|
@ -35,6 +36,7 @@ type (
|
||||||
roleService *role.Service
|
roleService *role.Service
|
||||||
scheduleService *schedule.Service
|
scheduleService *schedule.Service
|
||||||
settingsService *settings.Service
|
settingsService *settings.Service
|
||||||
|
snapshotService *snapshot.Service
|
||||||
stackService *stack.Service
|
stackService *stack.Service
|
||||||
tagService *tag.Service
|
tagService *tag.Service
|
||||||
teamMembershipService *teammembership.Service
|
teamMembershipService *teammembership.Service
|
||||||
|
@ -58,6 +60,7 @@ type (
|
||||||
RoleService *role.Service
|
RoleService *role.Service
|
||||||
ScheduleService *schedule.Service
|
ScheduleService *schedule.Service
|
||||||
SettingsService *settings.Service
|
SettingsService *settings.Service
|
||||||
|
SnapshotService *snapshot.Service
|
||||||
StackService *stack.Service
|
StackService *stack.Service
|
||||||
TagService *tag.Service
|
TagService *tag.Service
|
||||||
TeamMembershipService *teammembership.Service
|
TeamMembershipService *teammembership.Service
|
||||||
|
@ -83,6 +86,7 @@ func NewMigrator(parameters *MigratorParameters) *Migrator {
|
||||||
roleService: parameters.RoleService,
|
roleService: parameters.RoleService,
|
||||||
scheduleService: parameters.ScheduleService,
|
scheduleService: parameters.ScheduleService,
|
||||||
settingsService: parameters.SettingsService,
|
settingsService: parameters.SettingsService,
|
||||||
|
snapshotService: parameters.SnapshotService,
|
||||||
tagService: parameters.TagService,
|
tagService: parameters.TagService,
|
||||||
teamMembershipService: parameters.TeamMembershipService,
|
teamMembershipService: parameters.TeamMembershipService,
|
||||||
stackService: parameters.StackService,
|
stackService: parameters.StackService,
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/portainer/portainer/api/dataservices/role"
|
"github.com/portainer/portainer/api/dataservices/role"
|
||||||
"github.com/portainer/portainer/api/dataservices/schedule"
|
"github.com/portainer/portainer/api/dataservices/schedule"
|
||||||
"github.com/portainer/portainer/api/dataservices/settings"
|
"github.com/portainer/portainer/api/dataservices/settings"
|
||||||
|
"github.com/portainer/portainer/api/dataservices/snapshot"
|
||||||
"github.com/portainer/portainer/api/dataservices/ssl"
|
"github.com/portainer/portainer/api/dataservices/ssl"
|
||||||
"github.com/portainer/portainer/api/dataservices/stack"
|
"github.com/portainer/portainer/api/dataservices/stack"
|
||||||
"github.com/portainer/portainer/api/dataservices/tag"
|
"github.com/portainer/portainer/api/dataservices/tag"
|
||||||
|
@ -63,6 +64,7 @@ type Store struct {
|
||||||
APIKeyRepositoryService *apikeyrepository.Service
|
APIKeyRepositoryService *apikeyrepository.Service
|
||||||
ScheduleService *schedule.Service
|
ScheduleService *schedule.Service
|
||||||
SettingsService *settings.Service
|
SettingsService *settings.Service
|
||||||
|
SnapshotService *snapshot.Service
|
||||||
SSLSettingsService *ssl.Service
|
SSLSettingsService *ssl.Service
|
||||||
StackService *stack.Service
|
StackService *stack.Service
|
||||||
TagService *tag.Service
|
TagService *tag.Service
|
||||||
|
@ -171,6 +173,12 @@ func (store *Store) initServices() error {
|
||||||
}
|
}
|
||||||
store.SettingsService = settingsService
|
store.SettingsService = settingsService
|
||||||
|
|
||||||
|
snapshotService, err := snapshot.NewService(store.connection)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
store.SnapshotService = snapshotService
|
||||||
|
|
||||||
sslSettingsService, err := ssl.NewService(store.connection)
|
sslSettingsService, err := ssl.NewService(store.connection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -315,6 +323,10 @@ func (store *Store) Settings() dataservices.SettingsService {
|
||||||
return store.SettingsService
|
return store.SettingsService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (store *Store) Snapshot() dataservices.SnapshotService {
|
||||||
|
return store.SnapshotService
|
||||||
|
}
|
||||||
|
|
||||||
// SSLSettings gives access to the SSL Settings data management layer
|
// SSLSettings gives access to the SSL Settings data management layer
|
||||||
func (store *Store) SSLSettings() dataservices.SSLSettingsService {
|
func (store *Store) SSLSettings() dataservices.SSLSettingsService {
|
||||||
return store.SSLSettingsService
|
return store.SSLSettingsService
|
||||||
|
@ -375,6 +387,7 @@ type storeExport struct {
|
||||||
Role []portainer.Role `json:"roles,omitempty"`
|
Role []portainer.Role `json:"roles,omitempty"`
|
||||||
Schedules []portainer.Schedule `json:"schedules,omitempty"`
|
Schedules []portainer.Schedule `json:"schedules,omitempty"`
|
||||||
Settings portainer.Settings `json:"settings,omitempty"`
|
Settings portainer.Settings `json:"settings,omitempty"`
|
||||||
|
Snapshot []portainer.Snapshot `json:"snapshots,omitempty"`
|
||||||
SSLSettings portainer.SSLSettings `json:"ssl,omitempty"`
|
SSLSettings portainer.SSLSettings `json:"ssl,omitempty"`
|
||||||
Stack []portainer.Stack `json:"stacks,omitempty"`
|
Stack []portainer.Stack `json:"stacks,omitempty"`
|
||||||
Tag []portainer.Tag `json:"tags,omitempty"`
|
Tag []portainer.Tag `json:"tags,omitempty"`
|
||||||
|
@ -503,6 +516,14 @@ func (store *Store) Export(filename string) (err error) {
|
||||||
backup.Settings = *settings
|
backup.Settings = *settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if snapshot, err := store.Snapshot().Snapshots(); err != nil {
|
||||||
|
if !store.IsErrObjectNotFound(err) {
|
||||||
|
logrus.WithError(err).Errorf("Exporting Snapshots")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
backup.Snapshot = snapshot
|
||||||
|
}
|
||||||
|
|
||||||
if settings, err := store.SSLSettings().Settings(); err != nil {
|
if settings, err := store.SSLSettings().Settings(); err != nil {
|
||||||
if !store.IsErrObjectNotFound(err) {
|
if !store.IsErrObjectNotFound(err) {
|
||||||
log.Error().Err(err).Msg("exporting SSL Settings")
|
log.Error().Err(err).Msg("exporting SSL Settings")
|
||||||
|
@ -662,6 +683,10 @@ func (store *Store) Import(filename string) (err error) {
|
||||||
store.Settings().UpdateSettings(&backup.Settings)
|
store.Settings().UpdateSettings(&backup.Settings)
|
||||||
store.SSLSettings().UpdateSettings(&backup.SSLSettings)
|
store.SSLSettings().UpdateSettings(&backup.SSLSettings)
|
||||||
|
|
||||||
|
for _, v := range backup.Snapshot {
|
||||||
|
store.Snapshot().UpdateSnapshot(&v)
|
||||||
|
}
|
||||||
|
|
||||||
for _, v := range backup.Stack {
|
for _, v := range backup.Stack {
|
||||||
store.Stack().UpdateStack(v.ID, &v)
|
store.Stack().UpdateStack(v.ID, &v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
"UseLoadBalancer": false,
|
"UseLoadBalancer": false,
|
||||||
"UseServerMetrics": false
|
"UseServerMetrics": false
|
||||||
},
|
},
|
||||||
"Snapshots": null
|
"Snapshots": []
|
||||||
},
|
},
|
||||||
"LastCheckInDate": 0,
|
"LastCheckInDate": 0,
|
||||||
"Name": "local",
|
"Name": "local",
|
||||||
|
@ -77,127 +77,7 @@
|
||||||
"allowVolumeBrowserForRegularUsers": false,
|
"allowVolumeBrowserForRegularUsers": false,
|
||||||
"enableHostManagementFeatures": false
|
"enableHostManagementFeatures": false
|
||||||
},
|
},
|
||||||
"Snapshots": [
|
"Snapshots": [],
|
||||||
{
|
|
||||||
"DockerSnapshotRaw": {
|
|
||||||
"Containers": null,
|
|
||||||
"Images": null,
|
|
||||||
"Info": {
|
|
||||||
"Architecture": "",
|
|
||||||
"BridgeNfIp6tables": false,
|
|
||||||
"BridgeNfIptables": false,
|
|
||||||
"CPUSet": false,
|
|
||||||
"CPUShares": false,
|
|
||||||
"CgroupDriver": "",
|
|
||||||
"ContainerdCommit": {
|
|
||||||
"Expected": "",
|
|
||||||
"ID": ""
|
|
||||||
},
|
|
||||||
"Containers": 0,
|
|
||||||
"ContainersPaused": 0,
|
|
||||||
"ContainersRunning": 0,
|
|
||||||
"ContainersStopped": 0,
|
|
||||||
"CpuCfsPeriod": false,
|
|
||||||
"CpuCfsQuota": false,
|
|
||||||
"Debug": false,
|
|
||||||
"DefaultRuntime": "",
|
|
||||||
"DockerRootDir": "",
|
|
||||||
"Driver": "",
|
|
||||||
"DriverStatus": null,
|
|
||||||
"ExperimentalBuild": false,
|
|
||||||
"GenericResources": null,
|
|
||||||
"HttpProxy": "",
|
|
||||||
"HttpsProxy": "",
|
|
||||||
"ID": "",
|
|
||||||
"IPv4Forwarding": false,
|
|
||||||
"Images": 0,
|
|
||||||
"IndexServerAddress": "",
|
|
||||||
"InitBinary": "",
|
|
||||||
"InitCommit": {
|
|
||||||
"Expected": "",
|
|
||||||
"ID": ""
|
|
||||||
},
|
|
||||||
"Isolation": "",
|
|
||||||
"KernelMemory": false,
|
|
||||||
"KernelMemoryTCP": false,
|
|
||||||
"KernelVersion": "",
|
|
||||||
"Labels": null,
|
|
||||||
"LiveRestoreEnabled": false,
|
|
||||||
"LoggingDriver": "",
|
|
||||||
"MemTotal": 0,
|
|
||||||
"MemoryLimit": false,
|
|
||||||
"NCPU": 0,
|
|
||||||
"NEventsListener": 0,
|
|
||||||
"NFd": 0,
|
|
||||||
"NGoroutines": 0,
|
|
||||||
"Name": "",
|
|
||||||
"NoProxy": "",
|
|
||||||
"OSType": "",
|
|
||||||
"OSVersion": "",
|
|
||||||
"OomKillDisable": false,
|
|
||||||
"OperatingSystem": "",
|
|
||||||
"PidsLimit": false,
|
|
||||||
"Plugins": {
|
|
||||||
"Authorization": null,
|
|
||||||
"Log": null,
|
|
||||||
"Network": null,
|
|
||||||
"Volume": null
|
|
||||||
},
|
|
||||||
"RegistryConfig": null,
|
|
||||||
"RuncCommit": {
|
|
||||||
"Expected": "",
|
|
||||||
"ID": ""
|
|
||||||
},
|
|
||||||
"Runtimes": null,
|
|
||||||
"SecurityOptions": null,
|
|
||||||
"ServerVersion": "",
|
|
||||||
"SwapLimit": false,
|
|
||||||
"Swarm": {
|
|
||||||
"ControlAvailable": false,
|
|
||||||
"Error": "",
|
|
||||||
"LocalNodeState": "",
|
|
||||||
"NodeAddr": "",
|
|
||||||
"NodeID": "",
|
|
||||||
"RemoteManagers": null
|
|
||||||
},
|
|
||||||
"SystemTime": "",
|
|
||||||
"Warnings": null
|
|
||||||
},
|
|
||||||
"Networks": null,
|
|
||||||
"Version": {
|
|
||||||
"ApiVersion": "",
|
|
||||||
"Arch": "",
|
|
||||||
"GitCommit": "",
|
|
||||||
"GoVersion": "",
|
|
||||||
"Os": "",
|
|
||||||
"Platform": {
|
|
||||||
"Name": ""
|
|
||||||
},
|
|
||||||
"Version": ""
|
|
||||||
},
|
|
||||||
"Volumes": {
|
|
||||||
"Volumes": null,
|
|
||||||
"Warnings": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"DockerVersion": "20.10.13",
|
|
||||||
"GpuUseAll": false,
|
|
||||||
"GpuUseList": null,
|
|
||||||
"HealthyContainerCount": 0,
|
|
||||||
"ImageCount": 9,
|
|
||||||
"NodeCount": 0,
|
|
||||||
"RunningContainerCount": 5,
|
|
||||||
"ServiceCount": 0,
|
|
||||||
"StackCount": 2,
|
|
||||||
"StoppedContainerCount": 0,
|
|
||||||
"Swarm": false,
|
|
||||||
"Time": 1648610112,
|
|
||||||
"TotalCPU": 8,
|
|
||||||
"TotalMemory": 25098706944,
|
|
||||||
"UnhealthyContainerCount": 0,
|
|
||||||
"VolumeCount": 10
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Status": 1,
|
"Status": 1,
|
||||||
"TLSConfig": {
|
"TLSConfig": {
|
||||||
"TLS": false,
|
"TLS": false,
|
||||||
|
@ -777,6 +657,131 @@
|
||||||
"mpsUser": ""
|
"mpsUser": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"snapshots": [
|
||||||
|
{
|
||||||
|
"Docker": {
|
||||||
|
"DockerSnapshotRaw": {
|
||||||
|
"Containers": null,
|
||||||
|
"Images": null,
|
||||||
|
"Info": {
|
||||||
|
"Architecture": "",
|
||||||
|
"BridgeNfIp6tables": false,
|
||||||
|
"BridgeNfIptables": false,
|
||||||
|
"CPUSet": false,
|
||||||
|
"CPUShares": false,
|
||||||
|
"CgroupDriver": "",
|
||||||
|
"ContainerdCommit": {
|
||||||
|
"Expected": "",
|
||||||
|
"ID": ""
|
||||||
|
},
|
||||||
|
"Containers": 0,
|
||||||
|
"ContainersPaused": 0,
|
||||||
|
"ContainersRunning": 0,
|
||||||
|
"ContainersStopped": 0,
|
||||||
|
"CpuCfsPeriod": false,
|
||||||
|
"CpuCfsQuota": false,
|
||||||
|
"Debug": false,
|
||||||
|
"DefaultRuntime": "",
|
||||||
|
"DockerRootDir": "",
|
||||||
|
"Driver": "",
|
||||||
|
"DriverStatus": null,
|
||||||
|
"ExperimentalBuild": false,
|
||||||
|
"GenericResources": null,
|
||||||
|
"HttpProxy": "",
|
||||||
|
"HttpsProxy": "",
|
||||||
|
"ID": "",
|
||||||
|
"IPv4Forwarding": false,
|
||||||
|
"Images": 0,
|
||||||
|
"IndexServerAddress": "",
|
||||||
|
"InitBinary": "",
|
||||||
|
"InitCommit": {
|
||||||
|
"Expected": "",
|
||||||
|
"ID": ""
|
||||||
|
},
|
||||||
|
"Isolation": "",
|
||||||
|
"KernelMemory": false,
|
||||||
|
"KernelMemoryTCP": false,
|
||||||
|
"KernelVersion": "",
|
||||||
|
"Labels": null,
|
||||||
|
"LiveRestoreEnabled": false,
|
||||||
|
"LoggingDriver": "",
|
||||||
|
"MemTotal": 0,
|
||||||
|
"MemoryLimit": false,
|
||||||
|
"NCPU": 0,
|
||||||
|
"NEventsListener": 0,
|
||||||
|
"NFd": 0,
|
||||||
|
"NGoroutines": 0,
|
||||||
|
"Name": "",
|
||||||
|
"NoProxy": "",
|
||||||
|
"OSType": "",
|
||||||
|
"OSVersion": "",
|
||||||
|
"OomKillDisable": false,
|
||||||
|
"OperatingSystem": "",
|
||||||
|
"PidsLimit": false,
|
||||||
|
"Plugins": {
|
||||||
|
"Authorization": null,
|
||||||
|
"Log": null,
|
||||||
|
"Network": null,
|
||||||
|
"Volume": null
|
||||||
|
},
|
||||||
|
"RegistryConfig": null,
|
||||||
|
"RuncCommit": {
|
||||||
|
"Expected": "",
|
||||||
|
"ID": ""
|
||||||
|
},
|
||||||
|
"Runtimes": null,
|
||||||
|
"SecurityOptions": null,
|
||||||
|
"ServerVersion": "",
|
||||||
|
"SwapLimit": false,
|
||||||
|
"Swarm": {
|
||||||
|
"ControlAvailable": false,
|
||||||
|
"Error": "",
|
||||||
|
"LocalNodeState": "",
|
||||||
|
"NodeAddr": "",
|
||||||
|
"NodeID": "",
|
||||||
|
"RemoteManagers": null
|
||||||
|
},
|
||||||
|
"SystemTime": "",
|
||||||
|
"Warnings": null
|
||||||
|
},
|
||||||
|
"Networks": null,
|
||||||
|
"Version": {
|
||||||
|
"ApiVersion": "",
|
||||||
|
"Arch": "",
|
||||||
|
"GitCommit": "",
|
||||||
|
"GoVersion": "",
|
||||||
|
"Os": "",
|
||||||
|
"Platform": {
|
||||||
|
"Name": ""
|
||||||
|
},
|
||||||
|
"Version": ""
|
||||||
|
},
|
||||||
|
"Volumes": {
|
||||||
|
"Volumes": null,
|
||||||
|
"Warnings": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DockerVersion": "20.10.13",
|
||||||
|
"GpuUseAll": false,
|
||||||
|
"GpuUseList": null,
|
||||||
|
"HealthyContainerCount": 0,
|
||||||
|
"ImageCount": 9,
|
||||||
|
"NodeCount": 0,
|
||||||
|
"RunningContainerCount": 5,
|
||||||
|
"ServiceCount": 0,
|
||||||
|
"StackCount": 2,
|
||||||
|
"StoppedContainerCount": 0,
|
||||||
|
"Swarm": false,
|
||||||
|
"Time": 1648610112,
|
||||||
|
"TotalCPU": 8,
|
||||||
|
"TotalMemory": 25098706944,
|
||||||
|
"UnhealthyContainerCount": 0,
|
||||||
|
"VolumeCount": 10
|
||||||
|
},
|
||||||
|
"EndpointId": 1,
|
||||||
|
"Kubernetes": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"ssl": {
|
"ssl": {
|
||||||
"certPath": "",
|
"certPath": "",
|
||||||
"httpEnabled": true,
|
"httpEnabled": true,
|
||||||
|
|
|
@ -60,6 +60,15 @@ func initDemoLocalEndpoint(store dataservices.DataStore) (portainer.EndpointID,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := store.Endpoint().Create(localEndpoint)
|
err := store.Endpoint().Create(localEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
return id, errors.WithMessage(err, "failed creating local endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = store.Snapshot().Create(&portainer.Snapshot{EndpointID: id})
|
||||||
|
if err != nil {
|
||||||
|
return id, errors.WithMessage(err, "failed creating snapshot")
|
||||||
|
}
|
||||||
|
|
||||||
return id, errors.WithMessage(err, "failed creating local endpoint")
|
return id, errors.WithMessage(err, "failed creating local endpoint")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,5 +44,18 @@ func (handler *Handler) endpointInspect(w http.ResponseWriter, r *http.Request)
|
||||||
hideFields(endpoint)
|
hideFields(endpoint)
|
||||||
endpoint.ComposeSyntaxMaxVersion = handler.ComposeStackManager.ComposeSyntaxMaxVersion()
|
endpoint.ComposeSyntaxMaxVersion = handler.ComposeStackManager.ComposeSyntaxMaxVersion()
|
||||||
|
|
||||||
|
if !excludeSnapshot(r) {
|
||||||
|
err = handler.SnapshotService.FillSnapshotData(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return httperror.InternalServerError("Unable to add snapshot data", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return response.JSON(w, endpoint)
|
return response.JSON(w, endpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func excludeSnapshot(r *http.Request) bool {
|
||||||
|
excludeSnapshot, _ := request.RetrieveBooleanQueryParameter(r, "excludeSnapshot", true)
|
||||||
|
|
||||||
|
return excludeSnapshot
|
||||||
|
}
|
||||||
|
|
|
@ -103,6 +103,12 @@ func (handler *Handler) endpointList(w http.ResponseWriter, r *http.Request) *ht
|
||||||
paginatedEndpoints[idx].EdgeCheckinInterval = settings.EdgeAgentCheckinInterval
|
paginatedEndpoints[idx].EdgeCheckinInterval = settings.EdgeAgentCheckinInterval
|
||||||
}
|
}
|
||||||
paginatedEndpoints[idx].QueryDate = time.Now().Unix()
|
paginatedEndpoints[idx].QueryDate = time.Now().Unix()
|
||||||
|
if !query.excludeSnapshots {
|
||||||
|
err = handler.SnapshotService.FillSnapshotData(&paginatedEndpoints[idx])
|
||||||
|
if err != nil {
|
||||||
|
return httperror.InternalServerError("Unable to add snapshot data", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("X-Total-Count", strconv.Itoa(filteredEndpointCount))
|
w.Header().Set("X-Total-Count", strconv.Itoa(filteredEndpointCount))
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
"github.com/portainer/portainer/api/datastore"
|
"github.com/portainer/portainer/api/datastore"
|
||||||
"github.com/portainer/portainer/api/http/security"
|
"github.com/portainer/portainer/api/http/security"
|
||||||
|
"github.com/portainer/portainer/api/internal/snapshot"
|
||||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -203,6 +204,8 @@ func setup(t *testing.T, endpoints []portainer.Endpoint) (handler *Handler, tear
|
||||||
handler.DataStore = store
|
handler.DataStore = store
|
||||||
handler.ComposeStackManager = testhelpers.NewComposeStackManager()
|
handler.ComposeStackManager = testhelpers.NewComposeStackManager()
|
||||||
|
|
||||||
|
handler.SnapshotService, _ = snapshot.NewService("1s", store, nil, nil, nil)
|
||||||
|
|
||||||
return handler, teardown
|
return handler, teardown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,8 +53,6 @@ func (handler *Handler) endpointSnapshot(w http.ResponseWriter, r *http.Request)
|
||||||
latestEndpointReference.Status = portainer.EndpointStatusDown
|
latestEndpointReference.Status = portainer.EndpointStatusDown
|
||||||
}
|
}
|
||||||
|
|
||||||
latestEndpointReference.Snapshots = endpoint.Snapshots
|
|
||||||
latestEndpointReference.Kubernetes.Snapshots = endpoint.Kubernetes.Snapshots
|
|
||||||
latestEndpointReference.Agent.Version = endpoint.Agent.Version
|
latestEndpointReference.Agent.Version = endpoint.Agent.Version
|
||||||
|
|
||||||
err = handler.DataStore.Endpoint().UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
|
err = handler.DataStore.Endpoint().UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
|
||||||
|
|
|
@ -56,8 +56,6 @@ func (handler *Handler) endpointSnapshots(w http.ResponseWriter, r *http.Request
|
||||||
endpoint.Status = portainer.EndpointStatusDown
|
endpoint.Status = portainer.EndpointStatusDown
|
||||||
}
|
}
|
||||||
|
|
||||||
latestEndpointReference.Snapshots = endpoint.Snapshots
|
|
||||||
latestEndpointReference.Kubernetes.Snapshots = endpoint.Kubernetes.Snapshots
|
|
||||||
latestEndpointReference.Agent.Version = endpoint.Agent.Version
|
latestEndpointReference.Agent.Version = endpoint.Agent.Version
|
||||||
|
|
||||||
err = handler.DataStore.Endpoint().UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
|
err = handler.DataStore.Endpoint().UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
|
||||||
|
|
|
@ -329,5 +329,10 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = handler.SnapshotService.FillSnapshotData(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to add snapshot data", err}
|
||||||
|
}
|
||||||
|
|
||||||
return response.JSON(w, endpoint)
|
return response.JSON(w, endpoint)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ type EnvironmentsQuery struct {
|
||||||
status []portainer.EndpointStatus
|
status []portainer.EndpointStatus
|
||||||
edgeDevice *bool
|
edgeDevice *bool
|
||||||
edgeDeviceUntrusted bool
|
edgeDeviceUntrusted bool
|
||||||
|
excludeSnapshots bool
|
||||||
name string
|
name string
|
||||||
agentVersions []string
|
agentVersions []string
|
||||||
}
|
}
|
||||||
|
@ -74,6 +75,8 @@ func parseQuery(r *http.Request) (EnvironmentsQuery, error) {
|
||||||
|
|
||||||
edgeDeviceUntrusted, _ := request.RetrieveBooleanQueryParameter(r, "edgeDeviceUntrusted", true)
|
edgeDeviceUntrusted, _ := request.RetrieveBooleanQueryParameter(r, "edgeDeviceUntrusted", true)
|
||||||
|
|
||||||
|
excludeSnapshots, _ := request.RetrieveBooleanQueryParameter(r, "excludeSnapshots", true)
|
||||||
|
|
||||||
return EnvironmentsQuery{
|
return EnvironmentsQuery{
|
||||||
search: search,
|
search: search,
|
||||||
types: endpointTypes,
|
types: endpointTypes,
|
||||||
|
@ -84,6 +87,7 @@ func parseQuery(r *http.Request) (EnvironmentsQuery, error) {
|
||||||
status: status,
|
status: status,
|
||||||
edgeDevice: edgeDevice,
|
edgeDevice: edgeDevice,
|
||||||
edgeDeviceUntrusted: edgeDeviceUntrusted,
|
edgeDeviceUntrusted: edgeDeviceUntrusted,
|
||||||
|
excludeSnapshots: excludeSnapshots,
|
||||||
name: name,
|
name: name,
|
||||||
agentVersions: agentVersions,
|
agentVersions: agentVersions,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -57,6 +57,15 @@ func (factory *ProxyFactory) newDockerHTTPProxy(endpoint *portainer.Endpoint) (h
|
||||||
endpointURL.Scheme = "https"
|
endpointURL.Scheme = "https"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
snapshot, err := factory.dataStore.Snapshot().Snapshot(endpoint.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if snapshot.Docker != nil {
|
||||||
|
endpoint.Snapshots = []portainer.DockerSnapshot{*snapshot.Docker}
|
||||||
|
}
|
||||||
|
|
||||||
transportParameters := &docker.TransportParameters{
|
transportParameters := &docker.TransportParameters{
|
||||||
Endpoint: endpoint,
|
Endpoint: endpoint,
|
||||||
DataStore: factory.dataStore,
|
DataStore: factory.dataStore,
|
||||||
|
|
|
@ -119,27 +119,59 @@ func (service *Service) SnapshotEndpoint(endpoint *portainer.Endpoint) error {
|
||||||
return service.snapshotDockerEndpoint(endpoint)
|
return service.snapshotDockerEndpoint(endpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (service *Service) snapshotKubernetesEndpoint(endpoint *portainer.Endpoint) error {
|
func (service *Service) Create(snapshot portainer.Snapshot) error {
|
||||||
snapshot, err := service.kubernetesSnapshotter.CreateSnapshot(endpoint)
|
return service.dataStore.Snapshot().Create(&snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) FillSnapshotData(endpoint *portainer.Endpoint) error {
|
||||||
|
snapshot, err := service.dataStore.Snapshot().Snapshot(endpoint.ID)
|
||||||
|
if service.dataStore.IsErrObjectNotFound(err) {
|
||||||
|
endpoint.Snapshots = []portainer.DockerSnapshot{}
|
||||||
|
endpoint.Kubernetes.Snapshots = []portainer.KubernetesSnapshot{}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if snapshot != nil {
|
if snapshot.Docker != nil {
|
||||||
endpoint.Kubernetes.Snapshots = []portainer.KubernetesSnapshot{*snapshot}
|
endpoint.Snapshots = []portainer.DockerSnapshot{*snapshot.Docker}
|
||||||
|
}
|
||||||
|
|
||||||
|
if snapshot.Kubernetes != nil {
|
||||||
|
endpoint.Kubernetes.Snapshots = []portainer.KubernetesSnapshot{*snapshot.Kubernetes}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) snapshotKubernetesEndpoint(endpoint *portainer.Endpoint) error {
|
||||||
|
kubernetesSnapshot, err := service.kubernetesSnapshotter.CreateSnapshot(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if kubernetesSnapshot != nil {
|
||||||
|
snapshot := &portainer.Snapshot{EndpointID: endpoint.ID, Kubernetes: kubernetesSnapshot}
|
||||||
|
|
||||||
|
return service.dataStore.Snapshot().Create(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (service *Service) snapshotDockerEndpoint(endpoint *portainer.Endpoint) error {
|
func (service *Service) snapshotDockerEndpoint(endpoint *portainer.Endpoint) error {
|
||||||
snapshot, err := service.dockerSnapshotter.CreateSnapshot(endpoint)
|
dockerSnapshot, err := service.dockerSnapshotter.CreateSnapshot(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if snapshot != nil {
|
if dockerSnapshot != nil {
|
||||||
endpoint.Snapshots = []portainer.DockerSnapshot{*snapshot}
|
snapshot := &portainer.Snapshot{EndpointID: endpoint.ID, Docker: dockerSnapshot}
|
||||||
|
|
||||||
|
return service.dataStore.Snapshot().Create(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -203,8 +235,6 @@ func (service *Service) snapshotEndpoints() error {
|
||||||
latestEndpointReference.Status = portainer.EndpointStatusDown
|
latestEndpointReference.Status = portainer.EndpointStatusDown
|
||||||
}
|
}
|
||||||
|
|
||||||
latestEndpointReference.Snapshots = endpoint.Snapshots
|
|
||||||
latestEndpointReference.Kubernetes.Snapshots = endpoint.Kubernetes.Snapshots
|
|
||||||
latestEndpointReference.Agent.Version = endpoint.Agent.Version
|
latestEndpointReference.Agent.Version = endpoint.Agent.Version
|
||||||
|
|
||||||
err = service.dataStore.Endpoint().UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
|
err = service.dataStore.Endpoint().UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
|
||||||
|
|
|
@ -25,6 +25,7 @@ type testDatastore struct {
|
||||||
role dataservices.RoleService
|
role dataservices.RoleService
|
||||||
sslSettings dataservices.SSLSettingsService
|
sslSettings dataservices.SSLSettingsService
|
||||||
settings dataservices.SettingsService
|
settings dataservices.SettingsService
|
||||||
|
snapshot dataservices.SnapshotService
|
||||||
stack dataservices.StackService
|
stack dataservices.StackService
|
||||||
tag dataservices.TagService
|
tag dataservices.TagService
|
||||||
teamMembership dataservices.TeamMembershipService
|
teamMembership dataservices.TeamMembershipService
|
||||||
|
@ -69,6 +70,7 @@ func (d *testDatastore) APIKeyRepository() dataservices.APIKeyRepository {
|
||||||
return d.apiKeyRepositoryService
|
return d.apiKeyRepositoryService
|
||||||
}
|
}
|
||||||
func (d *testDatastore) Settings() dataservices.SettingsService { return d.settings }
|
func (d *testDatastore) Settings() dataservices.SettingsService { return d.settings }
|
||||||
|
func (d *testDatastore) Snapshot() dataservices.SnapshotService { return d.snapshot }
|
||||||
func (d *testDatastore) SSLSettings() dataservices.SSLSettingsService { return d.sslSettings }
|
func (d *testDatastore) SSLSettings() dataservices.SSLSettingsService { return d.sslSettings }
|
||||||
func (d *testDatastore) Stack() dataservices.StackService { return d.stack }
|
func (d *testDatastore) Stack() dataservices.StackService { return d.stack }
|
||||||
func (d *testDatastore) Tag() dataservices.TagService { return d.tag }
|
func (d *testDatastore) Tag() dataservices.TagService { return d.tag }
|
||||||
|
|
|
@ -1243,6 +1243,12 @@ type (
|
||||||
// WebhookType represents the type of resource a webhook is related to
|
// WebhookType represents the type of resource a webhook is related to
|
||||||
WebhookType int
|
WebhookType int
|
||||||
|
|
||||||
|
Snapshot struct {
|
||||||
|
EndpointID EndpointID `json:"EndpointId"`
|
||||||
|
Docker *DockerSnapshot `json:"Docker"`
|
||||||
|
Kubernetes *KubernetesSnapshot `json:"Kubernetes"`
|
||||||
|
}
|
||||||
|
|
||||||
// CLIService represents a service for managing CLI
|
// CLIService represents a service for managing CLI
|
||||||
CLIService interface {
|
CLIService interface {
|
||||||
ParseFlags(version string) (*CLIFlags, error)
|
ParseFlags(version string) (*CLIFlags, error)
|
||||||
|
@ -1420,6 +1426,7 @@ type (
|
||||||
Start()
|
Start()
|
||||||
SetSnapshotInterval(snapshotInterval string) error
|
SetSnapshotInterval(snapshotInterval string) error
|
||||||
SnapshotEndpoint(endpoint *Endpoint) error
|
SnapshotEndpoint(endpoint *Endpoint) error
|
||||||
|
FillSnapshotData(endpoint *Endpoint) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// SwarmStackManager represents a service to manage Swarm stacks
|
// SwarmStackManager represents a service to manage Swarm stacks
|
||||||
|
|
|
@ -92,6 +92,7 @@ function Loader({ children, storageKey }: LoaderProps) {
|
||||||
edgeDevice: true,
|
edgeDevice: true,
|
||||||
search: debouncedSearchValue,
|
search: debouncedSearchValue,
|
||||||
types: EdgeTypes,
|
types: EdgeTypes,
|
||||||
|
excludeSnapshots: true,
|
||||||
...pagination,
|
...pagination,
|
||||||
},
|
},
|
||||||
settings.autoRefreshRate * 1000
|
settings.autoRefreshRate * 1000
|
||||||
|
|
|
@ -17,6 +17,7 @@ export function WaitingRoomView() {
|
||||||
const { environments, isLoading, totalCount } = useEnvironmentList({
|
const { environments, isLoading, totalCount } = useEnvironmentList({
|
||||||
edgeDevice: true,
|
edgeDevice: true,
|
||||||
edgeDeviceUntrusted: true,
|
edgeDeviceUntrusted: true,
|
||||||
|
excludeSnapshots: true,
|
||||||
types: EdgeTypes,
|
types: EdgeTypes,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ export interface EnvironmentsQueryParams {
|
||||||
status?: EnvironmentStatus[];
|
status?: EnvironmentStatus[];
|
||||||
edgeDevice?: boolean;
|
edgeDevice?: boolean;
|
||||||
edgeDeviceUntrusted?: boolean;
|
edgeDeviceUntrusted?: boolean;
|
||||||
|
excludeSnapshots?: boolean;
|
||||||
provisioned?: boolean;
|
provisioned?: boolean;
|
||||||
name?: string;
|
name?: string;
|
||||||
agentVersions?: string[];
|
agentVersions?: string[];
|
||||||
|
|
|
@ -50,7 +50,7 @@ function EndpointsController($q, $scope, $state, $async, EndpointService, GroupS
|
||||||
function getPaginatedEndpoints(start, limit, search) {
|
function getPaginatedEndpoints(start, limit, search) {
|
||||||
const deferred = $q.defer();
|
const deferred = $q.defer();
|
||||||
$q.all({
|
$q.all({
|
||||||
endpoints: getEnvironments({ start, limit, query: { search } }),
|
endpoints: getEnvironments({ start, limit, query: { search, excludeSnapshots: true } }),
|
||||||
groups: GroupService.groups(),
|
groups: GroupService.groups(),
|
||||||
})
|
})
|
||||||
.then(function success(data) {
|
.then(function success(data) {
|
||||||
|
|
Loading…
Reference in New Issue