-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable Collector to be run as a Windows service #1120
Enable Collector to be run as a Windows service #1120
Conversation
8ec12b0
to
baf983a
Compare
Codecov Report
@@ Coverage Diff @@
## master #1120 +/- ##
=========================================
Coverage ? 86.91%
=========================================
Files ? 201
Lines ? 14532
Branches ? 0
=========================================
Hits ? 12631
Misses ? 1452
Partials ? 449
Continue to review full report at Codecov.
|
I will look into adding a couple of tests for the Note one thing I haven't implemented is the ability to install/uninstall/start/stop the Windows Service via command line flags. I don't really feel this is necessary as |
@jchengsfx please review. |
The SignalFx agent also uses kardianos and https://github.com/StackExchange/wmi for its windows service, and it has worked well for us so far (https://github.com/signalfx/signalfx-agent/blob/master/cmd/agent/agentmain/windows.go). |
baf983a
to
9028089
Compare
Normally I'd be keen to use a library with a higher level interface, but in this case I'm not sure kardianos is much different than using the |
(I intend to get back to this PR to add some tests tomorrow) |
cmd/otelcol/main_others.go
Outdated
|
||
import "log" | ||
|
||
func main() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we keep this in the main.go and move the helpers in a different file? Main other sounds like a second hand possibility :))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea having multiple main functions was probably a bad idea. I've restructured this slightly so there's only helper methods in this file - are you okay with how it is now or did you want me to rename the files as well?
(I feel like this is the right convention now where the xxx_{os}.go
files contain helper functions for the xxx.go
file)
6ca71bc
to
dc6ccdb
Compare
@@ -40,7 +40,8 @@ var ( | |||
) | |||
|
|||
type appTelemetry struct { | |||
views []*view.View | |||
views []*view.View | |||
server *http.Server |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q. Why did I change this file that's unrelated to the rest of the PR?
A. Because this PR introduces a second test that calls app.Start()
. Without this change, the app fails to start up on the second attempt because we never stopped listening to the metricsAddr
port.
dc6ccdb
to
6b40d31
Compare
I added tests and this is now ready for review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, mostly code cleanup comments.
service/service_windows.go
Outdated
|
||
func (s *WindowsService) Execute(args []string, requests <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) { | ||
if len(args) == 0 { | ||
log.Fatal("expected first argument supplied to service.Execute to be the service name") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Propagate error, here and everywhere else :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't really propogate errors in this file back to the main function as this function is invoked in a background goroutine via a syscall.
However, you did send me a on a path of reviewing how we handle errors here. Windows is not great at giving much information when services startup - you don't get any helpful error message from the startup command/dialog, & it takes a while to return any error. I tested the following:
- Log to event log (Event Viewer) directly: Where possible this is probably the best option as the error will come up in Application event logs, and include whatever info you specify.
- Return a system errorcode: This is useful to do where 1 is not possible or in addition to it. This will create an error representing the relevant error code in the System event logs.
- Use
log.Fatal
: This is probably the worst option as you just get a generic error message in the System event logs with no details of the error.
So I've refactored this file quite a bit to do 1 & 2 instead of 3 - you might want to take another look
log.Fatalf("failed to construct the application: %v", err) | ||
} | ||
|
||
err = app.Start() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rant: we should really rename this function to Run
(change #615 was rejected, but there were quite a few other breaking changes in the codebase since then).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea this is admittedly particularly confusing in this PR since we wait for the response of app.Start
in the windowsservice.stop
function. But if we rename this, I think it should be done in a separate PR
6b40d31
to
2db1df0
Compare
bf3e5c8
to
a8b8ba4
Compare
|
||
changes <- svc.Status{State: svc.StartPending} | ||
if err = s.start(elog, appErrorChannel); err != nil { | ||
elog.Error(3, fmt.Sprintf("failed to start service: %v", err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
a8b8ba4
to
b97c852
Compare
78e5077
to
dcb86e2
Compare
dcb86e2
to
081424c
Compare
@jchengsfx @dmitryax - can you please take a look? (this is also blocking #1153) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Link to tracking Issue:
#1119
Description:
When running the Collector on Windows, users will generally want to run this as a Windows service. It's not possible to just run a .exe directly as a Windows service, so some minor modifications needed to be made to the startup/service code.
This PR adds a separate main method on Windows (using build flags) that checks if the app is running interactively (i.e. invoked directly via the command line) vs running as a Windows service (i.e. invoked from service control manager).
If the executable is invoked from the command line, there are no changes to how the app is run. If the executable has been invoked from Windows service control manager, we call svc.Run on a service handler that is as a wrapper around
service.Application
.service.Parameters
, and redirect non-debug logs to the Windows Event Viewer.Testing:
Have installed and run this as a Windows service (created via MSI or sc.exe) to validate that it runs as expected.