From e7d85d4a8fe70530abf153002fb510ea5fcac4a3 Mon Sep 17 00:00:00 2001 From: Christian Ege Date: Fri, 7 Jun 2024 16:19:47 +0200 Subject: [PATCH] feat: add a swupdater package --- go.mod | 5 +- go.sum | 14 ++--- pkg/swupdater/swupdater.go | 108 +++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 pkg/swupdater/swupdater.go diff --git a/go.mod b/go.mod index d1f12aa..5164633 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,9 @@ go 1.21 require ( alexejk.io/go-xmlrpc v0.4.0 + github.com/gorilla/websocket v1.5.1 github.com/spf13/cobra v1.7.0 + github.com/stretchr/testify v1.8.4 ) require ( @@ -12,7 +14,6 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.0 // indirect - github.com/stretchr/testify v1.8.4 // indirect + golang.org/x/net v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 95a778e..bad1fcc 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,10 @@ alexejk.io/go-xmlrpc v0.4.0 h1:HvaeCuACuF2NBJruG90AJKc5JHRGj9vKxu2ltJntQR4= alexejk.io/go-xmlrpc v0.4.0/go.mod h1:M7f2OzqvZIWrN1LftR4uwW/bLpxrFoQYjWfm4gQB4JA= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -13,16 +14,11 @@ github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/swupdater/swupdater.go b/pkg/swupdater/swupdater.go new file mode 100644 index 0000000..554fc31 --- /dev/null +++ b/pkg/swupdater/swupdater.go @@ -0,0 +1,108 @@ +package swupdater + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "time" + + "github.com/gorilla/websocket" +) + +type SWUpdater struct { + hostName string + port int + path string + urlUpload string + urlStatus string + done chan error +} + +func NewSWUpdater(hostName, path string, port int) *SWUpdater { + return &SWUpdater{ + hostName: hostName, + port: port, + path: path, + urlUpload: fmt.Sprintf("http://%s:%d%s/upload", hostName, port, path), + urlStatus: fmt.Sprintf("ws://%s:%d%s/ws", hostName, port, path), + done: make(chan error), + } +} + +func (s *SWUpdater) upload(image io.Reader, timeout time.Duration) error { + req, err := http.NewRequest("POST", s.urlUpload, image) + if err != nil { + return fmt.Errorf("cannot create request: %w", err) + } + + client := &http.Client{Timeout: timeout} + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("cannot upload software image: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("cannot upload software image: status code %d", resp.StatusCode) + } + + return nil +} + +func (s *SWUpdater) waitForFinished() { + c, _, err := websocket.DefaultDialer.Dial(s.urlStatus, nil) + if err != nil { + s.done <- fmt.Errorf("cannot connect to websocket: %w", err) + return + } + defer c.Close() + + for { + _, message, err := c.ReadMessage() + if err != nil { + s.done <- fmt.Errorf("cannot read message from websocket: %w", err) + return + } + + data := make(map[string]string) + err = json.Unmarshal(message, &data) + if err != nil { + continue + } + + if data["type"] != "message" { + continue + } + + if data["text"] == "SWUPDATE successful" { + s.done <- nil + return + } + if data["text"] == "Installation failed" { + s.done <- errors.New("installation failed") + return + } + } +} + +func (s *SWUpdater) Update(image io.Reader, timeout time.Duration) error { + go s.waitForFinished() + go func() { + err := s.upload(image, timeout) + if err != nil { + s.done <- err + } + }() + + select { + case err := <-s.done: + if err != nil { + return err + } + return nil + case <-time.After(timeout): + return errors.New("timeout") + } +}