From a02f9f1f077d336b28a73a1a0813412c722c5857 Mon Sep 17 00:00:00 2001 From: Prabhat Khera <91852476+prabhat-org@users.noreply.github.com> Date: Tue, 5 Sep 2023 11:03:43 +1200 Subject: [PATCH] fix(kubernetes): run group permission when endpoint is up [EE-5427] (#10121) * update group access when env is down * fix tests --- .../test_data/output_24_to_latest.json | 1 + .../endpointgroups/endpointgroup_update.go | 7 ++- .../handler/endpoints/endpoint_inspect.go | 4 ++ api/http/utils/pendingActions.go | 61 +++++++++++++++++++ api/internal/authorization/authorizations.go | 4 +- api/internal/snapshot/snapshot.go | 7 +++ api/portainer.go | 9 +++ 7 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 api/http/utils/pendingActions.go diff --git a/api/datastore/test_data/output_24_to_latest.json b/api/datastore/test_data/output_24_to_latest.json index 8ada67237..813c96051 100644 --- a/api/datastore/test_data/output_24_to_latest.json +++ b/api/datastore/test_data/output_24_to_latest.json @@ -73,6 +73,7 @@ }, "LastCheckInDate": 0, "Name": "local", + "PendingActions": null, "PostInitMigrations": { "MigrateGPUs": true, "MigrateIngresses": true diff --git a/api/http/handler/endpointgroups/endpointgroup_update.go b/api/http/handler/endpointgroups/endpointgroup_update.go index a422b398a..16f7bab21 100644 --- a/api/http/handler/endpointgroups/endpointgroup_update.go +++ b/api/http/handler/endpointgroups/endpointgroup_update.go @@ -7,11 +7,13 @@ import ( portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/dataservices" + "github.com/portainer/portainer/api/http/utils" "github.com/portainer/portainer/api/internal/tag" "github.com/portainer/portainer/pkg/featureflags" 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 endpointGroupUpdatePayload struct { @@ -187,7 +189,10 @@ func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpoin if endpoint.Type == portainer.KubernetesLocalEnvironment || endpoint.Type == portainer.AgentOnKubernetesEnvironment || endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment { err = handler.AuthorizationService.CleanNAPWithOverridePolicies(tx, &endpoint, endpointGroup) if err != nil { - return nil, httperror.InternalServerError("Unable to update user authorizations", err) + // Update flag with endpoint and continue + endpoint.PendingActions = utils.GetUpdatedEndpointPendingActions(&endpoint, "CleanNAPWithOverridePolicies", endpointGroup.ID) + err = tx.Endpoint().UpdateEndpoint(endpoint.ID, &endpoint) + log.Warn().Err(err).Msgf("Unable to update user authorizations for endpoint (%d) and endpopint group (%d)", endpoint.ID, endpointGroup.ID) } } } diff --git a/api/http/handler/endpoints/endpoint_inspect.go b/api/http/handler/endpoints/endpoint_inspect.go index 919a1788e..23894dc44 100644 --- a/api/http/handler/endpoints/endpoint_inspect.go +++ b/api/http/handler/endpoints/endpoint_inspect.go @@ -4,6 +4,7 @@ import ( "net/http" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/http/utils" "github.com/portainer/portainer/api/internal/endpointutils" httperror "github.com/portainer/portainer/pkg/libhttp/error" "github.com/portainer/portainer/pkg/libhttp/request" @@ -78,6 +79,9 @@ func (handler *Handler) endpointInspect(w http.ResponseWriter, r *http.Request) } } + // Run the pending actions + utils.RunPendingActions(endpoint, handler.DataStore, handler.AuthorizationService) + return response.JSON(w, endpoint) } diff --git a/api/http/utils/pendingActions.go b/api/http/utils/pendingActions.go new file mode 100644 index 000000000..9d2285580 --- /dev/null +++ b/api/http/utils/pendingActions.go @@ -0,0 +1,61 @@ +package utils + +import ( + portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/dataservices" + "github.com/portainer/portainer/api/internal/authorization" + "github.com/rs/zerolog/log" +) + +func EndpointPendingActions(endpoint *portainer.Endpoint) *portainer.EndpointPendingActions { + return endpoint.PendingActions +} + +func GetUpdatedEndpointPendingActions(endpoint *portainer.Endpoint, action string, value interface{}) *portainer.EndpointPendingActions { + if endpoint.PendingActions == nil { + endpoint.PendingActions = &portainer.EndpointPendingActions{} + } + + switch action { + case "CleanNAPWithOverridePolicies": + endpoint.PendingActions.CleanNAPWithOverridePolicies.EndpointGroups = append(endpoint.PendingActions.CleanNAPWithOverridePolicies.EndpointGroups, value.(portainer.EndpointGroupID)) + } + + return endpoint.PendingActions +} + +func RunPendingActions(endpoint *portainer.Endpoint, dataStore dataservices.DataStoreTx, authorizationService *authorization.Service) error { + + if endpoint.PendingActions == nil { + return nil + } + + log.Info().Msgf("Running pending actions for endpoint %d", endpoint.ID) + + if endpoint.PendingActions.CleanNAPWithOverridePolicies.EndpointGroups != nil { + log.Info().Int("endpoint_id", int(endpoint.ID)).Msgf("Cleaning NAP with override policies for endpoint groups %v", endpoint.PendingActions.CleanNAPWithOverridePolicies.EndpointGroups) + failedEndpointGroupIDs := make([]portainer.EndpointGroupID, 0) + for _, endpointGroupID := range endpoint.PendingActions.CleanNAPWithOverridePolicies.EndpointGroups { + endpointGroup, err := dataStore.EndpointGroup().Read(portainer.EndpointGroupID(endpointGroupID)) + if err != nil { + log.Error().Err(err).Msgf("Error reading endpoint group to clean NAP with override policies for endpoint %d and endpoint group %d", endpoint.ID, endpointGroup.ID) + failedEndpointGroupIDs = append(failedEndpointGroupIDs, endpointGroupID) + continue + } + err = authorizationService.CleanNAPWithOverridePolicies(dataStore, endpoint, endpointGroup) + if err != nil { + failedEndpointGroupIDs = append(failedEndpointGroupIDs, endpointGroupID) + log.Error().Err(err).Msgf("Error cleaning NAP with override policies for endpoint %d and endpoint group %d", endpoint.ID, endpointGroup.ID) + } + } + + endpoint.PendingActions.CleanNAPWithOverridePolicies.EndpointGroups = failedEndpointGroupIDs + err := dataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint) + if err != nil { + log.Error().Err(err).Msgf("While running pending actions, error updating endpoint %d", endpoint.ID) + return err + } + } + + return nil +} diff --git a/api/internal/authorization/authorizations.go b/api/internal/authorization/authorizations.go index 5934b16d2..2514dfedf 100644 --- a/api/internal/authorization/authorizations.go +++ b/api/internal/authorization/authorizations.go @@ -9,12 +9,12 @@ import ( // Service represents a service used to // update authorizations associated to a user or team. type Service struct { - dataStore dataservices.DataStore + dataStore dataservices.DataStoreTx K8sClientFactory *cli.ClientFactory } // NewService returns a point to a new Service instance. -func NewService(dataStore dataservices.DataStore) *Service { +func NewService(dataStore dataservices.DataStoreTx) *Service { return &Service{ dataStore: dataStore, } diff --git a/api/internal/snapshot/snapshot.go b/api/internal/snapshot/snapshot.go index 8d5418889..dc429b144 100644 --- a/api/internal/snapshot/snapshot.go +++ b/api/internal/snapshot/snapshot.go @@ -10,6 +10,8 @@ import ( "github.com/portainer/portainer/api/agent" "github.com/portainer/portainer/api/crypto" "github.com/portainer/portainer/api/dataservices" + "github.com/portainer/portainer/api/http/utils" + "github.com/portainer/portainer/api/internal/authorization" "github.com/portainer/portainer/api/internal/endpointutils" "github.com/portainer/portainer/pkg/featureflags" @@ -304,6 +306,11 @@ func updateEndpointStatus(tx dataservices.DataStoreTx, endpoint *portainer.Endpo Str("URL", endpoint.URL).Err(err). Msg("background schedule error (environment snapshot), unable to update environment") } + + // Run the pending actions + if latestEndpointReference.Status == portainer.EndpointStatusUp { + utils.RunPendingActions(latestEndpointReference, tx, authorization.NewService(tx)) + } } // FetchDockerID fetches info.Swarm.Cluster.ID if environment(endpoint) is swarm and info.ID otherwise diff --git a/api/portainer.go b/api/portainer.go index 721c44b83..eb3640758 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -373,6 +373,12 @@ type ( //EdgeStackStatusType represents an edge stack status type EdgeStackStatusType int + EndpointPendingActions struct { + CleanNAPWithOverridePolicies struct { + EndpointGroups []EndpointGroupID `json:"EndpointGroups"` + } `json:"CleanNAPWithOverridePolicies"` + } + // Environment(Endpoint) represents a Docker environment(endpoint) with all the info required // to connect to it Endpoint struct { @@ -428,6 +434,9 @@ type ( // Whether we need to run any "post init migrations". PostInitMigrations EndpointPostInitMigrations `json:"PostInitMigrations"` + // Whether we need to run any action when an endpoint is back online. + PendingActions *EndpointPendingActions `json:"PendingActions"` + Edge EnvironmentEdgeSettings Agent struct {