package control import ( "bytes" "context" "fmt" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap" control "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // HealthCheck returns health status of the local IR node. // // If request is not signed with a key from white list, permission error returns. func (s *Server) HealthCheck(_ context.Context, req *control.HealthCheckRequest) (*control.HealthCheckResponse, error) { if err := s.isValidRequest(req); err != nil { return nil, status.Error(codes.PermissionDenied, err.Error()) } resp := new(control.HealthCheckResponse) body := new(control.HealthCheckResponse_Body) resp.SetBody(body) body.SetHealthStatus(s.prm.healthChecker.HealthStatus()) if err := SignMessage(&s.prm.key.PrivateKey, resp); err != nil { return nil, status.Error(codes.Internal, err.Error()) } return resp, nil } // TickEpoch forces a new epoch. // // If request is not signed with a key from white list, permission error returns. func (s *Server) TickEpoch(ctx context.Context, req *control.TickEpochRequest) (*control.TickEpochResponse, error) { if err := s.isValidRequest(req); err != nil { return nil, status.Error(codes.PermissionDenied, err.Error()) } resp := new(control.TickEpochResponse) resp.SetBody(new(control.TickEpochResponse_Body)) epoch, err := s.netmapClient.Epoch() if err != nil { return nil, fmt.Errorf("getting current epoch: %w", err) } vub, err := s.netmapClient.NewEpochControl(ctx, epoch+1, req.GetBody().GetVub()) if err != nil { return nil, fmt.Errorf("forcing new epoch: %w", err) } resp.Body.Vub = vub if err := SignMessage(&s.prm.key.PrivateKey, resp); err != nil { return nil, status.Error(codes.Internal, err.Error()) } return resp, nil } // RemoveNode forces a node removal. // // If request is not signed with a key from white list, permission error returns. func (s *Server) RemoveNode(ctx context.Context, req *control.RemoveNodeRequest) (*control.RemoveNodeResponse, error) { if err := s.isValidRequest(req); err != nil { return nil, status.Error(codes.PermissionDenied, err.Error()) } resp := new(control.RemoveNodeResponse) resp.SetBody(new(control.RemoveNodeResponse_Body)) nm, err := s.netmapClient.NetMap() if err != nil { return nil, fmt.Errorf("getting netmap: %w", err) } var nodeInfo netmap.NodeInfo for _, info := range nm.Nodes() { if bytes.Equal(info.PublicKey(), req.GetBody().GetKey()) { nodeInfo = info break } } if len(nodeInfo.PublicKey()) == 0 { return nil, status.Error(codes.NotFound, "no such node") } if nodeInfo.Status().IsOffline() { return nil, status.Error(codes.FailedPrecondition, "node is already offline") } vub, err := s.netmapClient.ForceRemovePeer(ctx, nodeInfo, req.GetBody().GetVub()) if err != nil { return nil, fmt.Errorf("forcing node removal: %w", err) } resp.Body.Vub = vub if err := SignMessage(&s.prm.key.PrivateKey, resp); err != nil { return nil, status.Error(codes.Internal, err.Error()) } return resp, nil } // RemoveContainer forces a container removal. func (s *Server) RemoveContainer(ctx context.Context, req *control.RemoveContainerRequest) (*control.RemoveContainerResponse, error) { if err := s.isValidRequest(req); err != nil { return nil, status.Error(codes.PermissionDenied, err.Error()) } if len(req.GetBody().GetContainerId()) > 0 && len(req.GetBody().GetOwner()) > 0 { return nil, status.Error(codes.InvalidArgument, "specify the owner and container at the same time is not allowed") } var vub uint32 if len(req.GetBody().GetContainerId()) > 0 { var containerID cid.ID if err := containerID.Decode(req.GetBody().GetContainerId()); err != nil { return nil, status.Error(codes.InvalidArgument, "failed to parse container ID: "+err.Error()) } var err error vub, err = s.removeContainer(ctx, containerID, req.GetBody().GetVub()) if err != nil { return nil, err } } else { var ownerID refs.OwnerID if err := ownerID.Unmarshal(req.GetBody().GetOwner()); err != nil { return nil, status.Error(codes.InvalidArgument, "failed to parse ownerID: %s"+err.Error()) } var owner user.ID if err := owner.ReadFromV2(ownerID); err != nil { return nil, status.Error(codes.InvalidArgument, "failed to read owner: "+err.Error()) } cids, err := s.containerClient.ContainersOf(&owner) if err != nil { return nil, fmt.Errorf("failed to get owner's containers: %w", err) } for _, containerID := range cids { vub, err = s.removeContainer(ctx, containerID, req.GetBody().GetVub()) if err != nil { return nil, err } } } resp := &control.RemoveContainerResponse{ Body: &control.RemoveContainerResponse_Body{ Vub: vub, }, } if err := SignMessage(&s.prm.key.PrivateKey, resp); err != nil { return nil, status.Error(codes.Internal, err.Error()) } return resp, nil } func (s *Server) removeContainer(ctx context.Context, containerID cid.ID, vub uint32) (uint32, error) { var prm container.DeletePrm prm.SetCID(containerID[:]) prm.SetControlTX(true) prm.SetVUB(vub) vub, err := s.containerClient.Delete(ctx, prm) if err != nil { return 0, fmt.Errorf("forcing container removal: %w", err) } return vub, nil }