Skip to content

Commit d224770

Browse files
Merge pull request #3 from guillaumerose/daemon
Add daemon mode for Windows
2 parents ee7100e + c5cfd48 commit d224770

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+25657
-6
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.DS_Store
2-
/admin-helper
3-
/admin-helper.exe
2+
/crc-admin-helper
3+
/crc-admin-helper.exe
44
.idea
55
dist
66
out

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ GOPATH ?= $(shell go env GOPATH)
77
BINARY_NAME := crc-admin-helper
88
RELEASE_DIR ?= release
99

10-
LDFLAGS := -X main.Version=$(VERSION) -extldflags='-static' -s -w $(GO_LDFLAGS)
10+
LDFLAGS := -X github.com/code-ready/admin-helper/pkg/constants.Version=$(VERSION) -extldflags='-static' -s -w $(GO_LDFLAGS)
1111

1212
# Add default target
1313
.PHONY: all

cmd/admin-helper/daemon.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"net/http"
6+
7+
"github.com/code-ready/admin-helper/pkg/api"
8+
"github.com/code-ready/admin-helper/pkg/hosts"
9+
"github.com/kardianos/service"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
var InstallDaemon = &cobra.Command{
14+
Use: "install-daemon",
15+
Short: "Install the daemon",
16+
RunE: func(cmd *cobra.Command, args []string) error {
17+
svc, err := svc()
18+
if err != nil {
19+
return err
20+
}
21+
if err := svc.Install(); err != nil {
22+
return err
23+
}
24+
return svc.Start()
25+
},
26+
}
27+
28+
var UninstallDaemon = &cobra.Command{
29+
Use: "uninstall-daemon",
30+
Short: "Uninstall the daemon",
31+
RunE: func(cmd *cobra.Command, args []string) error {
32+
svc, err := svc()
33+
if err != nil {
34+
return err
35+
}
36+
if err := svc.Stop(); err != nil {
37+
log.Println(err)
38+
}
39+
return svc.Uninstall()
40+
},
41+
}
42+
43+
var Daemon = &cobra.Command{
44+
Use: "daemon",
45+
Short: "Run as a daemon",
46+
RunE: func(cmd *cobra.Command, args []string) error {
47+
return daemon()
48+
},
49+
}
50+
51+
func daemon() error {
52+
svc, err := svc()
53+
if err != nil {
54+
return err
55+
}
56+
return svc.Run()
57+
}
58+
59+
func svc() (service.Service, error) {
60+
svcConfig := &service.Config{
61+
Name: "CodeReadyContainersAdminHelper",
62+
DisplayName: "CodeReadyContainers Admin Helper",
63+
Description: "Perform administrative tasks for the user",
64+
Arguments: []string{"daemon"},
65+
}
66+
prg := &program{}
67+
return service.New(prg, svcConfig)
68+
}
69+
70+
type program struct{}
71+
72+
func (p *program) Start(s service.Service) error {
73+
go func() {
74+
logger, err := s.Logger(nil)
75+
if err != nil {
76+
log.Fatal(err)
77+
}
78+
ln, err := listen()
79+
if err != nil {
80+
_ = logger.Error(err)
81+
return
82+
}
83+
hosts, err := hosts.New()
84+
if err != nil {
85+
_ = logger.Error(err)
86+
return
87+
}
88+
if err := http.Serve(ln, api.Mux(hosts)); err != nil {
89+
_ = logger.Error(err)
90+
return
91+
}
92+
}()
93+
return nil
94+
}
95+
96+
func (p *program) Stop(s service.Service) error {
97+
return nil
98+
}

cmd/admin-helper/listen_unix.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// +build !windows
2+
3+
package main
4+
5+
import "net"
6+
7+
func listen() (net.Listener, error) {
8+
return net.Listen("unix", "daemon.sock")
9+
}

cmd/admin-helper/listen_windows.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"net"
6+
7+
"github.com/Microsoft/go-winio"
8+
)
9+
10+
func listen() (net.Listener, error) {
11+
// see https://github.com/moby/moby/blob/46cdcd206c56172b95ba5c77b827a722dab426c5/daemon/listeners/listeners_windows.go
12+
// allow Administrators and SYSTEM, plus whatever additional users or groups were specified
13+
sddl := "D:P(A;;GA;;;BA)(A;;GA;;;SY)"
14+
sid, err := winio.LookupSidByName("crc-users")
15+
if err != nil {
16+
return nil, err
17+
}
18+
sddl += fmt.Sprintf("(A;;GRGW;;;%s)", sid)
19+
20+
return winio.ListenPipe(`\\.\pipe\crc-admin-helper`, &winio.PipeConfig{
21+
SecurityDescriptor: sddl, // Administrators and system
22+
MessageMode: true, // Use message mode so that CloseWrite() is supported
23+
InputBufferSize: 65536, // Use 64KB buffers to improve performance
24+
OutputBufferSize: 65536,
25+
})
26+
}

cmd/admin-helper/main.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,23 @@ package main
33
import (
44
"fmt"
55
"os"
6+
"runtime"
67

8+
"github.com/code-ready/admin-helper/pkg/constants"
79
"github.com/spf13/cobra"
810
)
911

