From fa4711946d98b0df0525605da553363fe4d191d3 Mon Sep 17 00:00:00 2001 From: andres-portainer <91705312+andres-portainer@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:18:17 -0300 Subject: [PATCH] fix(snapshots): fix background snapshots on environment creation EE-7273 (#12022) --- api/chisel/service_test.go | 8 +++++--- api/chisel/tunnel.go | 14 ++++++++++++-- .../endpointedge/endpointedge_status_inspect.go | 14 ++++++++++---- api/internal/snapshot/snapshot.go | 6 ++++-- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/api/chisel/service_test.go b/api/chisel/service_test.go index dd613d542..2e612a93b 100644 --- a/api/chisel/service_test.go +++ b/api/chisel/service_test.go @@ -14,8 +14,9 @@ import ( func TestPingAgentPanic(t *testing.T) { endpoint := &portainer.Endpoint{ - ID: 1, - Type: portainer.EdgeAgentOnDockerEnvironment, + ID: 1, + EdgeID: "test-edge-id", + Type: portainer.EdgeAgentOnDockerEnvironment, } _, store := datastore.MustNewTestStore(t, true, true) @@ -38,7 +39,8 @@ func TestPingAgentPanic(t *testing.T) { require.NoError(t, http.Serve(ln, mux)) }() - s.Open(endpoint) + err = s.Open(endpoint) + require.NoError(t, err) s.activeTunnels[endpoint.ID].Port = ln.Addr().(*net.TCPAddr).Port require.Error(t, s.pingAgent(endpoint.ID)) diff --git a/api/chisel/tunnel.go b/api/chisel/tunnel.go index 826e090cd..c8374a3e3 100644 --- a/api/chisel/tunnel.go +++ b/api/chisel/tunnel.go @@ -24,14 +24,24 @@ const ( maxAvailablePort = 65535 ) +var ( + ErrNonEdgeEnv = errors.New("cannot open a tunnel for non-edge environments") + ErrAsyncEnv = errors.New("cannot open a tunnel for async edge environments") + ErrInvalidEnv = errors.New("cannot open a tunnel for an invalid environment") +) + // Open will mark the tunnel as REQUIRED so the agent opens it func (s *Service) Open(endpoint *portainer.Endpoint) error { if !endpointutils.IsEdgeEndpoint(endpoint) { - return errors.New("cannot open a tunnel for non-edge environments") + return ErrNonEdgeEnv } if endpoint.Edge.AsyncMode { - return errors.New("cannot open a tunnel for async edge environments") + return ErrAsyncEnv + } + + if endpoint.ID == 0 || endpoint.EdgeID == "" { + return ErrInvalidEnv } s.mu.Lock() diff --git a/api/http/handler/endpointedge/endpointedge_status_inspect.go b/api/http/handler/endpointedge/endpointedge_status_inspect.go index 1978bdd15..87c02c8ad 100644 --- a/api/http/handler/endpointedge/endpointedge_status_inspect.go +++ b/api/http/handler/endpointedge/endpointedge_status_inspect.go @@ -20,6 +20,8 @@ import ( httperror "github.com/portainer/portainer/pkg/libhttp/error" "github.com/portainer/portainer/pkg/libhttp/request" "github.com/portainer/portainer/pkg/libhttp/response" + + "github.com/rs/zerolog/log" ) type stackStatusResponse struct { @@ -93,6 +95,8 @@ func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http return httperror.Forbidden("Permission denied to access environment", errors.New("the device has not been trusted yet")) } + firstConn := endpoint.LastCheckInDate == 0 + err = handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint) if err != nil { return httperror.Forbidden("Permission denied to access environment", err) @@ -107,7 +111,7 @@ func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http var statusResponse *endpointEdgeStatusInspectResponse err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { - statusResponse, err = handler.inspectStatus(tx, r, portainer.EndpointID(endpointID)) + statusResponse, err = handler.inspectStatus(tx, r, portainer.EndpointID(endpointID), firstConn) return err }) if err != nil { @@ -122,7 +126,7 @@ func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http return cacheResponse(w, endpoint.ID, *statusResponse) } -func (handler *Handler) inspectStatus(tx dataservices.DataStoreTx, r *http.Request, endpointID portainer.EndpointID) (*endpointEdgeStatusInspectResponse, error) { +func (handler *Handler) inspectStatus(tx dataservices.DataStoreTx, r *http.Request, endpointID portainer.EndpointID, firstConn bool) (*endpointEdgeStatusInspectResponse, error) { endpoint, err := tx.Endpoint().Endpoint(endpointID) if err != nil { return nil, err @@ -134,8 +138,10 @@ func (handler *Handler) inspectStatus(tx dataservices.DataStoreTx, r *http.Reque } // Take an initial snapshot - if endpoint.LastCheckInDate == 0 { - handler.ReverseTunnelService.Open(endpoint) + if firstConn { + if err := handler.ReverseTunnelService.Open(endpoint); err != nil { + log.Error().Err(err).Msg("could not open the tunnel") + } } agentPlatform, agentPlatformErr := parseAgentPlatform(r) diff --git a/api/internal/snapshot/snapshot.go b/api/internal/snapshot/snapshot.go index e38d5366a..f0cf504d0 100644 --- a/api/internal/snapshot/snapshot.go +++ b/api/internal/snapshot/snapshot.go @@ -64,14 +64,16 @@ func NewBackgroundSnapshotter(dataStore dataservices.DataStore, tunnelService po } for _, e := range endpoints { - if !endpointutils.IsEdgeEndpoint(&e) { + if !endpointutils.IsEdgeEndpoint(&e) || e.Edge.AsyncMode { continue } s, err := tx.Snapshot().Read(e.ID) if dataservices.IsErrObjectNotFound(err) || (err == nil && s.Docker == nil && s.Kubernetes == nil) { - tunnelService.Open(&e) + if err := tunnelService.Open(&e); err != nil { + log.Error().Err(err).Msg("could not open the tunnel") + } } }