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
|
||||
APIKeyRepository() APIKeyRepository
|
||||
Settings() SettingsService
|
||||
Snapshot() SnapshotService
|
||||
SSLSettings() SSLSettingsService
|
||||
Stack() StackService
|
||||
Tag() TagService
|
||||
|
@ -214,6 +215,15 @@ type (
|
|||
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 interface {
|
||||
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,
|
||||
ScheduleService: store.ScheduleService,
|
||||
SettingsService: store.SettingsService,
|
||||
SnapshotService: store.SnapshotService,
|
||||
StackService: store.StackService,
|
||||
TagService: store.TagService,
|
||||
TeamMembershipService: store.TeamMembershipService,
|
||||
|
|
|
@ -108,6 +108,9 @@ func (m *Migrator) Migrate() error {
|
|||
|
||||
// Portainer 2.15
|
||||
newMigration(60, m.migrateDBVersionToDB60),
|
||||
|
||||
// Portainer 2.16
|
||||
newMigration(70, m.migrateDBVersionToDB70),
|
||||
}
|
||||
|
||||
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/schedule"
|
||||
"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/tag"
|
||||
"github.com/portainer/portainer/api/dataservices/teammembership"
|
||||
|
@ -35,6 +36,7 @@ type (
|
|||
roleService *role.Service
|
||||
scheduleService *schedule.Service
|
||||
settingsService *settings.Service
|
||||
snapshotService *snapshot.Service
|
||||
stackService *stack.Service
|
||||
tagService *tag.Service
|
||||
teamMembershipService *teammembership.Service
|
||||
|
@ -58,6 +60,7 @@ type (
|
|||
RoleService *role.Service
|
||||
ScheduleService *schedule.Service
|
||||
SettingsService *settings.Service
|
||||
SnapshotService *snapshot.Service
|
||||
StackService *stack.Service
|
||||
TagService *tag.Service
|
||||
TeamMembershipService *teammembership.Service
|
||||
|
@ -83,6 +86,7 @@ func NewMigrator(parameters *MigratorParameters) *Migrator {
|
|||
roleService: parameters.RoleService,
|
||||
scheduleService: parameters.ScheduleService,
|
||||
settingsService: parameters.SettingsService,
|
||||
snapshotService: parameters.SnapshotService,
|
||||
tagService: parameters.TagService,
|
||||
teamMembershipService: parameters.TeamMembershipService,
|
||||
stackService: parameters.StackService,
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/portainer/portainer/api/dataservices/role"
|
||||
"github.com/portainer/portainer/api/dataservices/schedule"
|
||||
"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/stack"
|
||||
"github.com/portainer/portainer/api/dataservices/tag"
|
||||
|
@ -63,6 +64,7 @@ type Store struct {
|
|||
APIKeyRepositoryService *apikeyrepository.Service
|
||||
ScheduleService *schedule.Service
|
||||
SettingsService *settings.Service
|
||||
SnapshotService *snapshot.Service
|
||||
SSLSettingsService *ssl.Service
|
||||
StackService *stack.Service
|
||||
TagService *tag.Service
|
||||
|
@ -171,6 +173,12 @@ func (store *Store) initServices() error {
|
|||
}
|
||||
store.SettingsService = settingsService
|
||||
|
||||
snapshotService, err := snapshot.NewService(store.connection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
store.SnapshotService = snapshotService
|
||||
|
||||
sslSettingsService, err := ssl.NewService(store.connection)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -315,6 +323,10 @@ func (store *Store) Settings() dataservices.SettingsService {
|
|||
return store.SettingsService
|
||||
}
|
||||
|
||||
func (store *Store) Snapshot() dataservices.SnapshotService {
|
||||
return store.SnapshotService
|
||||
}
|
||||
|
||||
// SSLSettings gives access to the SSL Settings data management layer
|
||||
func (store *Store) SSLSettings() dataservices.SSLSettingsService {
|
||||
return store.SSLSettingsService
|
||||
|
@ -375,6 +387,7 @@ type storeExport struct {
|
|||
Role []portainer.Role `json:"roles,omitempty"`
|
||||
Schedules []portainer.Schedule `json:"schedules,omitempty"`
|
||||
Settings portainer.Settings `json:"settings,omitempty"`
|
||||
Snapshot []portainer.Snapshot `json:"snapshots,omitempty"`
|
||||
SSLSettings portainer.SSLSettings `json:"ssl,omitempty"`
|
||||
Stack []portainer.Stack `json:"stacks,omitempty"`
|
||||
Tag []portainer.Tag `json:"tags,omitempty"`
|
||||
|
@ -503,6 +516,14 @@ func (store *Store) Export(filename string) (err error) {
|
|||
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 !store.IsErrObjectNotFound(err) {
|
||||
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.SSLSettings().UpdateSettings(&backup.SSLSettings)
|
||||
|
||||
for _, v := range backup.Snapshot {
|
||||
store.Snapshot().UpdateSnapshot(&v)
|
||||
}
|
||||
|
||||
for _, v := range backup.Stack {
|
||||
store.Stack().UpdateStack(v.ID, &v)
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
"UseLoadBalancer": false,
|
||||
"UseServerMetrics": false
|
||||
},
|
||||
"Snapshots": null
|
||||
"Snapshots": []
|
||||
},
|
||||
"LastCheckInDate": 0,
|
||||
"Name": "local",
|
||||
|
@ -77,127 +77,7 @@
|
|||
"allowVolumeBrowserForRegularUsers": false,
|
||||
"enableHostManagementFeatures": false
|
||||
},
|
||||
"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
|
||||
}
|
||||
],
|
||||
"Snapshots": [],
|
||||
"Status": 1,
|
||||
"TLSConfig": {
|
||||
"TLS": false,
|
||||
|
@ -777,6 +657,131 @@
|
|||
"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": {
|
||||
"certPath": "",
|
||||
"httpEnabled": true,
|
||||
|
|
|
@ -60,6 +60,15 @@ func initDemoLocalEndpoint(store dataservices.DataStore) (portainer.EndpointID,
|
|||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
|
|
|
@ -44,5 +44,18 @@ func (handler *Handler) endpointInspect(w http.ResponseWriter, r *http.Request)
|
|||
hideFields(endpoint)
|
||||
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)
|
||||
}
|
||||
|
||||
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].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))
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/snapshot"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -203,6 +204,8 @@ func setup(t *testing.T, endpoints []portainer.Endpoint) (handler *Handler, tear
|
|||
handler.DataStore = store
|
||||
handler.ComposeStackManager = testhelpers.NewComposeStackManager()
|
||||
|
||||
handler.SnapshotService, _ = snapshot.NewService("1s", store, nil, nil, nil)
|
||||
|
||||
return handler, teardown
|
||||
}
|
||||
|
||||
|
|
|
@ -53,8 +53,6 @@ func (handler *Handler) endpointSnapshot(w http.ResponseWriter, r *http.Request)
|
|||
latestEndpointReference.Status = portainer.EndpointStatusDown
|
||||
}
|
||||
|
||||
latestEndpointReference.Snapshots = endpoint.Snapshots
|
||||
latestEndpointReference.Kubernetes.Snapshots = endpoint.Kubernetes.Snapshots
|
||||
latestEndpointReference.Agent.Version = endpoint.Agent.Version
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
latestEndpointReference.Snapshots = endpoint.Snapshots
|
||||
latestEndpointReference.Kubernetes.Snapshots = endpoint.Kubernetes.Snapshots
|
||||
latestEndpointReference.Agent.Version = endpoint.Agent.Version
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ type EnvironmentsQuery struct {
|
|||
status []portainer.EndpointStatus
|
||||
edgeDevice *bool
|
||||
edgeDeviceUntrusted bool
|
||||
excludeSnapshots bool
|
||||
name string
|
||||
agentVersions []string
|
||||
}
|
||||
|
@ -74,6 +75,8 @@ func parseQuery(r *http.Request) (EnvironmentsQuery, error) {
|
|||
|
||||
edgeDeviceUntrusted, _ := request.RetrieveBooleanQueryParameter(r, "edgeDeviceUntrusted", true)
|
||||
|
||||
excludeSnapshots, _ := request.RetrieveBooleanQueryParameter(r, "excludeSnapshots", true)
|
||||
|
||||
return EnvironmentsQuery{
|
||||
search: search,
|
||||
types: endpointTypes,
|
||||
|
@ -84,6 +87,7 @@ func parseQuery(r *http.Request) (EnvironmentsQuery, error) {
|
|||
status: status,
|
||||
edgeDevice: edgeDevice,
|
||||
edgeDeviceUntrusted: edgeDeviceUntrusted,
|
||||
excludeSnapshots: excludeSnapshots,
|
||||
name: name,
|
||||
agentVersions: agentVersions,
|
||||
}, nil
|
||||
|
|
|
@ -57,6 +57,15 @@ func (factory *ProxyFactory) newDockerHTTPProxy(endpoint *portainer.Endpoint) (h
|
|||
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{
|
||||
Endpoint: endpoint,
|
||||
DataStore: factory.dataStore,
|
||||
|
|
|
@ -119,27 +119,59 @@ func (service *Service) SnapshotEndpoint(endpoint *portainer.Endpoint) error {
|
|||
return service.snapshotDockerEndpoint(endpoint)
|
||||
}
|
||||
|
||||
func (service *Service) snapshotKubernetesEndpoint(endpoint *portainer.Endpoint) error {
|
||||
snapshot, err := service.kubernetesSnapshotter.CreateSnapshot(endpoint)
|
||||
func (service *Service) Create(snapshot portainer.Snapshot) error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
if snapshot != nil {
|
||||
endpoint.Kubernetes.Snapshots = []portainer.KubernetesSnapshot{*snapshot}
|
||||
if snapshot.Docker != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func (service *Service) snapshotDockerEndpoint(endpoint *portainer.Endpoint) error {
|
||||
snapshot, err := service.dockerSnapshotter.CreateSnapshot(endpoint)
|
||||
dockerSnapshot, err := service.dockerSnapshotter.CreateSnapshot(endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if snapshot != nil {
|
||||
endpoint.Snapshots = []portainer.DockerSnapshot{*snapshot}
|
||||
if dockerSnapshot != nil {
|
||||
snapshot := &portainer.Snapshot{EndpointID: endpoint.ID, Docker: dockerSnapshot}
|
||||
|
||||
return service.dataStore.Snapshot().Create(snapshot)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -203,8 +235,6 @@ func (service *Service) snapshotEndpoints() error {
|
|||
latestEndpointReference.Status = portainer.EndpointStatusDown
|
||||
}
|
||||
|
||||
latestEndpointReference.Snapshots = endpoint.Snapshots
|
||||
latestEndpointReference.Kubernetes.Snapshots = endpoint.Kubernetes.Snapshots
|
||||
latestEndpointReference.Agent.Version = endpoint.Agent.Version
|
||||
|
||||
err = service.dataStore.Endpoint().UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
|
||||
|
|
|
@ -25,6 +25,7 @@ type testDatastore struct {
|
|||
role dataservices.RoleService
|
||||
sslSettings dataservices.SSLSettingsService
|
||||
settings dataservices.SettingsService
|
||||
snapshot dataservices.SnapshotService
|
||||
stack dataservices.StackService
|
||||
tag dataservices.TagService
|
||||
teamMembership dataservices.TeamMembershipService
|
||||
|
@ -69,6 +70,7 @@ func (d *testDatastore) APIKeyRepository() dataservices.APIKeyRepository {
|
|||
return d.apiKeyRepositoryService
|
||||
}
|
||||
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) Stack() dataservices.StackService { return d.stack }
|
||||
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 int
|
||||
|
||||
Snapshot struct {
|
||||
EndpointID EndpointID `json:"EndpointId"`
|
||||
Docker *DockerSnapshot `json:"Docker"`
|
||||
Kubernetes *KubernetesSnapshot `json:"Kubernetes"`
|
||||
}
|
||||
|
||||
// CLIService represents a service for managing CLI
|
||||
CLIService interface {
|
||||
ParseFlags(version string) (*CLIFlags, error)
|
||||
|
@ -1420,6 +1426,7 @@ type (
|
|||
Start()
|
||||
SetSnapshotInterval(snapshotInterval string) error
|
||||
SnapshotEndpoint(endpoint *Endpoint) error
|
||||
FillSnapshotData(endpoint *Endpoint) error
|
||||
}
|
||||
|
||||
// SwarmStackManager represents a service to manage Swarm stacks
|
||||
|
|
|
@ -92,6 +92,7 @@ function Loader({ children, storageKey }: LoaderProps) {
|
|||
edgeDevice: true,
|
||||
search: debouncedSearchValue,
|
||||
types: EdgeTypes,
|
||||
excludeSnapshots: true,
|
||||
...pagination,
|
||||
},
|
||||
settings.autoRefreshRate * 1000
|
||||
|
|
|
@ -17,6 +17,7 @@ export function WaitingRoomView() {
|
|||
const { environments, isLoading, totalCount } = useEnvironmentList({
|
||||
edgeDevice: true,
|
||||
edgeDeviceUntrusted: true,
|
||||
excludeSnapshots: true,
|
||||
types: EdgeTypes,
|
||||
});
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ export interface EnvironmentsQueryParams {
|
|||
status?: EnvironmentStatus[];
|
||||
edgeDevice?: boolean;
|
||||
edgeDeviceUntrusted?: boolean;
|
||||
excludeSnapshots?: boolean;
|
||||
provisioned?: boolean;
|
||||
name?: string;
|
||||
agentVersions?: string[];
|
||||
|
|
|
@ -50,7 +50,7 @@ function EndpointsController($q, $scope, $state, $async, EndpointService, GroupS
|
|||
function getPaginatedEndpoints(start, limit, search) {
|
||||
const deferred = $q.defer();
|
||||
$q.all({
|
||||
endpoints: getEnvironments({ start, limit, query: { search } }),
|
||||
endpoints: getEnvironments({ start, limit, query: { search, excludeSnapshots: true } }),
|
||||
groups: GroupService.groups(),
|
||||
})
|
||||
.then(function success(data) {
|
||||
|
|
Loading…
Reference in New Issue