10-
var Version = "dev"
11-
1212
func main() {
1313
rootCmd := &cobra.Command{
1414
Use: "admin-helper",
15-
Version: Version,
15+
Version: constants.Version,
1616
SilenceUsage: true,
1717
}
1818

1919
rootCmd.AddCommand(Add, Remove, Clean, Contains)
20+
if runtime.GOOS == "windows" {
21+
rootCmd.AddCommand(InstallDaemon, UninstallDaemon, Daemon)
22+
}
2023

2124
if err := rootCmd.Execute(); err != nil {
2225
fmt.Fprintln(os.Stderr, err)

cmd/sample-client/main.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// +build windows
2+
3+
package main
4+
5+
import (
6+
"context"
7+
"fmt"
8+
"io/ioutil"
9+
"log"
10+
"net"
11+
"net/http"
12+
"time"
13+
14+
"github.com/Microsoft/go-winio"
15+
)
16+
17+
func main() {
18+
client := &http.Client{
19+
Transport: &http.Transport{
20+
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
21+
return winio.DialPipeContext(ctx, `\\.\pipe\crc-admin-helper`)
22+
},
23+
},
24+
Timeout: 5 * time.Second,
25+
}
26+
27+
res, err := client.Get("http://unix/version")
28+
if err != nil {
29+
log.Fatal(err)
30+
}
31+
bin, err := ioutil.ReadAll(res.Body)
32+
defer res.Body.Close()
33+
if err != nil {
34+
log.Fatal(err)
35+
}
36+
fmt.Println(string(bin))
37+
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ module github.com/code-ready/admin-helper
33
go 1.14
44

55
require (
6+
github.com/Microsoft/go-winio v0.5.0
67
github.com/dimchansky/utfbom v1.1.1 // indirect
78
github.com/goodhosts/hostsfile v0.0.7
9+
github.com/kardianos/service v1.2.0
810
github.com/spf13/cobra v1.1.1
911
github.com/stretchr/testify v1.3.0
1012
)

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
1313
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
1414
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
1515
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
16+
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
17+
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
1618
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
1719
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
1820
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -103,6 +105,8 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
103105
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
104106
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
105107
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
108+
github.com/kardianos/service v1.2.0 h1:bGuZ/epo3vrt8IPC7mnKQolqFeYJb7Cs8Rk4PSOBB/g=
109+
github.com/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
106110
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
107111
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
108112
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
@@ -132,6 +136,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
132136
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
133137
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
134138
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
139+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
135140
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
136141
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
137142
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
@@ -153,6 +158,7 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
153158
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
154159
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
155160
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
161+
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
156162
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
157163
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
158164
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -240,6 +246,10 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
240246
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
241247
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
242248
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
249+
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
250+
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
251+
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
252+
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
243253
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
244254
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
245255
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=

pkg/api/mux.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package api
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
8+
"github.com/code-ready/admin-helper/pkg/constants"
9+
"github.com/code-ready/admin-helper/pkg/hosts"
10+
"github.com/code-ready/admin-helper/pkg/types"
11+
)
12+
13+
func Mux(hosts *hosts.Hosts) http.Handler {
14+
mux := http.NewServeMux()
15+
mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
16+
_, _ = fmt.Fprint(w, constants.Version)
17+
})
18+
mux.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) {
19+
if r.Method != http.MethodPost {
20+
http.Error(w, "post only", http.StatusBadRequest)
21+
return
22+
}
23+
var req types.AddRequest
24+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
25+
http.Error(w, err.Error(), http.StatusBadRequest)
26+
return
27+
}
28+
if err := hosts.Add(req.IP, req.Hosts); err != nil {
29+
http.Error(w, err.Error(), http.StatusInternalServerError)
30+
return
31+
}
32+
w.WriteHeader(http.StatusOK)
33+
})
34+
mux.HandleFunc("/remove", func(w http.ResponseWriter, r *http.Request) {
35+
if r.Method != http.MethodPost {
36+
http.Error(w, "post only", http.StatusBadRequest)
37+
return
38+
}
39+
var req types.RemoveRequest
40+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
41+
http.Error(w, err.Error(), http.StatusBadRequest)
42+
return
43+
}
44+
if err := hosts.Remove(req.Hosts); err != nil {
45+
http.Error(w, err.Error(), http.StatusInternalServerError)
46+
return
47+
}
48+
w.WriteHeader(http.StatusOK)
49+
})
50+
mux.HandleFunc("/clean", func(w http.ResponseWriter, r *http.Request) {
51+
if r.Method != http.MethodPost {
52+
http.Error(w, "post only", http.StatusBadRequest)
53+
return
54+
}
55+
var req types.CleanRequest
56+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
57+
http.Error(w, err.Error(), http.StatusBadRequest)
58+
return
59+
}
60+
if err := hosts.Clean(req.Domains); err != nil {
61+
http.Error(w, err.Error(), http.StatusInternalServerError)
62+
return
63+
}
64+
w.WriteHeader(http.StatusOK)
65+
})
66+
return mux
67+
}

pkg/api/mux_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package api
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"testing"
7+
8+
"github.com/code-ready/admin-helper/pkg/client"
9+
"github.com/code-ready/admin-helper/pkg/constants"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestMux(t *testing.T) {
14+
ts := httptest.NewServer(Mux(nil))
15+
defer ts.Close()
16+
17+
client := client.New(http.DefaultClient, ts.URL)
18+
version, err := client.Version()
19+
assert.NoError(t, err)
20+
assert.Equal(t, constants.Version, version)
21+
}

0 commit comments

Comments
 (0)