-
Notifications
You must be signed in to change notification settings - Fork 118
/
stopintegration.go
128 lines (104 loc) · 4.26 KB
/
stopintegration.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright 2020 New Relic Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package stopintegration
import (
"context"
"encoding/json"
"github.com/newrelic/infrastructure-agent/pkg/config"
"runtime"
"time"
"github.com/newrelic/infrastructure-agent/internal/agent/cmdchannel"
"github.com/newrelic/infrastructure-agent/internal/agent/cmdchannel/runintegration"
"github.com/newrelic/infrastructure-agent/internal/integrations/v4/integration"
"github.com/newrelic/infrastructure-agent/pkg/backend/commandapi"
"github.com/newrelic/infrastructure-agent/pkg/integrations/cmdapi"
"github.com/newrelic/infrastructure-agent/pkg/integrations/track"
"github.com/newrelic/infrastructure-agent/pkg/integrations/track/ctx"
"github.com/newrelic/infrastructure-agent/pkg/integrations/v4/dm"
"github.com/newrelic/infrastructure-agent/pkg/log"
"github.com/shirou/gopsutil/v3/process"
)
const (
cmdName = "stop_integration"
terminationGracePeriod = 1 * time.Minute
)
// NewHandler creates a cmd-channel handler for stop-integration requests.
func NewHandler(tracker *track.Tracker, il integration.InstancesLookup, dmEmitter dm.Emitter, l log.Entry) *cmdchannel.CmdHandler {
handleF := func(ctx context.Context, cmd commandapi.Command, initialFetch bool) (err error) {
if runtime.GOOS == "windows" {
return cmdchannel.ErrOSNotSupported
}
l.WithField(config.TracesFieldName, config.FeatureTrace).Trace("stop integration request received")
var args runintegration.RunIntArgs
if err = json.Unmarshal(cmd.Args, &args); err != nil {
err = cmdchannel.NewArgsErr(err)
return
}
if args.IntegrationName == "" {
err = cmdchannel.NewArgsErr(runintegration.ErrNoIntName)
return
}
if cmdapi.IsForbiddenToRunStopFromCmdAPI(args.IntegrationName) {
return runintegration.ErrIntNotAllowed
}
pidC, tracked := tracker.PIDReadChan(args.Hash())
// integration isn't running
if pidC == nil {
if tracked {
runintegration.LogDecorated(l, cmd, args).Debug("integration is not running")
} else {
runintegration.LogDecorated(l, cmd, args).Warn("cannot stop non tracked integration")
}
return nil
}
p, err := process.NewProcess(int32(<-pidC))
if err != nil {
runintegration.LogDecorated(l, cmd, args).WithError(err).Warn("cannot retrieve process")
notifyPlatformWithLog(dmEmitter, il, cmd, args, "process-not-found", l)
return
}
stopModeUsed := "error"
// request graceful stop (SIGTERM)
err = p.TerminateWithContext(ctx)
if err != nil {
runintegration.LogDecorated(l, cmd, args).WithError(err).Debug("cannot SIGTERM process")
} else {
// wait grace period, blocking is fine as cmd handlers run within their own goroutines.
time.Sleep(terminationGracePeriod)
}
isRunning, err := p.IsRunningWithContext(ctx)
if err != nil {
runintegration.LogDecorated(l, cmd, args).WithError(err).Warn("cannot retrieve process running state")
} else {
stopModeUsed = "sigterm"
}
// force termination (SIGKILL)
if isRunning || err != nil {
stopped := tracker.Kill(args.Hash())
runintegration.LogDecorated(l, cmd, args).WithField("stopped", stopped).Debug("integration force quit")
stopModeUsed = "sigkill"
}
notifyPlatformWithLog(dmEmitter, il, cmd, args, stopModeUsed, l)
// no further error handling required
err = nil
return
}
return cmdchannel.NewCmdHandler(cmdName, handleF)
}
func notifyPlatformWithLog(dmEmitter dm.Emitter, il integration.InstancesLookup, cmd commandapi.Command, args runintegration.RunIntArgs, stopModeUsed string, l log.Entry) {
if err := notifyPlatform(dmEmitter, il, cmd, args, stopModeUsed); err != nil {
runintegration.LogDecorated(l, cmd, args).WithError(err).Warn("cannot notify platform about command")
}
}
func notifyPlatform(dmEmitter dm.Emitter, il integration.InstancesLookup, cmd commandapi.Command, args runintegration.RunIntArgs, stopModeUsed string) error {
def, err := integration.NewDefinition(runintegration.NewConfigFromCmdChannelRunInt(args), il, nil, nil)
if err != nil {
return err
}
ccReq := ctx.NewCmdChannelRequest(cmdName, cmd.Hash, args.IntegrationName, args.IntegrationArgs, cmd.Metadata)
def.CmdChanReq = &ccReq
ev := ccReq.Event("cmd-api")
ev["cmd_stop_mode"] = stopModeUsed
runintegration.NotifyPlatform(dmEmitter, def, ev)
return nil
}