mirror of
https://github.com/portainer/portainer.git
synced 2025-07-19 21:39:40 +02:00
feat(snapshots): avoid parsing raw snapshots when possible BE-11724 (#560)
This commit is contained in:
parent
0dfde1374d
commit
995c3ef81b
16 changed files with 89 additions and 28 deletions
|
@ -159,6 +159,7 @@ type (
|
||||||
|
|
||||||
SnapshotService interface {
|
SnapshotService interface {
|
||||||
BaseCRUD[portainer.Snapshot, portainer.EndpointID]
|
BaseCRUD[portainer.Snapshot, portainer.EndpointID]
|
||||||
|
ReadWithoutSnapshotRaw(ID portainer.EndpointID) (*portainer.Snapshot, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSLSettingsService represents a service for managing application settings
|
// SSLSettingsService represents a service for managing application settings
|
||||||
|
|
|
@ -38,3 +38,16 @@ func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||||
func (service *Service) Create(snapshot *portainer.Snapshot) error {
|
func (service *Service) Create(snapshot *portainer.Snapshot) error {
|
||||||
return service.Connection.CreateObjectWithId(BucketName, int(snapshot.EndpointID), snapshot)
|
return service.Connection.CreateObjectWithId(BucketName, int(snapshot.EndpointID), snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service *Service) ReadWithoutSnapshotRaw(ID portainer.EndpointID) (*portainer.Snapshot, error) {
|
||||||
|
var snapshot *portainer.Snapshot
|
||||||
|
|
||||||
|
err := service.Connection.ViewTx(func(tx portainer.Transaction) error {
|
||||||
|
var err error
|
||||||
|
snapshot, err = service.Tx(tx).ReadWithoutSnapshotRaw(ID)
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
return snapshot, err
|
||||||
|
}
|
||||||
|
|
|
@ -12,3 +12,26 @@ type ServiceTx struct {
|
||||||
func (service ServiceTx) Create(snapshot *portainer.Snapshot) error {
|
func (service ServiceTx) Create(snapshot *portainer.Snapshot) error {
|
||||||
return service.Tx.CreateObjectWithId(BucketName, int(snapshot.EndpointID), snapshot)
|
return service.Tx.CreateObjectWithId(BucketName, int(snapshot.EndpointID), snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service ServiceTx) ReadWithoutSnapshotRaw(ID portainer.EndpointID) (*portainer.Snapshot, error) {
|
||||||
|
var snapshot struct {
|
||||||
|
Docker *struct {
|
||||||
|
X struct{} `json:"DockerSnapshotRaw"`
|
||||||
|
*portainer.DockerSnapshot
|
||||||
|
} `json:"Docker"`
|
||||||
|
|
||||||
|
portainer.Snapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
identifier := service.Connection.ConvertToKey(int(ID))
|
||||||
|
|
||||||
|
if err := service.Tx.GetObject(service.Bucket, identifier, &snapshot); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if snapshot.Docker != nil {
|
||||||
|
snapshot.Snapshot.Docker = snapshot.Docker.DockerSnapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
return &snapshot.Snapshot, nil
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ import (
|
||||||
// @security jwt
|
// @security jwt
|
||||||
// @produce json
|
// @produce json
|
||||||
// @param id path int true "Environment(Endpoint) identifier"
|
// @param id path int true "Environment(Endpoint) identifier"
|
||||||
|
// @param excludeSnapshot query bool false "if true, the snapshot data won't be retrieved"
|
||||||
|
// @param excludeSnapshotRaw query bool false "if true, the SnapshotRaw field won't be retrieved"
|
||||||
// @success 200 {object} portainer.Endpoint "Success"
|
// @success 200 {object} portainer.Endpoint "Success"
|
||||||
// @failure 400 "Invalid request"
|
// @failure 400 "Invalid request"
|
||||||
// @failure 404 "Environment(Endpoint) not found"
|
// @failure 404 "Environment(Endpoint) not found"
|
||||||
|
@ -37,8 +39,7 @@ func (handler *Handler) endpointInspect(w http.ResponseWriter, r *http.Request)
|
||||||
return httperror.InternalServerError("Unable to find an environment with the specified identifier inside the database", err)
|
return httperror.InternalServerError("Unable to find an environment with the specified identifier inside the database", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint)
|
if err := handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint); err != nil {
|
||||||
if err != nil {
|
|
||||||
return httperror.Forbidden("Permission denied to access environment", err)
|
return httperror.Forbidden("Permission denied to access environment", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,9 +52,11 @@ func (handler *Handler) endpointInspect(w http.ResponseWriter, r *http.Request)
|
||||||
endpointutils.UpdateEdgeEndpointHeartbeat(endpoint, settings)
|
endpointutils.UpdateEdgeEndpointHeartbeat(endpoint, settings)
|
||||||
endpoint.ComposeSyntaxMaxVersion = handler.ComposeStackManager.ComposeSyntaxMaxVersion()
|
endpoint.ComposeSyntaxMaxVersion = handler.ComposeStackManager.ComposeSyntaxMaxVersion()
|
||||||
|
|
||||||
if !excludeSnapshot(r) {
|
excludeSnapshot, _ := request.RetrieveBooleanQueryParameter(r, "excludeSnapshot", true)
|
||||||
err = handler.SnapshotService.FillSnapshotData(endpoint)
|
excludeRaw, _ := request.RetrieveBooleanQueryParameter(r, "excludeSnapshotRaw", true)
|
||||||
if err != nil {
|
|
||||||
|
if !excludeSnapshot {
|
||||||
|
if err := handler.SnapshotService.FillSnapshotData(endpoint, !excludeRaw); err != nil {
|
||||||
return httperror.InternalServerError("Unable to add snapshot data", err)
|
return httperror.InternalServerError("Unable to add snapshot data", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,9 +86,3 @@ func (handler *Handler) endpointInspect(w http.ResponseWriter, r *http.Request)
|
||||||
|
|
||||||
return response.JSON(w, endpoint)
|
return response.JSON(w, endpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
func excludeSnapshot(r *http.Request) bool {
|
|
||||||
excludeSnapshot, _ := request.RetrieveBooleanQueryParameter(r, "excludeSnapshot", true)
|
|
||||||
|
|
||||||
return excludeSnapshot
|
|
||||||
}
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ const (
|
||||||
// @param edgeDeviceUntrusted query bool false "if true, show only untrusted edge agents, if false show only trusted edge agents (relevant only for edge agents)"
|
// @param edgeDeviceUntrusted query bool false "if true, show only untrusted edge agents, if false show only trusted edge agents (relevant only for edge agents)"
|
||||||
// @param edgeCheckInPassedSeconds query number false "if bigger then zero, show only edge agents that checked-in in the last provided seconds (relevant only for edge agents)"
|
// @param edgeCheckInPassedSeconds query number false "if bigger then zero, show only edge agents that checked-in in the last provided seconds (relevant only for edge agents)"
|
||||||
// @param excludeSnapshots query bool false "if true, the snapshot data won't be retrieved"
|
// @param excludeSnapshots query bool false "if true, the snapshot data won't be retrieved"
|
||||||
|
// @param excludeSnapshotRaw query bool false "if true, the SnapshotRaw field won't be retrieved"
|
||||||
// @param name query string false "will return only environments(endpoints) with this name"
|
// @param name query string false "will return only environments(endpoints) with this name"
|
||||||
// @param edgeStackId query portainer.EdgeStackID false "will return the environements of the specified edge stack"
|
// @param edgeStackId query portainer.EdgeStackID false "will return the environements of the specified edge stack"
|
||||||
// @param edgeStackStatus query string false "only applied when edgeStackId exists. Filter the returned environments based on their deployment status in the stack (not the environment status!)" Enum("Pending", "Ok", "Error", "Acknowledged", "Remove", "RemoteUpdateSuccess", "ImagesPulled")
|
// @param edgeStackStatus query string false "only applied when edgeStackId exists. Filter the returned environments based on their deployment status in the stack (not the environment status!)" Enum("Pending", "Ok", "Error", "Acknowledged", "Remove", "RemoteUpdateSuccess", "ImagesPulled")
|
||||||
|
@ -59,6 +60,7 @@ func (handler *Handler) endpointList(w http.ResponseWriter, r *http.Request) *ht
|
||||||
limit, _ := request.RetrieveNumericQueryParameter(r, "limit", true)
|
limit, _ := request.RetrieveNumericQueryParameter(r, "limit", true)
|
||||||
sortField, _ := request.RetrieveQueryParameter(r, "sort", true)
|
sortField, _ := request.RetrieveQueryParameter(r, "sort", true)
|
||||||
sortOrder, _ := request.RetrieveQueryParameter(r, "order", true)
|
sortOrder, _ := request.RetrieveQueryParameter(r, "order", true)
|
||||||
|
excludeRaw, _ := request.RetrieveBooleanQueryParameter(r, "excludeSnapshotRaw", true)
|
||||||
|
|
||||||
endpointGroups, err := handler.DataStore.EndpointGroup().ReadAll()
|
endpointGroups, err := handler.DataStore.EndpointGroup().ReadAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -114,7 +116,7 @@ func (handler *Handler) endpointList(w http.ResponseWriter, r *http.Request) *ht
|
||||||
endpointutils.UpdateEdgeEndpointHeartbeat(&paginatedEndpoints[idx], settings)
|
endpointutils.UpdateEdgeEndpointHeartbeat(&paginatedEndpoints[idx], settings)
|
||||||
|
|
||||||
if !query.excludeSnapshots {
|
if !query.excludeSnapshots {
|
||||||
if err := handler.SnapshotService.FillSnapshotData(&paginatedEndpoints[idx]); err != nil {
|
if err := handler.SnapshotService.FillSnapshotData(&paginatedEndpoints[idx], !excludeRaw); err != nil {
|
||||||
return httperror.InternalServerError("Unable to add snapshot data", err)
|
return httperror.InternalServerError("Unable to add snapshot data", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,7 +272,7 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := handler.SnapshotService.FillSnapshotData(endpoint); err != nil {
|
if err := handler.SnapshotService.FillSnapshotData(endpoint, true); err != nil {
|
||||||
return httperror.InternalServerError("Unable to add snapshot data", err)
|
return httperror.InternalServerError("Unable to add snapshot data", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (handler *Handler) systemNodesCount(w http.ResponseWriter, r *http.Request)
|
||||||
var nodes int
|
var nodes int
|
||||||
|
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
if err := snapshot.FillSnapshotData(handler.dataStore, &endpoint); err != nil {
|
if err := snapshot.FillSnapshotData(handler.dataStore, &endpoint, false); err != nil {
|
||||||
return httperror.InternalServerError("Unable to add snapshot data", err)
|
return httperror.InternalServerError("Unable to add snapshot data", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,7 @@ func (transport *Transport) getDockerID() (string, error) {
|
||||||
if transport.snapshotService != nil {
|
if transport.snapshotService != nil {
|
||||||
endpoint := portainer.Endpoint{ID: transport.endpoint.ID}
|
endpoint := portainer.Endpoint{ID: transport.endpoint.ID}
|
||||||
|
|
||||||
if err := transport.snapshotService.FillSnapshotData(&endpoint); err == nil && len(endpoint.Snapshots) > 0 {
|
if err := transport.snapshotService.FillSnapshotData(&endpoint, true); err == nil && len(endpoint.Snapshots) > 0 {
|
||||||
if dockerID, err := snapshot.FetchDockerID(endpoint.Snapshots[0]); err == nil {
|
if dockerID, err := snapshot.FetchDockerID(endpoint.Snapshots[0]); err == nil {
|
||||||
transport.dockerID = dockerID
|
transport.dockerID = dockerID
|
||||||
return dockerID, nil
|
return dockerID, nil
|
||||||
|
|
|
@ -170,8 +170,8 @@ func (service *Service) Create(snapshot portainer.Snapshot) error {
|
||||||
return service.dataStore.Snapshot().Create(&snapshot)
|
return service.dataStore.Snapshot().Create(&snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (service *Service) FillSnapshotData(endpoint *portainer.Endpoint) error {
|
func (service *Service) FillSnapshotData(endpoint *portainer.Endpoint, includeRaw bool) error {
|
||||||
return FillSnapshotData(service.dataStore, endpoint)
|
return FillSnapshotData(service.dataStore, endpoint, includeRaw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (service *Service) snapshotKubernetesEndpoint(endpoint *portainer.Endpoint) error {
|
func (service *Service) snapshotKubernetesEndpoint(endpoint *portainer.Endpoint) error {
|
||||||
|
@ -328,8 +328,16 @@ func FetchDockerID(snapshot portainer.DockerSnapshot) (string, error) {
|
||||||
return info.Swarm.Cluster.ID, nil
|
return info.Swarm.Cluster.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FillSnapshotData(tx dataservices.DataStoreTx, endpoint *portainer.Endpoint) error {
|
func FillSnapshotData(tx dataservices.DataStoreTx, endpoint *portainer.Endpoint, includeRaw bool) error {
|
||||||
snapshot, err := tx.Snapshot().Read(endpoint.ID)
|
var snapshot *portainer.Snapshot
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if includeRaw {
|
||||||
|
snapshot, err = tx.Snapshot().Read(endpoint.ID)
|
||||||
|
} else {
|
||||||
|
snapshot, err = tx.Snapshot().ReadWithoutSnapshotRaw(endpoint.ID)
|
||||||
|
}
|
||||||
|
|
||||||
if tx.IsErrObjectNotFound(err) {
|
if tx.IsErrObjectNotFound(err) {
|
||||||
endpoint.Snapshots = []portainer.DockerSnapshot{}
|
endpoint.Snapshots = []portainer.DockerSnapshot{}
|
||||||
endpoint.Kubernetes.Snapshots = []portainer.KubernetesSnapshot{}
|
endpoint.Kubernetes.Snapshots = []portainer.KubernetesSnapshot{}
|
||||||
|
|
|
@ -1622,7 +1622,7 @@ type (
|
||||||
Start()
|
Start()
|
||||||
SetSnapshotInterval(snapshotInterval string) error
|
SetSnapshotInterval(snapshotInterval string) error
|
||||||
SnapshotEndpoint(endpoint *Endpoint) error
|
SnapshotEndpoint(endpoint *Endpoint) error
|
||||||
FillSnapshotData(endpoint *Endpoint) error
|
FillSnapshotData(endpoint *Endpoint, includeRaw bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// SwarmStackManager represents a service to manage Swarm stacks
|
// SwarmStackManager represents a service to manage Swarm stacks
|
||||||
|
|
|
@ -4,5 +4,5 @@ import { useEnvironmentId } from './useEnvironmentId';
|
||||||
|
|
||||||
export function useCurrentEnvironment(force = true) {
|
export function useCurrentEnvironment(force = true) {
|
||||||
const id = useEnvironmentId(force);
|
const id = useEnvironmentId(force);
|
||||||
return useEnvironment(id);
|
return useEnvironment(id, undefined, { excludeSnapshot: false });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { DockerSnapshot } from '@/react/docker/snapshots/types';
|
import { DockerSnapshot } from '@/react/docker/snapshots/types';
|
||||||
import { useIsPodman } from '@/react/portainer/environments/queries/useIsPodman';
|
|
||||||
import {
|
import {
|
||||||
Environment,
|
Environment,
|
||||||
PlatformType,
|
PlatformType,
|
||||||
KubernetesSnapshot,
|
KubernetesSnapshot,
|
||||||
|
ContainerEngine,
|
||||||
} from '@/react/portainer/environments/types';
|
} from '@/react/portainer/environments/types';
|
||||||
import { getPlatformType } from '@/react/portainer/environments/utils';
|
import { getPlatformType } from '@/react/portainer/environments/utils';
|
||||||
import { getDockerEnvironmentType } from '@/react/portainer/environments/utils/getDockerEnvironmentType';
|
import { getDockerEnvironmentType } from '@/react/portainer/environments/utils/getDockerEnvironmentType';
|
||||||
|
|
||||||
export function EngineVersion({ environment }: { environment: Environment }) {
|
export function EngineVersion({ environment }: { environment: Environment }) {
|
||||||
const platform = getPlatformType(environment.Type);
|
const platform = getPlatformType(environment.Type);
|
||||||
const isPodman = useIsPodman(environment.Id);
|
const isPodman = environment.ContainerEngine === ContainerEngine.Podman;
|
||||||
|
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case PlatformType.Docker:
|
case PlatformType.Docker:
|
||||||
|
|
|
@ -110,6 +110,7 @@ export function EnvironmentList({ onClickBrowse, onRefresh }: Props) {
|
||||||
updateInformation: isBE,
|
updateInformation: isBE,
|
||||||
edgeAsync: getEdgeAsyncValue(connectionTypes),
|
edgeAsync: getEdgeAsyncValue(connectionTypes),
|
||||||
platformTypes,
|
platformTypes,
|
||||||
|
excludeSnapshotRaw: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryWithSort = {
|
const queryWithSort = {
|
||||||
|
|
|
@ -42,6 +42,7 @@ export interface BaseEnvironmentsQueryParams {
|
||||||
edgeAsync?: boolean;
|
edgeAsync?: boolean;
|
||||||
edgeDeviceUntrusted?: boolean;
|
edgeDeviceUntrusted?: boolean;
|
||||||
excludeSnapshots?: boolean;
|
excludeSnapshots?: boolean;
|
||||||
|
excludeSnapshotRaw?: boolean;
|
||||||
provisioned?: boolean;
|
provisioned?: boolean;
|
||||||
name?: string;
|
name?: string;
|
||||||
agentVersions?: string[];
|
agentVersions?: string[];
|
||||||
|
@ -119,9 +120,15 @@ export async function getAgentVersions() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getEndpoint(id: EnvironmentId) {
|
export async function getEndpoint(
|
||||||
|
id: EnvironmentId,
|
||||||
|
excludeSnapshot = true,
|
||||||
|
excludeSnapshotRaw = true
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const { data: endpoint } = await axios.get<Environment>(buildUrl(id));
|
const { data: endpoint } = await axios.get<Environment>(buildUrl(id), {
|
||||||
|
params: { excludeSnapshot, excludeSnapshotRaw },
|
||||||
|
});
|
||||||
return endpoint;
|
return endpoint;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw parseAxiosError(e as Error);
|
throw parseAxiosError(e as Error);
|
||||||
|
|
|
@ -10,11 +10,20 @@ import { environmentQueryKeys } from './query-keys';
|
||||||
export function useEnvironment<T = Environment>(
|
export function useEnvironment<T = Environment>(
|
||||||
environmentId?: EnvironmentId,
|
environmentId?: EnvironmentId,
|
||||||
select?: (environment: Environment) => T,
|
select?: (environment: Environment) => T,
|
||||||
options?: { autoRefreshRate?: number }
|
options?: {
|
||||||
|
autoRefreshRate?: number;
|
||||||
|
excludeSnapshot?: boolean;
|
||||||
|
excludeSnapshotRaw?: boolean;
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
return useQuery(
|
return useQuery(
|
||||||
environmentQueryKeys.item(environmentId!),
|
environmentQueryKeys.item(environmentId!),
|
||||||
() => getEndpoint(environmentId!),
|
() =>
|
||||||
|
getEndpoint(
|
||||||
|
environmentId!,
|
||||||
|
options?.excludeSnapshot ?? undefined,
|
||||||
|
options?.excludeSnapshotRaw ?? undefined
|
||||||
|
),
|
||||||
{
|
{
|
||||||
select,
|
select,
|
||||||
...withError('Failed loading environment'),
|
...withError('Failed loading environment'),
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { EdgeGroupId, EdgeTypes } from '@/react/portainer/environments/types';
|
||||||
|
|
||||||
export function useEnvironments(edgeGroupIds: Array<EdgeGroupId>) {
|
export function useEnvironments(edgeGroupIds: Array<EdgeGroupId>) {
|
||||||
const environmentsQuery = useEnvironmentList(
|
const environmentsQuery = useEnvironmentList(
|
||||||
{ edgeGroupIds, types: EdgeTypes, pageLimit: 0 },
|
{ edgeGroupIds, types: EdgeTypes, pageLimit: 0, excludeSnapshots: true },
|
||||||
{
|
{
|
||||||
enabled: edgeGroupIds.length > 0,
|
enabled: edgeGroupIds.length > 0,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue