Skip to content

Commit

Permalink
use gokapi types (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
damdo authored Mar 5, 2023
1 parent 57e2c63 commit 9160316
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 114 deletions.
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ module github.com/gokrazy/selfupdate
go 1.20

require (
github.com/antihax/optional v1.0.0
github.com/gokrazy/gokapi v0.0.0-20230221202227-fc8991bfa4ab
github.com/gokrazy/gokrazy v0.0.0-20230215184006-9cdaabefeec8
github.com/gokrazy/updater v0.0.0-20230215172637-813ccc7f21e2
)

require (
github.com/gokrazy/internal v0.0.0-20220129150711-9ed298107648 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
github.com/mdlayher/watchdog v0.0.0-20201005150459-8bdc4f41966b // indirect
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
golang.org/x/net v0.6.0 // indirect
golang.org/x/oauth2 v0.5.0 // indirect
golang.org/x/sys v0.5.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.0 // indirect
)
32 changes: 29 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/gokrazy/gokapi v0.0.0-20230221202227-fc8991bfa4ab h1:w40XuAWHVKBrglRpyTGDCxn6LgTB2IhrAjRadZuYkVk=
github.com/gokrazy/gokapi v0.0.0-20230221202227-fc8991bfa4ab/go.mod h1:gsfT8xJ3ocl55b26EKYqBihG/Z5iu/9AP/6YlaAd16U=
github.com/gokrazy/gokrazy v0.0.0-20230215184006-9cdaabefeec8 h1:S2Ac5RfoKzqB8GAT8lHHHniwkGvnqTcCugwbb4fwPFY=
github.com/gokrazy/gokrazy v0.0.0-20230215184006-9cdaabefeec8/go.mod h1:v4yQTOzEIpUmkKHYGMfqhktZXwvaxUpc2VfFTMyHAYI=
github.com/gokrazy/internal v0.0.0-20220129150711-9ed298107648 h1:kBuLicM0xJw3xEe4607WlnzGL+qSwPqdyh5/LUiCdq0=
github.com/gokrazy/internal v0.0.0-20220129150711-9ed298107648/go.mod h1:Gc9sU6yJ/qxg3gJZ1pjfcTAULa0swdTa4TH51g1e00E=
github.com/gokrazy/updater v0.0.0-20230215172637-813ccc7f21e2 h1:kBY5R1tSf+EYZ+QaSrofLaVJtBqYsVNVBWkdMq3Smcg=
github.com/gokrazy/updater v0.0.0-20230215172637-813ccc7f21e2/go.mod h1:PYOvzGOL4nlBmuxu7IyKQTFLaxr61+WPRNRzVtuYOHw=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/mdlayher/watchdog v0.0.0-20201005150459-8bdc4f41966b h1:7tUBfsEEBWfFeHOB7CUfoOamak+Gx/BlirfXyPk1WjI=
github.com/mdlayher/watchdog v0.0.0-20201005150459-8bdc4f41966b/go.mod h1:bmoJUS6qOA3uKFvF3KVuhf7mU1KQirzQMeHXtPyKEqg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201005065044-765f4ea38db3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/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=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
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.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
25 changes: 20 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import (
"fmt"
"log"
"math/rand"
"net/url"
"os"
"os/signal"
"syscall"
"time"

"github.com/gokrazy/gokapi/gusapi"
"github.com/gokrazy/gokrazy"
)

Expand All @@ -21,6 +23,10 @@ type opts struct {
skipWaiting bool
}

const (
serverAPIPath = "/api/v1"
)

func main() {
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()
Expand Down Expand Up @@ -68,9 +74,18 @@ func logic(ctx context.Context, o opts) error {
return fmt.Errorf("could read neither /perm/http-port.txt, nor /etc/http-port.txt, nor /http-port.txt: %s", err.Error())
}

gusBasePath, err := url.JoinPath(o.gusServer, serverAPIPath)
if err != nil {
return fmt.Errorf("error joining gus server url: %w", err)
}

gusCfg := gusapi.NewConfiguration()
gusCfg.BasePath = gusBasePath
gusCli := gusapi.NewAPIClient(gusCfg)

if o.skipWaiting {
log.Print("skipping waiting, performing an immediate updateProcess")
if err := updateProcess(ctx, &updateRequest{MachineID: machineID, SBOMHash: sbomHash}, o.gusServer, sbomHash, o.destinationDir, httpPassword, httpPort); err != nil {
if err := updateProcess(ctx, gusCli, machineID, o.gusServer, sbomHash, o.destinationDir, httpPassword, httpPort); err != nil {
// If the updateProcess fails we exit with an error
// so that gokrazy supervisor will restart the process.
return fmt.Errorf("error performing updateProcess: %v", err)
Expand All @@ -93,16 +108,16 @@ func logic(ctx context.Context, o opts) error {
case <-ticker.C:
jitter := time.Duration(rand.Int63n(250)) * time.Second
time.Sleep(jitter)
if err := updateProcess(ctx, &updateRequest{MachineID: machineID, SBOMHash: sbomHash}, o.gusServer, sbomHash, o.destinationDir, httpPassword, httpPort); err != nil {
if err := updateProcess(ctx, gusCli, machineID, sbomHash, o.gusServer, o.destinationDir, httpPassword, httpPort); err != nil {
log.Printf("error performing updateProcess: %v", err)
continue
}
}
}
}

func updateProcess(ctx context.Context, upReq *updateRequest, gusServer, sbomHash, destinationDir, httpPassword, httpPort string) error {
response, err := checkForUpdates(ctx, gusServer, upReq)
func updateProcess(ctx context.Context, gusCli *gusapi.APIClient, machineID, gusServer, sbomHash, destinationDir, httpPassword, httpPort string) error {
response, err := checkForUpdates(ctx, gusCli, machineID)
if err != nil {
return fmt.Errorf("unable to check for updates: %w", err)
}
Expand All @@ -114,7 +129,7 @@ func updateProcess(ctx context.Context, upReq *updateRequest, gusServer, sbomHas
}

// The SBOMHash differs, start the selfupdate procedure.
if err := selfupdate(ctx, gusServer, destinationDir, upReq, response, httpPassword, httpPort); err != nil {
if err := selfupdate(ctx, gusCli, gusServer, machineID, destinationDir, response, httpPassword, httpPort); err != nil {
return fmt.Errorf("unable to perform the selfupdate procedure: %w", err)
}

Expand Down
125 changes: 23 additions & 102 deletions selfupdate.go
Original file line number Diff line number Diff line change
@@ -1,129 +1,50 @@
package main

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"

"github.com/antihax/optional"
"github.com/gokrazy/gokapi/gusapi"
"github.com/gokrazy/updater"
)

type updateRequest struct {
MachineID string `json:"machine_id"`
SBOMHash string `json:"sbom_hash"`
}

type attemptRequest struct {
MachineID string `json:"machine_id"`
SBOMHash string `json:"sbom_hash"`
}

type updateResponse struct {
SBOMHash string `json:"sbom_hash"`
RegistryType string `json:"registry_type"`
Link string `json:"download_link"`
}

const (
updateAPI = "api/v1/update"
attemptUpdateAPI = "api/v1/attempt"
)

func registerUpdateAttempt(ctx context.Context, gusServer string, request *attemptRequest) error {
reqBody, err := json.Marshal(request)
if err != nil {
return fmt.Errorf("error json encoding request: %w", err)
}

attemptEndpoint, err := url.JoinPath(gusServer, attemptUpdateAPI)
if err != nil {
return fmt.Errorf("error joining attempt endpoint: %w", err)
}

req, err := http.NewRequestWithContext(ctx, "POST", attemptEndpoint, bytes.NewBuffer(reqBody))
if err != nil {
return fmt.Errorf("error creating http request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("error making http request: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected HTTP status code: %v", resp.Status)
}
return nil
}

func checkForUpdates(ctx context.Context, gusServer string, request *updateRequest) (*updateResponse, error) {
reqBody, err := json.Marshal(request)
if err != nil {
return nil, fmt.Errorf("error json encoding request: %w", err)
}

updateEndpoint, err := url.JoinPath(gusServer, updateAPI)
if err != nil {
return nil, fmt.Errorf("error joining update endpoint: %w", err)
}

req, err := http.NewRequestWithContext(ctx, "POST", updateEndpoint, bytes.NewBuffer(reqBody))
func checkForUpdates(ctx context.Context, gusCli *gusapi.APIClient, machineID string) (gusapi.UpdateResponse, error) {
response, _, err := gusCli.UpdateApi.Update(ctx, &gusapi.UpdateApiUpdateOpts{
Body: optional.NewInterface(&gusapi.UpdateRequest{
MachineId: machineID,
}),
})
if err != nil {
return nil, fmt.Errorf("error creating http request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making http request: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected HTTP status code: %v", resp.Status)
}

respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading http response: %w", err)
}

var response updateResponse
if err := json.Unmarshal(respBody, &response); err != nil {
return nil, fmt.Errorf("error json decoding response: %w", err)
return gusapi.UpdateResponse{}, fmt.Errorf("error making http request: %w", err)
}

return &response, nil
return response, nil
}

func shouldUpdate(response *updateResponse, sbomHash string) bool {
if response.SBOMHash == sbomHash {
log.Printf("device's gokrazy version: %s is already the desired one, skipping", response.SBOMHash)
func shouldUpdate(response gusapi.UpdateResponse, sbomHash string) bool {
if response.SbomHash == sbomHash {
log.Printf("device's gokrazy version: %s is already the desired one, skipping", response.SbomHash)
return false
}

log.Printf("device's gokrazy version: %s, desired version: %s, proceeding with the update", sbomHash, response.SBOMHash)
log.Printf("device's gokrazy version: %s, desired version: %s, proceeding with the update", sbomHash, response.SbomHash)

return true
}

func selfupdate(ctx context.Context, gusServer, destinationDir string, request *updateRequest, response *updateResponse, httpPassword, httpPort string) error {
func selfupdate(ctx context.Context, gusCli *gusapi.APIClient, gusServer, machineID, destinationDir string, response gusapi.UpdateResponse, httpPassword, httpPort string) error {
log.Print("starting self-update procedure")

attemptReq := attemptRequest{MachineID: request.MachineID, SBOMHash: response.SBOMHash}
if err := registerUpdateAttempt(ctx, gusServer, &attemptReq); err != nil {
return fmt.Errorf("error registering update attempt to %q: %w", gusServer, err)
if _, _, err := gusCli.UpdateApi.Attempt(ctx, &gusapi.UpdateApiAttemptOpts{
Body: optional.NewInterface(&gusapi.AttemptRequest{
MachineId: machineID,
SbomHash: response.SbomHash,
}),
}); err != nil {
return fmt.Errorf("error registering update attempt: %w", err)
}

var readClosers rcs
Expand All @@ -133,7 +54,7 @@ func selfupdate(ctx context.Context, gusServer, destinationDir string, request *
case "http", "localdisk":
readClosers, err = httpFetcher(response, gusServer, destinationDir)
if err != nil {
return fmt.Errorf("error fetching %q update from link %q: %w", response.RegistryType, response.Link, err)
return fmt.Errorf("error fetching %q update from link %q: %w", response.RegistryType, response.DownloadLink, err)
}
default:
return fmt.Errorf("unrecognized registry type %q", response.RegistryType)
Expand Down
8 changes: 5 additions & 3 deletions update_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"path/filepath"
"strconv"
"syscall"

"github.com/gokrazy/gokapi/gusapi"
)

const (
Expand All @@ -27,14 +29,14 @@ type rcs struct {
}

// httpFetcher handles a http update link.
func httpFetcher(response *updateResponse, gusServer, destinationDir string) (rcs, error) {
func httpFetcher(response gusapi.UpdateResponse, gusServer, destinationDir string) (rcs, error) {
// The link may be a relative url if the server's backend registry is its local disk.
// Ensure we have an absolute url by adding the base (gusServer) url
// when necessary.
link, err := ensureAbsoluteHTTPLink(gusServer, response.Link)
link, err := ensureAbsoluteHTTPLink(gusServer, response.DownloadLink)
if err != nil {
return rcs{}, fmt.Errorf("error ensuring absolute HTTP link %q + %q: %w",
gusServer, response.Link, err)
gusServer, response.DownloadLink, err)
}

if err := os.MkdirAll(destinationDir, 0755); err != nil {
Expand Down

0 comments on commit 9160316

Please sign in to comment.