github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/client/userd/daemon/grpc.go (about)

     1  package daemon
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"runtime"
    11  	"strings"
    12  	"sync/atomic"
    13  	"time"
    14  
    15  	"go.opentelemetry.io/otel"
    16  	"go.opentelemetry.io/otel/trace"
    17  	"google.golang.org/grpc/codes"
    18  	"google.golang.org/grpc/status"
    19  	"google.golang.org/protobuf/types/known/emptypb"
    20  	empty "google.golang.org/protobuf/types/known/emptypb"
    21  
    22  	"github.com/datawire/dlib/derror"
    23  	"github.com/datawire/dlib/dexec"
    24  	"github.com/datawire/dlib/dgroup"
    25  	"github.com/datawire/dlib/dlog"
    26  	"github.com/telepresenceio/telepresence/rpc/v2/common"
    27  	rpc "github.com/telepresenceio/telepresence/rpc/v2/connector"
    28  	"github.com/telepresenceio/telepresence/rpc/v2/daemon"
    29  	"github.com/telepresenceio/telepresence/rpc/v2/manager"
    30  	"github.com/telepresenceio/telepresence/v2/pkg/client"
    31  	"github.com/telepresenceio/telepresence/v2/pkg/client/logging"
    32  	"github.com/telepresenceio/telepresence/v2/pkg/client/scout"
    33  	"github.com/telepresenceio/telepresence/v2/pkg/client/socket"
    34  	"github.com/telepresenceio/telepresence/v2/pkg/client/userd"
    35  	"github.com/telepresenceio/telepresence/v2/pkg/errcat"
    36  	"github.com/telepresenceio/telepresence/v2/pkg/proc"
    37  	"github.com/telepresenceio/telepresence/v2/pkg/tracing"
    38  )
    39  
    40  func callRecovery(c context.Context, r any, err error) error {
    41  	if perr := derror.PanicToError(r); perr != nil {
    42  		dlog.Errorf(c, "%+v", perr)
    43  		err = perr
    44  	}
    45  	return err
    46  }
    47  
    48  type reqNumberKey struct{}
    49  
    50  func getReqNumber(ctx context.Context) int64 {
    51  	num := ctx.Value(reqNumberKey{})
    52  	if num == nil {
    53  		return 0
    54  	}
    55  	return num.(int64)
    56  }
    57  
    58  func withReqNumber(ctx context.Context, num int64) context.Context {
    59  	return context.WithValue(ctx, reqNumberKey{}, num)
    60  }
    61  
    62  func (s *service) callCtx(ctx context.Context, name string) context.Context {
    63  	num := atomic.AddInt64(&s.ucn, 1)
    64  	ctx = withReqNumber(ctx, num)
    65  	return dgroup.WithGoroutineName(ctx, fmt.Sprintf("/%s-%d", name, num))
    66  }
    67  
    68  func (s *service) logCall(c context.Context, callName string, f func(context.Context)) {
    69  	c = s.callCtx(c, callName)
    70  	dlog.Debug(c, "called")
    71  	defer dlog.Debug(c, "returned")
    72  	f(c)
    73  }
    74  
    75  func (s *service) FuseFTPError() error {
    76  	return s.fuseFTPError
    77  }
    78  
    79  func (s *service) WithSession(c context.Context, callName string, f func(context.Context, userd.Session) error) (err error) {
    80  	s.logCall(c, callName, func(_ context.Context) {
    81  		if atomic.LoadInt32(&s.sessionQuitting) != 0 {
    82  			err = status.Error(codes.Canceled, "session cancelled")
    83  			return
    84  		}
    85  		s.sessionLock.RLock()
    86  		defer s.sessionLock.RUnlock()
    87  		if s.session == nil {
    88  			err = status.Error(codes.Unavailable, "no active session")
    89  			return
    90  		}
    91  		if s.sessionContext.Err() != nil {
    92  			// Session context has been cancelled
    93  			err = status.Error(codes.Canceled, "session cancelled")
    94  			return
    95  		}
    96  		defer func() { err = callRecovery(c, recover(), err) }()
    97  		num := getReqNumber(c)
    98  		ctx := dgroup.WithGoroutineName(s.sessionContext, fmt.Sprintf("/%s-%d", callName, num))
    99  		ctx, span := otel.Tracer("").Start(ctx, callName)
   100  		defer span.End()
   101  		err = f(ctx, s.session)
   102  	})
   103  	return
   104  }
   105  
   106  func (s *service) Version(_ context.Context, _ *empty.Empty) (*common.VersionInfo, error) {
   107  	executable, err := client.Executable()
   108  	if err != nil {
   109  		return &common.VersionInfo{}, err
   110  	}
   111  	return &common.VersionInfo{
   112  		ApiVersion: client.APIVersion,
   113  		Version:    client.Version(),
   114  		Executable: executable,
   115  		Name:       client.DisplayName,
   116  	}, nil
   117  }
   118  
   119  func (s *service) Connect(ctx context.Context, cr *rpc.ConnectRequest) (result *rpc.ConnectInfo, err error) {
   120  	s.logCall(ctx, "Connect", func(c context.Context) {
   121  		select {
   122  		case <-ctx.Done():
   123  			err = status.Error(codes.Unavailable, ctx.Err().Error())
   124  			return
   125  		case s.connectRequest <- cr:
   126  		}
   127  
   128  		select {
   129  		case <-ctx.Done():
   130  			err = status.Error(codes.Unavailable, ctx.Err().Error())
   131  		case result = <-s.connectResponse:
   132  		}
   133  	})
   134  	return result, err
   135  }
   136  
   137  func (s *service) Disconnect(ctx context.Context, ex *empty.Empty) (*empty.Empty, error) {
   138  	s.logCall(ctx, "Disconnect", func(ctx context.Context) {
   139  		s.cancelSession()
   140  		_ = s.withRootDaemon(ctx, func(ctx context.Context, rd daemon.DaemonClient) error {
   141  			_, err := rd.Disconnect(ctx, ex)
   142  			return err
   143  		})
   144  	})
   145  	return &empty.Empty{}, nil
   146  }
   147  
   148  func (s *service) Status(ctx context.Context, ex *empty.Empty) (result *rpc.ConnectInfo, err error) {
   149  	s.logCall(ctx, "Status", func(c context.Context) {
   150  		s.sessionLock.RLock()
   151  		defer s.sessionLock.RUnlock()
   152  		if s.session == nil {
   153  			result = &rpc.ConnectInfo{Error: rpc.ConnectInfo_DISCONNECTED}
   154  			_ = s.withRootDaemon(c, func(c context.Context, dc daemon.DaemonClient) error {
   155  				result.DaemonStatus, err = dc.Status(c, ex)
   156  				return nil
   157  			})
   158  		} else {
   159  			result = s.session.Status(s.sessionContext)
   160  		}
   161  	})
   162  	return
   163  }
   164  
   165  // isMultiPortIntercept checks if the intercept is one of several active intercepts on the same workload.
   166  // If it is, then the first returned value will be true and the second will indicate if those intercepts are
   167  // on different services. Otherwise, this function returns false, false.
   168  func (s *service) isMultiPortIntercept(spec *manager.InterceptSpec) (multiPort, multiService bool) {
   169  	wis := s.session.InterceptsForWorkload(spec.Agent, spec.Namespace)
   170  
   171  	// The InterceptsForWorkload will not include failing or removed intercepts so the
   172  	// subject must be added unless it's already there.
   173  	active := false
   174  	for _, is := range wis {
   175  		if is.Name == spec.Name {
   176  			active = true
   177  			break
   178  		}
   179  	}
   180  	if !active {
   181  		wis = append(wis, spec)
   182  	}
   183  	if len(wis) < 2 {
   184  		return false, false
   185  	}
   186  	var suid string
   187  	for _, is := range wis {
   188  		if suid == "" {
   189  			suid = is.ServiceUid
   190  		} else if suid != is.ServiceUid {
   191  			return true, true
   192  		}
   193  	}
   194  	return true, false
   195  }
   196  
   197  func (s *service) scoutInterceptEntries(ctx context.Context, spec *manager.InterceptSpec, result *rpc.InterceptResult) ([]scout.Entry, bool) {
   198  	// The scout belongs to the session and can only contain session specific meta-data,
   199  	// so we don't want to use scout.SetMetadatum() here.
   200  	entries := make([]scout.Entry, 0, 7)
   201  	if spec != nil {
   202  		entries = append(entries,
   203  			scout.Entry{Key: "service_name", Value: spec.ServiceName},
   204  			scout.Entry{Key: "service_namespace", Value: spec.Namespace},
   205  			scout.Entry{Key: "intercept_mechanism", Value: spec.Mechanism},
   206  			scout.Entry{Key: "intercept_mechanism_numargs", Value: len(spec.Mechanism)},
   207  		)
   208  		multiPort, multiService := s.isMultiPortIntercept(spec)
   209  		if multiPort {
   210  			entries = append(entries, scout.Entry{Key: "multi_port", Value: multiPort})
   211  			if multiService {
   212  				entries = append(entries, scout.Entry{Key: "multi_service", Value: multiService})
   213  			}
   214  		}
   215  	}
   216  	if result != nil {
   217  		entries = append(entries, scout.Entry{Key: "workload_kind", Value: result.WorkloadKind})
   218  		if result.Error != common.InterceptError_UNSPECIFIED {
   219  			es := result.Error.String()
   220  			if result.ErrorText != "" {
   221  				es = fmt.Sprintf("%s: %s", es, result.ErrorText)
   222  			}
   223  			dlog.Debugf(ctx, "reporting error: %s", es)
   224  			entries = append(entries, scout.Entry{Key: "error", Value: es})
   225  			return entries, false
   226  		}
   227  	}
   228  	return entries, true
   229  }
   230  
   231  func (s *service) CanIntercept(c context.Context, ir *rpc.CreateInterceptRequest) (result *rpc.InterceptResult, err error) {
   232  	var entries []scout.Entry
   233  	ok := false
   234  	defer func() {
   235  		var action string
   236  		if ok {
   237  			action = "connector_can_intercept_success"
   238  		} else {
   239  			action = "connector_can_intercept_fail"
   240  		}
   241  		scout.Report(c, action, entries...)
   242  	}()
   243  	err = s.WithSession(c, "CanIntercept", func(c context.Context, session userd.Session) error {
   244  		span := trace.SpanFromContext(c)
   245  		tracing.RecordInterceptSpec(span, ir.Spec)
   246  		_, result = session.CanIntercept(c, ir)
   247  		if result == nil {
   248  			result = &rpc.InterceptResult{Error: common.InterceptError_UNSPECIFIED}
   249  		}
   250  		entries, ok = s.scoutInterceptEntries(c, ir.GetSpec(), result)
   251  		return nil
   252  	})
   253  	return
   254  }
   255  
   256  func (s *service) CreateIntercept(c context.Context, ir *rpc.CreateInterceptRequest) (result *rpc.InterceptResult, err error) {
   257  	var entries []scout.Entry
   258  	ok := false
   259  	defer func() {
   260  		var action string
   261  		if ok {
   262  			action = "connector_create_intercept_success"
   263  		} else {
   264  			action = "connector_create_intercept_fail"
   265  		}
   266  		scout.Report(c, action, entries...)
   267  	}()
   268  	err = s.WithSession(c, "CreateIntercept", func(c context.Context, session userd.Session) error {
   269  		span := trace.SpanFromContext(c)
   270  		tracing.RecordInterceptSpec(span, ir.Spec)
   271  		result = session.AddIntercept(c, ir)
   272  		if result != nil && result.InterceptInfo != nil {
   273  			tracing.RecordInterceptInfo(span, result.InterceptInfo)
   274  		}
   275  		entries, ok = s.scoutInterceptEntries(c, ir.GetSpec(), result)
   276  		return nil
   277  	})
   278  	return
   279  }
   280  
   281  func (s *service) RemoveIntercept(c context.Context, rr *manager.RemoveInterceptRequest2) (result *rpc.InterceptResult, err error) {
   282  	var spec *manager.InterceptSpec
   283  	var entries []scout.Entry
   284  	ok := false
   285  	defer func() {
   286  		var action string
   287  		if ok {
   288  			action = "connector_remove_intercept_success"
   289  		} else {
   290  			action = "connector_remove_intercept_fail"
   291  		}
   292  		scout.Report(c, action, entries...)
   293  	}()
   294  	err = s.WithSession(c, "RemoveIntercept", func(c context.Context, session userd.Session) error {
   295  		result = &rpc.InterceptResult{}
   296  		spec = session.GetInterceptSpec(rr.Name)
   297  		if spec != nil {
   298  			result.ServiceUid = spec.ServiceUid
   299  			result.WorkloadKind = spec.WorkloadKind
   300  		}
   301  		if err := session.RemoveIntercept(c, rr.Name); err != nil {
   302  			if status.Code(err) == codes.NotFound {
   303  				result.Error = common.InterceptError_NOT_FOUND
   304  				result.ErrorText = rr.Name
   305  				result.ErrorCategory = int32(errcat.User)
   306  			} else {
   307  				result.Error = common.InterceptError_TRAFFIC_MANAGER_ERROR
   308  				result.ErrorText = err.Error()
   309  				result.ErrorCategory = int32(errcat.Unknown)
   310  			}
   311  		}
   312  		entries, ok = s.scoutInterceptEntries(c, spec, result)
   313  		return nil
   314  	})
   315  	return result, err
   316  }
   317  
   318  func (s *service) UpdateIntercept(c context.Context, rr *manager.UpdateInterceptRequest) (result *manager.InterceptInfo, err error) {
   319  	err = s.WithSession(c, "UpdateIntercept", func(c context.Context, session userd.Session) error {
   320  		result, err = session.ManagerClient().UpdateIntercept(c, rr)
   321  		return err
   322  	})
   323  	return
   324  }
   325  
   326  func (s *service) AddInterceptor(ctx context.Context, interceptor *rpc.Interceptor) (*empty.Empty, error) {
   327  	return &empty.Empty{}, s.WithSession(ctx, "AddInterceptor", func(_ context.Context, session userd.Session) error {
   328  		return session.AddInterceptor(interceptor.InterceptId, interceptor)
   329  	})
   330  }
   331  
   332  func (s *service) RemoveInterceptor(ctx context.Context, interceptor *rpc.Interceptor) (*empty.Empty, error) {
   333  	return &empty.Empty{}, s.WithSession(ctx, "RemoveInterceptor", func(_ context.Context, session userd.Session) error {
   334  		return session.RemoveInterceptor(interceptor.InterceptId)
   335  	})
   336  }
   337  
   338  func (s *service) List(c context.Context, lr *rpc.ListRequest) (result *rpc.WorkloadInfoSnapshot, err error) {
   339  	err = s.WithSession(c, "List", func(c context.Context, session userd.Session) error {
   340  		result, err = session.WorkloadInfoSnapshot(c, []string{lr.Namespace}, lr.Filter)
   341  		return err
   342  	})
   343  	return
   344  }
   345  
   346  func (s *service) WatchWorkloads(wr *rpc.WatchWorkloadsRequest, stream rpc.Connector_WatchWorkloadsServer) error {
   347  	var sessionCtx context.Context
   348  	var session userd.Session
   349  
   350  	err := s.WithSession(stream.Context(), "WatchWorkloads", func(c context.Context, s userd.Session) error {
   351  		session, sessionCtx = s, c
   352  		return nil
   353  	})
   354  	if err != nil {
   355  		return nil
   356  	}
   357  
   358  	return session.WatchWorkloads(sessionCtx, wr, stream)
   359  }
   360  
   361  func (s *service) Uninstall(c context.Context, ur *rpc.UninstallRequest) (result *common.Result, err error) {
   362  	err = s.WithSession(c, "Uninstall", func(c context.Context, session userd.Session) error {
   363  		result, err = session.Uninstall(c, ur)
   364  		return err
   365  	})
   366  	return
   367  }
   368  
   369  func (s *service) GetConfig(ctx context.Context, empty *empty.Empty) (cfg *rpc.ClientConfig, err error) {
   370  	err = s.WithSession(ctx, "GetConfig", func(c context.Context, session userd.Session) error {
   371  		sc, err := session.GetConfig(ctx)
   372  		if err != nil {
   373  			return err
   374  		}
   375  		data, err := json.Marshal(sc)
   376  		if err != nil {
   377  			return status.Error(codes.Internal, err.Error())
   378  		}
   379  		cfg = &rpc.ClientConfig{Json: data}
   380  		return nil
   381  	})
   382  	return
   383  }
   384  
   385  func (s *service) GatherLogs(ctx context.Context, request *rpc.LogsRequest) (result *rpc.LogsResponse, err error) {
   386  	err = s.WithSession(ctx, "GatherLogs", func(c context.Context, session userd.Session) error {
   387  		result, err = session.GatherLogs(c, request)
   388  		return err
   389  	})
   390  	return
   391  }
   392  
   393  func (s *service) SetLogLevel(ctx context.Context, request *rpc.LogLevelRequest) (result *empty.Empty, err error) {
   394  	s.logCall(ctx, "SetLogLevel", func(c context.Context) {
   395  		mrq := &manager.LogLevelRequest{
   396  			LogLevel: request.LogLevel,
   397  			Duration: request.Duration,
   398  		}
   399  		setLocal := func() {
   400  			duration := time.Duration(0)
   401  			if request.Duration != nil {
   402  				duration = request.Duration.AsDuration()
   403  			}
   404  			if err = logging.SetAndStoreTimedLevel(ctx, s.timedLogLevel, request.LogLevel, duration, userd.ProcessName); err != nil {
   405  				err = status.Error(codes.Internal, err.Error())
   406  			} else if !s.rootSessionInProc {
   407  				err = s.withRootDaemon(ctx, func(ctx context.Context, rd daemon.DaemonClient) error {
   408  					_, err := rd.SetLogLevel(ctx, mrq)
   409  					return err
   410  				})
   411  			}
   412  		}
   413  		setRemote := func() {
   414  			err = s.WithSession(ctx, "SetLogLevel", func(ctx context.Context, session userd.Session) error {
   415  				_, err := session.ManagerClient().SetLogLevel(ctx, mrq)
   416  				return err
   417  			})
   418  		}
   419  		switch request.Scope {
   420  		case rpc.LogLevelRequest_LOCAL_ONLY:
   421  			setLocal()
   422  		case rpc.LogLevelRequest_REMOTE_ONLY:
   423  			setRemote()
   424  		default:
   425  			setLocal()
   426  			if err == nil {
   427  				setRemote()
   428  			}
   429  		}
   430  	})
   431  	return &empty.Empty{}, err
   432  }
   433  
   434  func (s *service) Quit(ctx context.Context, ex *empty.Empty) (*empty.Empty, error) {
   435  	s.logCall(ctx, "Quit", func(c context.Context) {
   436  		s.sessionLock.RLock()
   437  		defer s.sessionLock.RUnlock()
   438  		s.cancelSessionReadLocked()
   439  		s.quit()
   440  		_ = s.withRootDaemon(ctx, func(ctx context.Context, rd daemon.DaemonClient) error {
   441  			_, err := rd.Quit(ctx, ex)
   442  			return err
   443  		})
   444  	})
   445  	return ex, nil
   446  }
   447  
   448  func (s *service) RemoteMountAvailability(ctx context.Context, _ *empty.Empty) (*common.Result, error) {
   449  	if proc.RunningInContainer() {
   450  		// We mount using docker volumes and the telemount driver plugin.
   451  		return errcat.ToResult(nil), nil
   452  	}
   453  	if client.GetConfig(ctx).Intercept().UseFtp {
   454  		return errcat.ToResult(s.FuseFTPError()), nil
   455  	}
   456  
   457  	// Use CombinedOutput to include stderr which has information about whether they
   458  	// need to upgrade to a newer version of macFUSE or not
   459  	var cmd *dexec.Cmd
   460  	if runtime.GOOS == "windows" {
   461  		cmd = proc.CommandContext(ctx, "sshfs-win", "cmd", "-V")
   462  	} else {
   463  		cmd = proc.CommandContext(ctx, "sshfs", "-V")
   464  	}
   465  	cmd.DisableLogging = true
   466  	out, err := cmd.CombinedOutput()
   467  	if err != nil {
   468  		dlog.Errorf(ctx, "sshfs not installed: %v", err)
   469  		return errcat.ToResult(errors.New("sshfs is not installed on your local machine")), nil
   470  	}
   471  
   472  	// OSXFUSE changed to macFUSE, and we've noticed that older versions of OSXFUSE
   473  	// can cause browsers to hang + kernel crashes, so we add an error to prevent
   474  	// our users from running into this problem.
   475  	// OSXFUSE isn't included in the output of sshfs -V in versions of 4.0.0 so
   476  	// we check for that as a proxy for if they have the right version or not.
   477  	if bytes.Contains(out, []byte("OSXFUSE")) {
   478  		return errcat.ToResult(errors.New(`macFUSE 4.0.5 or higher is required on your local machine`)), nil
   479  	}
   480  	return errcat.ToResult(nil), nil
   481  }
   482  
   483  func (s *service) GetNamespaces(ctx context.Context, req *rpc.GetNamespacesRequest) (*rpc.GetNamespacesResponse, error) {
   484  	var resp rpc.GetNamespacesResponse
   485  	err := s.WithSession(ctx, "GetNamespaces", func(ctx context.Context, session userd.Session) error {
   486  		resp.Namespaces = session.GetCurrentNamespaces(req.ForClientAccess)
   487  		return nil
   488  	})
   489  	if err != nil {
   490  		return nil, err
   491  	}
   492  
   493  	if p := req.Prefix; p != "" {
   494  		var namespaces []string
   495  		for _, namespace := range resp.Namespaces {
   496  			if strings.HasPrefix(namespace, p) {
   497  				namespaces = append(namespaces, namespace)
   498  			}
   499  		}
   500  		resp.Namespaces = namespaces
   501  	}
   502  
   503  	return &resp, nil
   504  }
   505  
   506  func (s *service) GatherTraces(ctx context.Context, request *rpc.TracesRequest) (result *common.Result, err error) {
   507  	err = s.WithSession(ctx, "GatherTraces", func(ctx context.Context, session userd.Session) error {
   508  		result = session.GatherTraces(ctx, request)
   509  		return nil
   510  	})
   511  	return
   512  }
   513  
   514  func (s *service) TrafficManagerVersion(ctx context.Context, _ *empty.Empty) (vi *common.VersionInfo, err error) {
   515  	err = s.WithSession(ctx, "TrafficManagerVersion", func(ctx context.Context, session userd.Session) error {
   516  		vi = &common.VersionInfo{Name: session.ManagerName(), Version: "v" + session.ManagerVersion().String()}
   517  		return nil
   518  	})
   519  	return
   520  }
   521  
   522  func (s *service) RootDaemonVersion(ctx context.Context, empty *empty.Empty) (vi *common.VersionInfo, err error) {
   523  	err = s.withRootDaemon(ctx, func(ctx context.Context, rd daemon.DaemonClient) error {
   524  		vi, err = rd.Version(ctx, empty)
   525  		return err
   526  	})
   527  	return vi, err
   528  }
   529  
   530  func (s *service) AgentImageFQN(ctx context.Context, empty *emptypb.Empty) (fqn *manager.AgentImageFQN, err error) {
   531  	err = s.WithSession(ctx, "AgentImageFQN", func(ctx context.Context, session userd.Session) error {
   532  		fqn, err = session.ManagerClient().GetAgentImageFQN(ctx, empty)
   533  		return err
   534  	})
   535  	return fqn, err
   536  }
   537  
   538  func (s *service) GetClusterSubnets(ctx context.Context, _ *empty.Empty) (cs *rpc.ClusterSubnets, err error) {
   539  	podSubnets := []*manager.IPNet{}
   540  	svcSubnets := []*manager.IPNet{}
   541  	err = s.WithSession(ctx, "GetClusterSubnets", func(ctx context.Context, session userd.Session) error {
   542  		// The manager can sometimes send the different subnets in different Sends,
   543  		// but after 5 seconds of listening to it, we should expect to have everything
   544  		tCtx, tCancel := context.WithTimeout(ctx, 5*time.Second)
   545  		defer tCancel()
   546  		infoStream, err := session.ManagerClient().WatchClusterInfo(tCtx, session.SessionInfo())
   547  		if err != nil {
   548  			return err
   549  		}
   550  		for {
   551  			mgrInfo, err := infoStream.Recv()
   552  			if err != nil {
   553  				if tCtx.Err() != nil || errors.Is(err, io.EOF) {
   554  					err = nil
   555  				}
   556  				return err
   557  			}
   558  			if mgrInfo.ServiceSubnet != nil {
   559  				svcSubnets = append(svcSubnets, mgrInfo.ServiceSubnet)
   560  			}
   561  			podSubnets = append(podSubnets, mgrInfo.PodSubnets...)
   562  		}
   563  	})
   564  	if err != nil {
   565  		return nil, err
   566  	}
   567  	return &rpc.ClusterSubnets{PodSubnets: podSubnets, SvcSubnets: svcSubnets}, nil
   568  }
   569  
   570  func (s *service) GetIntercept(ctx context.Context, request *manager.GetInterceptRequest) (ii *manager.InterceptInfo, err error) {
   571  	err = s.WithSession(ctx, "GetIntercept", func(ctx context.Context, session userd.Session) error {
   572  		ii = session.GetInterceptInfo(request.Name)
   573  		if ii == nil {
   574  			return status.Errorf(codes.NotFound, "found no intercept named %s", request.Name)
   575  		}
   576  		return nil
   577  	})
   578  	return ii, err
   579  }
   580  
   581  func (s *service) SetDNSExcludes(ctx context.Context, req *daemon.SetDNSExcludesRequest) (*emptypb.Empty, error) {
   582  	err := s.WithSession(ctx, "SetDNSExcludes", func(ctx context.Context, session userd.Session) error {
   583  		_, err := session.RootDaemon().SetDNSExcludes(ctx, req)
   584  		return err
   585  	})
   586  	return &empty.Empty{}, err
   587  }
   588  
   589  func (s *service) SetDNSMappings(ctx context.Context, req *daemon.SetDNSMappingsRequest) (*emptypb.Empty, error) {
   590  	err := s.WithSession(ctx, "SetDNSMappings", func(ctx context.Context, session userd.Session) error {
   591  		_, err := session.RootDaemon().SetDNSMappings(ctx, req)
   592  		return err
   593  	})
   594  	return &empty.Empty{}, err
   595  }
   596  
   597  func (s *service) withRootDaemon(ctx context.Context, f func(ctx context.Context, daemonClient daemon.DaemonClient) error) error {
   598  	if s.rootSessionInProc {
   599  		return status.Error(codes.Unavailable, "root daemon is embedded")
   600  	}
   601  	conn, err := socket.Dial(ctx, socket.RootDaemonPath(ctx))
   602  	if err == nil {
   603  		defer conn.Close()
   604  		err = f(ctx, daemon.NewDaemonClient(conn))
   605  	}
   606  	if err != nil {
   607  		err = status.Errorf(status.Code(err), "root daemon: %s", err.Error())
   608  	}
   609  	return err
   610  }