(ctx context.Context, logger *zap.Logger, conf *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator)
| 17 | } |
| 18 | |
| 19 | func Agent(ctx context.Context, logger *zap.Logger, conf *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command { |
| 20 | var cmd = &cobra.Command{ |
| 21 | Use: "agent", |
| 22 | Short: "starts keploy agent for hooking and starting proxy", |
| 23 | // Hidden: true, |
| 24 | PreRunE: func(cmd *cobra.Command, _ []string) error { |
| 25 | return cmdConfigurator.Validate(ctx, cmd) |
| 26 | }, |
| 27 | RunE: func(cmd *cobra.Command, _ []string) error { |
| 28 | svc, err := serviceFactory.GetService(ctx, cmd.Name()) |
| 29 | if err != nil { |
| 30 | utils.LogError(logger, err, "failed to get service") |
| 31 | return nil |
| 32 | } |
| 33 | |
| 34 | var a agent.Service |
| 35 | var ok bool |
| 36 | if a, ok = svc.(agent.Service); !ok { |
| 37 | utils.LogError(logger, nil, "service doesn't satisfy agent service interface") |
| 38 | return nil |
| 39 | } |
| 40 | |
| 41 | // Self-terminate (gracefully) if the parent keploy client dies |
| 42 | // abnormally, so the agent never orphans and keeps eBPF hooks / |
| 43 | // DNS / proxy+ingress ports alive that would hang the next run. |
| 44 | // Read the flags directly so this never depends on config wiring. |
| 45 | // |
| 46 | // Skip it in docker mode: there the agent runs in its OWN |
| 47 | // `docker run --rm` container (separate PID namespace), so |
| 48 | // --client-pid is the *host* keploy PID and is not visible here — |
| 49 | // kill(pid, 0) would return ESRCH and we'd self-terminate |
| 50 | // immediately, breaking record/replay. The container's --rm |
| 51 | // lifecycle bounds the agent in that mode instead. |
| 52 | clientPID, cpErr := cmd.Flags().GetUint32("client-pid") |
| 53 | isDocker, dockErr := cmd.Flags().GetBool("is-docker") |
| 54 | switch { |
| 55 | case cpErr != nil || dockErr != nil: |
| 56 | // A flag was renamed/removed or the command was built without |
| 57 | // AddFlags. Don't guess — leave the watchdog off and say so, |
| 58 | // rather than silently watching a zero PID. |
| 59 | logger.Debug("could not read client-pid/is-docker flags; parent-death watchdog left disabled", |
| 60 | zap.NamedError("clientPidErr", cpErr), zap.NamedError("isDockerErr", dockErr)) |
| 61 | case isDocker: |
| 62 | logger.Debug("parent-death watchdog disabled in docker mode (separate PID namespace; --rm bounds the container)") |
| 63 | default: |
| 64 | watchParentProcess(ctx, logger, int(clientPID)) |
| 65 | } |
| 66 | |
| 67 | startAgentCh := make(chan int) |
| 68 | router := chi.NewRouter() |
| 69 | |
| 70 | routes.ActiveHooks.New(router, a, logger) |
| 71 | go func() { |
| 72 | select { |
| 73 | case <-ctx.Done(): |
| 74 | logger.Info("context cancelled before agent http server could start") |
| 75 | return |
| 76 | case p := <-startAgentCh: |
nothing calls this directly
no test coverage detected