Compare commits

...

3 commits

Author SHA1 Message Date
3c9867b75b fix apidoc
All checks were successful
continuous-integration/drone/push Build is passing
2021-12-19 22:44:30 +01:00
e3cb739db5 fix SysinfoCard: reloader not cleared 2021-12-19 22:37:43 +01:00
4cc757d550 add tests for rauc, ui and middleware 2021-12-19 22:29:12 +01:00
26 changed files with 477 additions and 56 deletions

View file

@ -1,11 +1,11 @@
root = "./src"
root = "."
tmp_dir = "tmp"
[build]
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ./src/."
delay = 1000
exclude_dir = ["assets", "tmp", "vendor"]
exclude_dir = ["tmp", "vendor", "ui/dist", "ui/node_modules", "ui/src"]
exclude_file = []
exclude_regex = []
exclude_unchanged = false

View file

@ -18,6 +18,7 @@ linters:
linters-settings:
lll:
line-length: 88
tab-width: 4
gocognit:
min-complexity: 10
nestif:

17
go.mod
View file

@ -9,10 +9,21 @@ require (
github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.7.7
github.com/go-errors/errors v1.4.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.4.2
github.com/gorilla/websocket v1.4.1
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

53
go.sum
View file

@ -2,6 +2,7 @@ code.thetadev.de/TSGRain/ginzip v0.1.1 h1:+X0L6qumEZiKYSLmM+Q0LqKVHsKvdcg4CVzsEp
code.thetadev.de/TSGRain/ginzip v0.1.1/go.mod h1:BH7VkvpP83vPRyMQ8rLIjKycQwGzF+/mFV0BKzg+BuA=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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=
@ -29,31 +30,42 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -67,8 +79,8 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -76,21 +88,32 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -48,27 +48,47 @@ const statusJson = `{"compatible":"TSGRain","variant":"dev","booted":"A",` +
`{"class":"rootfs","device":"/dev/mmcblk0p2","type":"ext4","bootname":"A",` +
`"state":"booted","parent":null,"mountpoint":"/","boot_status":"good"}}]}`
func printLinesWithDelay(lines string) {
func printLinesWithDelay(lines string, delay time.Duration) {
for _, line := range strings.Split(lines, "\n") {
fmt.Println(line)
time.Sleep(500 * time.Millisecond)
time.Sleep(delay)
}
}
func getBoolEnvvar(name string) bool {
val := strings.ToLower(os.Getenv(name))
return val != "" && val != "false" && val != "0"
}
func main() {
arg := ""
method := ""
if len(os.Args) > 1 {
arg = os.Args[1]
method = os.Args[1]
}
switch arg {
case "fail":
printLinesWithDelay(outputFailure)
test := getBoolEnvvar("RAUC_MOCK_TEST")
failure := getBoolEnvvar("RAUC_MOCK_FAIL")
delay := 500 * time.Millisecond
if test {
delay = 10 * time.Millisecond
}
switch method {
case "install":
printLinesWithDelay(outputSuccess)
if failure {
printLinesWithDelay(outputFailure, delay)
} else {
printLinesWithDelay(outputSuccess, delay)
}
case "status":
if os.Args[2] != "--output-format=json" {
fmt.Println("output format must be json")
os.Exit(1)
}
fmt.Println(statusJson)
default:
fmt.Println("invalid method")
os.Exit(1)
}
}

View file

@ -7,14 +7,13 @@ import (
"code.thetadev.de/TSGRain/SEBRAUC/src/rauc"
"code.thetadev.de/TSGRain/SEBRAUC/src/server"
"code.thetadev.de/TSGRain/SEBRAUC/src/server/mode"
"code.thetadev.de/TSGRain/SEBRAUC/src/server/stream"
"code.thetadev.de/TSGRain/SEBRAUC/src/util"
"code.thetadev.de/TSGRain/SEBRAUC/src/util/mode"
)
func main() {
fmt.Println("SEBRAUC " + util.Version())
mode.Set(util.Mode)
if mode.IsDev() {
fmt.Println("Test mode active - no update operations are executed.")

View file

@ -5,6 +5,7 @@ package model
// RaucStatus contains information about the current RAUC updater status.
//
//swagger:model RaucStatus
//nolint:lll
type RaucStatus struct {
// True if the installer is running
// required: true

View file

@ -26,12 +26,12 @@ type Rauc struct {
func (r *Rauc) SetBroadcaster(bc util.Broadcaster) {
r.bc = bc
r.bc.Broadcast(r.GetStatusJson())
r.bcStatus()
}
func (r *Rauc) completed(updateFile string) {
r.status.Installing = false
r.bc.Broadcast(r.GetStatusJson())
r.bcStatus()
_ = os.Remove(updateFile)
}
@ -56,9 +56,10 @@ func (r *Rauc) RunRauc(updateFile string) error {
r.status = model.RaucStatus{
Installing: true,
}
r.bc.Broadcast(r.GetStatusJson())
r.bcStatus()
cmd := util.CommandFromString(fmt.Sprintf("%s install %s", util.RaucCmd, updateFile))
cmd := util.CommandFromString(
fmt.Sprintf("%s install %s", util.RaucCmd, updateFile))
readPipe, _ := cmd.StdoutPipe()
cmd.Stderr = cmd.Stdout
@ -88,7 +89,7 @@ func (r *Rauc) RunRauc(updateFile string) error {
}
if hasUpdate {
r.bc.Broadcast(r.GetStatusJson())
r.bcStatus()
}
}
}()
@ -118,3 +119,7 @@ func (r *Rauc) GetStatusJson() []byte {
statusJson, _ := json.Marshal(r.status)
return statusJson
}
func (r *Rauc) bcStatus() {
r.bc.Broadcast(r.GetStatusJson())
}

116
src/rauc/rauc_test.go Normal file
View file

@ -0,0 +1,116 @@
package rauc
import (
"os"
"path/filepath"
"testing"
"time"
"code.thetadev.de/TSGRain/SEBRAUC/src/util"
"github.com/stretchr/testify/assert"
)
type broadcasterMock struct {
messages []string
}
func (b *broadcasterMock) Broadcast(msg []byte) {
b.messages = append(b.messages, string(msg))
}
func TestRauc(t *testing.T) {
//nolint:lll
tests := []struct {
name string
fail string
messages []string
}{
{
name: "ok",
fail: "",
messages: []string{
"{\"installing\":false,\"percent\":0,\"message\":\"\",\"last_error\":\"\",\"log\":\"\"}",
"{\"installing\":true,\"percent\":0,\"message\":\"\",\"last_error\":\"\",\"log\":\"\"}",
"{\"installing\":true,\"percent\":0,\"message\":\"Installing\",\"last_error\":\"\",\"log\":\"0% Installing\\n\"}",
"{\"installing\":true,\"percent\":0,\"message\":\"Determining slot states\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n\"}",
"{\"installing\":true,\"percent\":20,\"message\":\"Determining slot states done.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n\"}",
"{\"installing\":true,\"percent\":20,\"message\":\"Checking bundle\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n\"}",
"{\"installing\":true,\"percent\":20,\"message\":\"Verifying signature\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n\"}",
"{\"installing\":true,\"percent\":40,\"message\":\"Verifying signature done.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n\"}",
"{\"installing\":true,\"percent\":40,\"message\":\"Checking bundle done.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n\"}",
"{\"installing\":true,\"percent\":40,\"message\":\"Checking manifest contents\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n\"}",
"{\"installing\":true,\"percent\":60,\"message\":\"Checking manifest contents done.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n60% Checking manifest contents done.\\n\"}",
"{\"installing\":true,\"percent\":60,\"message\":\"Determining target install group\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n60% Checking manifest contents done.\\n60% Determining target install group\\n\"}",
"{\"installing\":true,\"percent\":80,\"message\":\"Determining target install group done.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n60% Checking manifest contents done.\\n60% Determining target install group\\n80% Determining target install group done.\\n\"}",
"{\"installing\":true,\"percent\":80,\"message\":\"Updating slots\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n60% Checking manifest contents done.\\n60% Determining target install group\\n80% Determining target install group done.\\n80% Updating slots\\n\"}",
"{\"installing\":true,\"percent\":80,\"message\":\"Checking slot rootfs.0\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n60% Checking manifest contents done.\\n60% Determining target install group\\n80% Determining target install group done.\\n80% Updating slots\\n80% Checking slot rootfs.0\\n\"}",
"{\"installing\":true,\"percent\":90,\"message\":\"Checking slot rootfs.0 done.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n60% Checking manifest contents done.\\n60% Determining target install group\\n80% Determining target install group done.\\n80% Updating slots\\n80% Checking slot rootfs.0\\n90% Checking slot rootfs.0 done.\\n\"}",
"{\"installing\":true,\"percent\":90,\"message\":\"Copying image to rootfs.0\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n60% Checking manifest contents done.\\n60% Determining target install group\\n80% Determining target install group done.\\n80% Updating slots\\n80% Checking slot rootfs.0\\n90% Checking slot rootfs.0 done.\\n90% Copying image to rootfs.0\\n\"}",
"{\"installing\":true,\"percent\":100,\"message\":\"Copying image to rootfs.0 done.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n60% Checking manifest contents done.\\n60% Determining target install group\\n80% Determining target install group done.\\n80% Updating slots\\n80% Checking slot rootfs.0\\n90% Checking slot rootfs.0 done.\\n90% Copying image to rootfs.0\\n100% Copying image to rootfs.0 done.\\n\"}",
"{\"installing\":true,\"percent\":100,\"message\":\"Updating slots done.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n60% Checking manifest contents done.\\n60% Determining target install group\\n80% Determining target install group done.\\n80% Updating slots\\n80% Checking slot rootfs.0\\n90% Checking slot rootfs.0 done.\\n90% Copying image to rootfs.0\\n100% Copying image to rootfs.0 done.\\n100% Updating slots done.\\n\"}",
"{\"installing\":true,\"percent\":100,\"message\":\"Installing done.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n60% Checking manifest contents done.\\n60% Determining target install group\\n80% Determining target install group done.\\n80% Updating slots\\n80% Checking slot rootfs.0\\n90% Checking slot rootfs.0 done.\\n90% Copying image to rootfs.0\\n100% Copying image to rootfs.0 done.\\n100% Updating slots done.\\n100% Installing done.\\n\"}",
"{\"installing\":false,\"percent\":100,\"message\":\"Installing done.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n20% Verifying signature\\n40% Verifying signature done.\\n40% Checking bundle done.\\n40% Checking manifest contents\\n60% Checking manifest contents done.\\n60% Determining target install group\\n80% Determining target install group done.\\n80% Updating slots\\n80% Checking slot rootfs.0\\n90% Checking slot rootfs.0 done.\\n90% Copying image to rootfs.0\\n100% Copying image to rootfs.0 done.\\n100% Updating slots done.\\n100% Installing done.\\nInstalling `/app/tsgrain-update-raspberrypi3.raucb` succeeded\\n\"}",
},
},
{
name: "fail",
fail: "1",
messages: []string{
"{\"installing\":false,\"percent\":0,\"message\":\"\",\"last_error\":\"\",\"log\":\"\"}",
"{\"installing\":true,\"percent\":0,\"message\":\"\",\"last_error\":\"\",\"log\":\"\"}",
"{\"installing\":true,\"percent\":0,\"message\":\"Installing\",\"last_error\":\"\",\"log\":\"0% Installing\\n\"}",
"{\"installing\":true,\"percent\":0,\"message\":\"Determining slot states\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n\"}",
"{\"installing\":true,\"percent\":20,\"message\":\"Determining slot states done.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n\"}",
"{\"installing\":true,\"percent\":20,\"message\":\"Checking bundle\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n\"}",
"{\"installing\":true,\"percent\":40,\"message\":\"Checking bundle failed.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n40% Checking bundle failed.\\n\"}",
"{\"installing\":true,\"percent\":100,\"message\":\"Installing failed.\",\"last_error\":\"\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n40% Checking bundle failed.\\n100% Installing failed.\\n\"}",
"{\"installing\":true,\"percent\":100,\"message\":\"Installing failed.\",\"last_error\":\"Failed to check bundle identifier: Invalid identifier. Did you pass a valid RAUC bundle?\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n40% Checking bundle failed.\\n100% Installing failed.\\nLastError: Failed to check bundle identifier: Invalid identifier. Did you pass a valid RAUC bundle?\\n\"}",
"{\"installing\":false,\"percent\":100,\"message\":\"Installing failed.\",\"last_error\":\"Failed to check bundle identifier: Invalid identifier. Did you pass a valid RAUC bundle?\",\"log\":\"0% Installing\\n0% Determining slot states\\n20% Determining slot states done.\\n20% Checking bundle\\n40% Checking bundle failed.\\n100% Installing failed.\\nLastError: Failed to check bundle identifier: Invalid identifier. Did you pass a valid RAUC bundle?\\nInstalling /app/demo` failed\\n\"}",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
os.Setenv("RAUC_MOCK_TEST", "1")
os.Setenv("RAUC_MOCK_FAIL", tt.fail)
updater := &Rauc{}
bc := &broadcasterMock{}
updater.SetBroadcaster(bc)
testfile := createTmpfile()
err := updater.RunRauc(testfile)
assert.NoError(t, err)
// Dont run multiple updates concurrently
err = updater.RunRauc(testfile)
assert.ErrorIs(t, err, util.ErrAlreadyRunning)
// Wait for updater to finish
for updater.GetStatus().Installing {
time.Sleep(50 * time.Millisecond)
}
assert.False(t, util.DoesFileExist(testfile), "update file was not deleted")
assert.Equal(t, tt.messages, bc.messages)
})
}
}
func createTmpfile() string {
tmpdir, err := util.GetTmpdir()
if err != nil {
panic(err)
}
tmpfile := filepath.Join(tmpdir, "test.raucb")
_, err = os.Create(tmpfile)
if err != nil {
panic(err)
}
return tmpfile
}

View file

@ -0,0 +1,25 @@
package middleware
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func TestCache(t *testing.T) {
router := gin.New()
router.Use(Cache)
router.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "HelloWorld") })
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "HelloWorld", w.Body.String())
assert.Equal(t, "public, max-age=604800, immutable",
w.Header().Get("Cache-Control"))
}

View file

@ -26,7 +26,7 @@ func ErrorHandler(isApi bool) gin.HandlerFunc {
func PanicHandler(isApi bool) gin.HandlerFunc {
return nice.Recovery(func(c *gin.Context, err interface{}) {
writeError(c, fmt.Errorf("panic: %s", err), isApi)
writeError(c, fmt.Errorf("[PANIC] %s", err), isApi)
})
}

View file

@ -0,0 +1,140 @@
package middleware
import (
"errors"
"net/http"
"net/http/httptest"
"testing"
"code.thetadev.de/TSGRain/SEBRAUC/src/util"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func TestErrorHandler(t *testing.T) {
tests := []struct {
name string
controller gin.HandlerFunc
isApi bool
expectResponse string
expectStatus int
}{
{
name: "error",
controller: controllerError,
isApi: false,
expectResponse: "400 Bad Request: error test",
expectStatus: http.StatusBadRequest,
},
{
name: "error_api",
controller: controllerError,
isApi: true,
//nolint:lll
expectResponse: `{"error":"Bad Request","status_code":400,"msg":"error test"}`,
expectStatus: http.StatusBadRequest,
},
{
name: "generic_error",
controller: controllerErrorGeneric,
isApi: false,
expectResponse: "500 Internal Server Error: generic error",
expectStatus: http.StatusInternalServerError,
},
{
name: "generic_error_api",
controller: controllerErrorGeneric,
isApi: true,
//nolint:lll
expectResponse: `{"error":"Internal Server Error","status_code":500,"msg":"generic error"}`,
expectStatus: http.StatusInternalServerError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
router := gin.New()
router.Use(ErrorHandler(tt.isApi))
router.GET("/", tt.controller)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
router.ServeHTTP(w, req)
assert.Equal(t, tt.expectStatus, w.Code)
assert.Equal(t, tt.expectResponse, w.Body.String())
})
}
}
func TestPanicHandler(t *testing.T) {
tests := []struct {
name string
controller gin.HandlerFunc
isApi bool
expectResponse string
expectStatus int
}{
{
name: "panic",
controller: controllerPanic,
isApi: false,
expectResponse: "500 Internal Server Error: [PANIC] panic message",
expectStatus: http.StatusInternalServerError,
},
{
name: "panic_api",
controller: controllerPanic,
isApi: true,
//nolint:lll
expectResponse: `{"error":"Internal Server Error","status_code":500,"msg":"[PANIC] panic message"}`,
expectStatus: http.StatusInternalServerError,
},
{
name: "panic_w_error",
controller: controllerPanicErr,
isApi: false,
expectResponse: "500 Internal Server Error: [PANIC] panic message in error",
expectStatus: http.StatusInternalServerError,
},
{
name: "panic_w_error_api",
controller: controllerPanicErr,
isApi: true,
//nolint:lll
expectResponse: `{"error":"Internal Server Error","status_code":500,"msg":"[PANIC] panic message in error"}`,
expectStatus: http.StatusInternalServerError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
router := gin.New()
router.Use(PanicHandler(tt.isApi))
router.GET("/", tt.controller)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
router.ServeHTTP(w, req)
assert.Equal(t, tt.expectStatus, w.Code)
assert.Equal(t, tt.expectResponse, w.Body.String())
})
}
}
func controllerError(c *gin.Context) {
c.Error(util.HttpErrNew("error test", http.StatusBadRequest))
}
func controllerErrorGeneric(c *gin.Context) {
c.Error(errors.New("generic error"))
}
func controllerPanic(c *gin.Context) {
panic("panic message")
}
func controllerPanicErr(c *gin.Context) {
panic(errors.New("panic message in error"))
}

View file

@ -18,10 +18,10 @@ import (
"code.thetadev.de/TSGRain/SEBRAUC/src/model"
"code.thetadev.de/TSGRain/SEBRAUC/src/server/middleware"
"code.thetadev.de/TSGRain/SEBRAUC/src/server/mode"
"code.thetadev.de/TSGRain/SEBRAUC/src/server/swagger"
"code.thetadev.de/TSGRain/SEBRAUC/src/sysinfo"
"code.thetadev.de/TSGRain/SEBRAUC/src/util"
"code.thetadev.de/TSGRain/SEBRAUC/src/util/mode"
"code.thetadev.de/TSGRain/SEBRAUC/ui"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
@ -124,7 +124,7 @@ func (srv *SEBRAUCServer) Run() error {
// $ref: "#/definitions/StatusMessage"
// 409:
// description: already running
// schema:
// schema:
// $ref: "#/definitions/Error"
// 500:
// description: Server Error
@ -154,8 +154,6 @@ func (srv *SEBRAUCServer) controllerUpdate(c *gin.Context) {
err = srv.udater.RunRauc(updateFile)
if err == nil {
writeStatus(c, true, "Update started")
} else if errors.Is(err, util.ErrAlreadyRunning) {
writeStatus(c, false, "Updater already running")
} else {
c.Error(err)
return

View file

@ -20,7 +20,7 @@ func Test_Execute(t *testing.T) {
case <-execution:
// expected
case <-time.After(100 * time.Millisecond):
t.Fatal("fExecute should be executed once")
t.Fatal("Execute should be executed once")
}
select {

View file

@ -8,8 +8,8 @@ import (
"sync"
"time"
"code.thetadev.de/TSGRain/SEBRAUC/src/server/mode"
"code.thetadev.de/TSGRain/SEBRAUC/src/util"
"code.thetadev.de/TSGRain/SEBRAUC/src/util/mode"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)
@ -146,7 +146,8 @@ func newUpgrader(allowedWebSocketOrigins []string) *websocket.Upgrader {
func compileAllowedWebSocketOrigins(allowedOrigins []string) []*regexp.Regexp {
var compiledAllowedOrigins []*regexp.Regexp
for _, origin := range allowedOrigins {
compiledAllowedOrigins = append(compiledAllowedOrigins, regexp.MustCompile(origin))
compiledAllowedOrigins = append(compiledAllowedOrigins,
regexp.MustCompile(origin))
}
return compiledAllowedOrigins

View file

@ -9,7 +9,7 @@ import (
"testing"
"time"
"code.thetadev.de/TSGRain/SEBRAUC/src/server/mode"
"code.thetadev.de/TSGRain/SEBRAUC/src/util/mode"
"github.com/fortytw2/leaktest"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"

View file

@ -216,6 +216,8 @@ paths:
$ref: "#/definitions/StatusMessage"
"409":
description: already running
schema:
$ref: "#/definitions/Error"
"500":
description: Server Error
schema:

View file

@ -0,0 +1,44 @@
package swagger
import (
"bytes"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func TestSwagger(t *testing.T) {
router := gin.New()
Register(router)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/swagger/", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, swaggerHtml, w.Body.Bytes())
assert.NotEmpty(t, w.Header().Get("Cache-Control"))
w = httptest.NewRecorder()
req, _ = http.NewRequest("GET", "/api/swagger/swagger.yaml", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, swaggerYaml, w.Body.Bytes())
assert.NotEmpty(t, w.Header().Get("Cache-Control"))
}
func TestSwaggerData(t *testing.T) {
assert.True(t, bytes.Contains(swaggerHtml,
[]byte("https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js")),
"HTML data missing",
)
assert.True(t, bytes.Contains(swaggerYaml,
[]byte("REST API for the SEBRAUC firmware updater")),
"YAML data missing",
)
}

View file

@ -3,11 +3,11 @@
package util
import "code.thetadev.de/TSGRain/SEBRAUC/src/server/mode"
import "code.thetadev.de/TSGRain/SEBRAUC/src/util/mode"
const (
RebootCmd = "shutdown -r 0"
RaucCmd = "rauc"
Mode = mode.Prod
appmode = mode.Prod
)

View file

@ -3,11 +3,11 @@
package util
import "code.thetadev.de/TSGRain/SEBRAUC/src/server/mode"
import "code.thetadev.de/TSGRain/SEBRAUC/src/util/mode"
const (
RebootCmd = "touch /tmp/sebrauc_reboot_test"
RaucCmd = "go run code.thetadev.de/TSGRain/SEBRAUC/src/fixtures/rauc_mock"
Mode = mode.Dev
appmode = mode.Dev
)

View file

@ -6,7 +6,7 @@ import (
)
var (
ErrAlreadyRunning = errors.New("rauc already running")
ErrAlreadyRunning = HttpErrNew("rauc already running", http.StatusConflict)
ErrFileDoesNotExist = errors.New("file does not exist")
ErrPageNotFound = HttpErrNew("page not found", http.StatusNotFound)
)

View file

@ -11,17 +11,16 @@ const (
TestDev = "testdev"
)
var mode = Dev
var currentMode = Dev
// Set sets the new mode.
func Set(newMode string) {
mode = newMode
currentMode = newMode
updateGinMode()
}
// Get returns the current mode.
func Get() string {
return mode
return currentMode
}
// IsDev returns true if the current mode is dev mode.

View file

@ -1,7 +1,13 @@
package util
import "code.thetadev.de/TSGRain/SEBRAUC/src/util/mode"
var version = "dev"
func init() {
mode.Set(appmode)
}
func Version() string {
return version
}

View file

@ -23,6 +23,8 @@ type State = {
}
export default class SysinfoCard extends Component<Props, State> {
private fetchTimeout: number | undefined
constructor(props?: Props | undefined, context?: any) {
super(props, context)
this.fetchInfo()
@ -36,13 +38,12 @@ export default class SysinfoCard extends Component<Props, State> {
this.setState({sysinfo: response.data})
} else {
console.log("error fetching info", response.data)
console.log("error fetching info", response.data)
window.setTimeout(this.fetchInfo, 3000)
this.fetchTimeout = window.setTimeout(this.fetchInfo, 3000)
}
})
.catch((reason) => {
console.log("error fetching info", reason)
window.setTimeout(this.fetchInfo, 3000)
this.fetchTimeout = window.setTimeout(this.fetchInfo, 3000)
})
}
@ -150,6 +151,12 @@ export default class SysinfoCard extends Component<Props, State> {
)
}
componentWillUnmount() {
if (this.fetchTimeout !== undefined) {
window.clearTimeout(this.fetchTimeout)
}
}
render() {
if (this.state.sysinfo) {
return this.renderSysinfo()

23
ui/ui_test.go Normal file
View file

@ -0,0 +1,23 @@
package ui
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func TestUI(t *testing.T) {
router := gin.New()
Register(router)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, w.Body.String(), "SEBRAUC")
assert.NotEmpty(t, w.Header().Get("Cache-Control"))
}