Compare commits

..

10 Commits

Author SHA1 Message Date
Michael Stapelberg
9a9519186b integration: add 'gok update' test
Some checks failed
gokrazy CI / CI (macos-latest) (push) Has been cancelled
gokrazy CI / CI (ubuntu-latest) (push) Has been cancelled
gokrazy CI / CI (windows-latest) (push) Has been cancelled
2025-10-03 19:12:17 +02:00
Michael Stapelberg
5675dbb2eb packer: set Accept header (request), not Content-Type (response) 2025-10-03 19:11:55 +02:00
Michael Stapelberg
ab29b81133 tests: use t.Chdir now that we are on Go 1.24 2025-10-03 18:32:43 +02:00
Michael Stapelberg
19d09ffb32 switch from breml/rootcerts to x/crypto fallback bundle
related to https://github.com/golang/go/issues/69898
2025-09-12 09:34:42 +02:00
dependabot[bot]
8143336564 build(deps): bump golang.org/x/oauth2 from 0.23.0 to 0.27.0 (#93)
---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-version: 0.27.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-19 10:26:58 +02:00
Michael Stapelberg
2979dc9b26 pull in latest gokrazy/updater 2025-07-05 15:59:45 +02:00
Michael Stapelberg
ab76ef531d update to latest gokrazy/updater (now needs a context) 2025-06-01 08:57:36 +02:00
Michael Stapelberg
2e390edb12 gok vm run: print a tip for how to enable the serial console
This should make it a little easier for users.
2025-05-30 19:00:00 +02:00
Michael Stapelberg
d145a5b689 go.mod: update to latest versions of dependencies 2025-05-30 16:58:56 +02:00
Michael Stapelberg
a0558bc6a0 GitHub Actions: verify gok builds on Windows and macOS, too
related to https://github.com/gokrazy/tools/issues/89
2025-05-29 17:03:18 +02:00
15 changed files with 311 additions and 81 deletions

View File

@@ -7,8 +7,14 @@ on:
jobs:
build:
name: build-and-test
runs-on: ubuntu-latest
name: CI
strategy:
matrix:
os:
- macos-latest
- ubuntu-latest
- windows-latest
runs-on: ${{ matrix.os }}
steps:
- name: Check out code into the Go module directory
@@ -21,14 +27,19 @@ jobs:
id: go
- name: Ensure all files were formatted as per gofmt
if: matrix.os == 'ubuntu-latest'
run: |
[ "$(gofmt -l $(find . -name '*.go') 2>&1)" = "" ]
- name: Build and run tests
- name: Build
run: |
go install -mod=mod ./cmd/...
- name: Run tests
if: matrix.os == 'ubuntu-latest'
# TestRelativeParentDir verifies breakglass.authorized_keys
# is correctly included, and the gok CLI only creates that
# file when it finds SSH keys.
run: |
mkdir -p ~/.ssh && echo dummy > ~/.ssh/id_ed25519.pub
go install -mod=mod ./cmd/...
go test -mod=mod -v ./...

22
go.mod
View File

@@ -1,24 +1,26 @@
module github.com/gokrazy/tools
go 1.24
go 1.24.0
toolchain go1.24.6
require (
github.com/breml/rootcerts v0.2.20
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0
github.com/gokrazy/gokapi v0.0.0-20250222080418-e140e9c461d8
github.com/gokrazy/internal v0.0.0-20250520205945-c2e4e2b4f611
github.com/gokrazy/updater v0.0.0-20230215172637-813ccc7f21e2
github.com/gokrazy/internal v0.0.0-20250526201501-559979153369
github.com/gokrazy/updater v0.0.0-20250705135802-db129c40879c
github.com/google/renameio/v2 v2.0.0
github.com/mattn/go-isatty v0.0.20
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
golang.org/x/mod v0.23.0
golang.org/x/sync v0.1.0
golang.org/x/sys v0.28.0
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
golang.org/x/crypto/x509roots/fallback v0.0.0-20250911151450-96dc232fbd79
golang.org/x/mod v0.24.0
golang.org/x/sync v0.14.0
golang.org/x/sys v0.33.0
)
require (
github.com/antihax/optional v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
)

39
go.sum
View File

@@ -1,38 +1,37 @@
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/breml/rootcerts v0.2.20 h1:koth1lShwiiDp3VOX6/4qKEZ87S7HgDKsnDr47XEIq0=
github.com/breml/rootcerts v0.2.20/go.mod h1:S/PKh+4d1HUn4HQovEB8hPJZO6pUZYrIhmXBhsegfXw=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 h1:C7t6eeMaEQVy6e8CarIhscYQlNmw5e3G36y7l7Y21Ao=
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw=
github.com/gokrazy/gokapi v0.0.0-20250222080418-e140e9c461d8 h1:BvyzTtbpz1GCGD35Z3G/ZR0nK0j3Fh+dRCCso+w3RKE=
github.com/gokrazy/gokapi v0.0.0-20250222080418-e140e9c461d8/go.mod h1:rVItujrJo0NpYZhFR5dYdzLDqMoMCtjEZkdxoCRDo+o=
github.com/gokrazy/internal v0.0.0-20250520205945-c2e4e2b4f611 h1:BcmhkIKeIsw5xGheRGOCj97zjevG+VImWiP2/XGF2Gg=
github.com/gokrazy/internal v0.0.0-20250520205945-c2e4e2b4f611/go.mod h1:dQY4EMkD4L5ZjYJ0SPtpgYbV7MIUMCxNIXiOfnZ6jP4=
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/gokrazy/internal v0.0.0-20250526201501-559979153369 h1:aNni2iPwJbowfHW1SFapKLfY+ZPUIcBfFrJvYPAh3p4=
github.com/gokrazy/internal v0.0.0-20250526201501-559979153369/go.mod h1:dQY4EMkD4L5ZjYJ0SPtpgYbV7MIUMCxNIXiOfnZ6jP4=
github.com/gokrazy/updater v0.0.0-20250705135802-db129c40879c h1:j4/v9FR/cOy6nog5rmXUtauBsOU3mm+rTPn5IENUbmg=
github.com/gokrazy/updater v0.0.0-20250705135802-db129c40879c/go.mod h1:EtAn+BPibqnAHnYGj3FW5e284xNsiOOMOL2dJiwu7H4=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/crypto/x509roots/fallback v0.0.0-20250911151450-96dc232fbd79 h1:WZWglxfb13JCTbJyKY1pk0V94spHxJzMAQW29INytRQ=
golang.org/x/crypto/x509roots/fallback v0.0.0-20250911151450-96dc232fbd79/go.mod h1:MEIPiCnxvQEjA4astfaKItNwEVZA5Ki+3+nyGbJ5N18=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,197 @@
package gokupdate_test
import (
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"hash"
"hash/crc32"
"io"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"github.com/gokrazy/internal/config"
"github.com/gokrazy/tools/gok"
"github.com/gokrazy/tools/internal/packer"
)
type gokrazyTestInstance struct {
configDir string
}
func (inst *gokrazyTestInstance) writeConfig(t *testing.T, basename, content string) {
t.Helper()
fn := filepath.Join(inst.configDir, basename)
if err := os.WriteFile(fn, []byte(content), 0600); err != nil {
t.Fatal(err)
}
}
func writeGokrazyInstance(t *testing.T) *gokrazyTestInstance {
t.Helper()
// Redirect os.UserConfigDir() to a temporary directory under our
// control. gokrazy always uses a path under os.UserConfigDir().
var configDir string
switch runtime.GOOS {
case "linux":
configHomeDir := t.TempDir()
os.Setenv("XDG_CONFIG_HOME", configHomeDir)
// where linux looks:
configDir = filepath.Join(configHomeDir, "gokrazy")
case "darwin":
homeDir := t.TempDir()
os.Setenv("HOME", homeDir)
// where darwin looks:
configDir = filepath.Join(homeDir, "Library", "Application Support", "gokrazy")
default:
t.Fatalf("GOOS=%s unsupported", runtime.GOOS)
}
if err := os.MkdirAll(configDir, 0755); err != nil {
t.Fatal(err)
}
return &gokrazyTestInstance{
configDir: configDir,
}
}
func TestGokUpdate(t *testing.T) {
// Run this whole test in a throw-away temporary directory to not litter the
// gokrazy/tools repository working copy.
t.Chdir(t.TempDir())
_ = writeGokrazyInstance(t)
// TODO: run the gokrazy instance in a VM instead of providing a fake
// implementation of the update protocol.
mux := http.NewServeMux()
mux.HandleFunc("/update/features", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "not found", http.StatusNotFound)
})
mux.HandleFunc("/update/", func(w http.ResponseWriter, r *http.Request) {
// accept whatever for now.
var hash hash.Hash
switch r.Header.Get("X-Gokrazy-Update-Hash") {
case "crc32":
hash = crc32.NewIEEE()
default:
hash = sha256.New()
}
if _, err := io.Copy(hash, r.Body); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "%x", hash.Sum(nil))
})
mux.HandleFunc("/reboot", func(w http.ResponseWriter, r *http.Request) {
// you got it, boss!
})
mux.HandleFunc("/uploadtemp/", func(w http.ResponseWriter, r *http.Request) {
log.Printf("[HTTP] uploadtemp: %s", r.URL.Path)
})
mux.HandleFunc("/divert", func(w http.ResponseWriter, r *http.Request) {
log.Printf("[HTTP] divert: %s to %s",
r.FormValue("path"),
r.FormValue("diversion"))
})
mux.HandleFunc("/log", func(w http.ResponseWriter, r *http.Request) {
log.Printf("[HTTP] log: %s", r.FormValue("path"))
w.Header().Set("Content-type", "text/event-stream")
if r.FormValue("stream") == "stdout" {
const text = "Hello Sun"
line := fmt.Sprintf("data: %s\n", text)
if _, err := fmt.Fprintln(w, line); err != nil {
return
}
}
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
select {}
})
fakeBuildTimestamp := "fake-" + time.Now().Format(time.RFC3339)
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(strings.ToLower(r.Header.Get("Accept")), "application/json") {
status := struct {
BuildTimestamp string `json:"BuildTimestamp"`
}{
BuildTimestamp: fakeBuildTimestamp,
}
b, err := json.Marshal(&status)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(b)
return
}
http.Error(w, "handler not implemented", http.StatusNotImplemented)
})
srv := httptest.NewServer(mux)
u, err := url.Parse(srv.URL)
if err != nil {
t.Fatal(err)
}
// create a new instance
c := gok.Context{
Args: []string{
"--parent_dir", "gokrazy",
"-i", "hello",
"new",
},
}
t.Logf("running %q", append([]string{"<gok>"}, c.Args...))
if err := c.Execute(context.Background()); err != nil {
t.Fatalf("%v: %v", c.Args, err)
}
// update the instance config to speak to the test server
const configPath = "gokrazy/hello/config.json"
b, err := os.ReadFile(configPath)
if err != nil {
t.Fatal(err)
}
var cfg config.Struct
if err := json.Unmarshal(b, &cfg); err != nil {
t.Fatal(err)
}
cfg.Update.Hostname = "localhost"
cfg.Update.HTTPPort = u.Port()
t.Logf("Updated cfg.Update = %+v", cfg.Update)
b, err = cfg.FormatForFile()
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(configPath, b, 0644); err != nil {
t.Fatal(err)
}
// verify overwrite works (i.e. locates extrafiles)
ctx := context.WithValue(context.Background(), packer.BuildTimestampOverride, fakeBuildTimestamp)
c = gok.Context{
Args: []string{
"--parent_dir", "gokrazy",
"-i", "hello",
"update",
},
}
t.Logf("running %q", append([]string{"<gok>"}, c.Args...))
if err := c.Execute(ctx); err != nil {
t.Fatalf("%v: %v", c.Args, err)
}
}

View File

@@ -15,9 +15,10 @@ import (
func TestNonModuleFiles(t *testing.T) {
// Run this whole test in a throw-away temporary directory to not litter the
// gokrazy/tools repository working copy.
parent := t.TempDir()
t.Chdir(t.TempDir())
// create a new instance
parent := t.TempDir()
c := gok.Context{
Args: []string{
"--parent_dir=" + parent,

View File

@@ -13,22 +13,7 @@ import (
func TestRelativeParentDir(t *testing.T) {
// Run this whole test in a throw-away temporary directory to not litter the
// gokrazy/tools repository working copy.
// TODO(go1.24): use t.Chdir()
oldwd, err := os.Open(".")
if err != nil {
t.Fatal(err)
}
if err := os.Chdir(t.TempDir()); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
err := oldwd.Chdir()
oldwd.Close()
if err != nil {
t.Fatal(err)
}
})
t.Chdir(t.TempDir())
// create a new instance
c := gok.Context{

View File

@@ -151,7 +151,7 @@ func (r *overwriteImplConfig) run(ctx context.Context, args []string, stdout, st
Output: &output,
}
pack.Main("gokrazy gok")
pack.Main(ctx, "gokrazy gok")
return nil
}

View File

@@ -127,7 +127,7 @@ func (r *runImplConfig) run(ctx context.Context, args []string, stdout, stderr i
return err
}
target, err := updater.NewTarget(updateBaseUrl.String(), httpClient)
target, err := updater.NewTarget(ctx, updateBaseUrl.String(), httpClient)
if err != nil {
return fmt.Errorf("checking target partuuid support: %v", err)
}
@@ -153,7 +153,7 @@ func (r *runImplConfig) run(ctx context.Context, args []string, stdout, stderr i
{
start := time.Now()
err := target.Put("uploadtemp/gok-run/"+basename, io.TeeReader(f, &progress.Writer{}))
err := target.Put(ctx, "uploadtemp/gok-run/"+basename, io.TeeReader(f, &progress.Writer{}))
if err != nil {
return fmt.Errorf("uploading temporary binary: %v", err)
}
@@ -171,6 +171,7 @@ func (r *runImplConfig) run(ctx context.Context, args []string, stdout, stderr i
// /user/<basename>. Includes an automatic service restart.
{
err := target.Divert(
ctx,
"/user/"+basename,
"gok-run/"+basename,
cfg.PackageConfig[importPath].CommandLineFlags,

View File

@@ -80,7 +80,7 @@ func (r *sbomConfig) run(ctx context.Context, args []string, stdout, stderr io.W
Cfg: cfg,
}
sbomMarshaled, sbomWithHash, err := pack.GenerateSBOM()
sbomMarshaled, sbomWithHash, err := pack.GenerateSBOM(ctx)
if os.IsNotExist(err) {
// Common case, handle with a good error message
os.Stderr.WriteString("\n")

View File

@@ -90,7 +90,7 @@ func (r *updateImplConfig) run(ctx context.Context, args []string, stdout, stder
Cfg: cfg,
}
pack.Main("gokrazy gok")
pack.Main(ctx, "gokrazy gok")
return nil
}

View File

@@ -76,11 +76,7 @@ func init() {
instanceflag.RegisterPflags(vmRunCmd.Flags())
}
func (r *vmRunConfig) buildFullDiskImage(ctx context.Context, dest string) error {
fileCfg, err := config.ApplyInstanceFlag()
if err != nil {
return err
}
func (r *vmRunConfig) buildFullDiskImage(ctx context.Context, dest string, fileCfg *config.Struct) error {
if r.arch != "" {
os.Setenv("GOARCH", r.arch)
@@ -137,7 +133,7 @@ func (r *vmRunConfig) buildFullDiskImage(ctx context.Context, dest string) error
Output: &output,
}
pack.Main("gokrazy gok")
pack.Main(ctx, "gokrazy gok")
return nil
}
@@ -231,6 +227,22 @@ func (r *vmRunConfig) runQEMU(ctx context.Context, fullDiskImage string, extraAr
}
func (r *vmRunConfig) run(ctx context.Context, args []string, stdout, stderr io.Writer) error {
fileCfg, err := config.ApplyInstanceFlag()
if err != nil {
return err
}
if fileCfg.SerialConsole == "disabled" {
// The serial console is disabled by default:
// https://gokrazy.org/userguide/instance-config/#serialconsole
// 'gok vm run' currently launches QEMU such that
// there is a serial0 monitor available, but no HDMI.
// Hence, print a tip for how to get the serial console to work.
log.Printf("")
log.Printf(` Tip: Your config.json disables the serial console. Set "SerialConsole": "ttyAMA0,115200", then select View -> serial0 in QEMU to access an interactive shell for debugging.`)
log.Printf("")
}
f, err := os.CreateTemp("", "gokrazy-vm")
if err != nil {
return err
@@ -241,7 +253,7 @@ func (r *vmRunConfig) run(ctx context.Context, args []string, stdout, stderr io.
fdi := f.Name()
log.Printf("building disk image")
if !r.dry {
if err := r.buildFullDiskImage(ctx, fdi); err != nil {
if err := r.buildFullDiskImage(ctx, fdi, fileCfg); err != nil {
return err
}
}

View File

@@ -1,10 +1,11 @@
package packer
import (
"encoding/pem"
"os"
"path/filepath"
"github.com/breml/rootcerts/embedded"
"golang.org/x/crypto/x509roots/fallback/bundle"
)
func (pack *Pack) findSystemCertsPEM() (string, error) {
@@ -14,6 +15,7 @@ func (pack *Pack) findSystemCertsPEM() (string, error) {
defer func() {
log.Printf("Loading system CA certificates from %s", source)
}()
// On Linux, we can copy the operating systems certificate store.
// certFiles is defined in cacerts_linux.go (or defined as empty in
// cacertsstub.go on non-Linux):
@@ -37,7 +39,18 @@ func (pack *Pack) findSystemCertsPEM() (string, error) {
return string(b), nil
}
// Fall back to github.com/breml/rootcerts, i.e. the bundled Mozilla CA list:
source = "bundled Mozilla CA list"
return embedded.MozillaCACertificatesPEM(), nil
// Fall back to the x/crypto fallback bundle root certificates:
source = "bundled x509roots/fallback/bundle"
return xrf(), nil
}
func xrf() string {
var certs []byte
for c := range bundle.Roots() {
certs = append(certs, pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: c.Certificate,
})...)
}
return string(certs)
}

View File

@@ -37,6 +37,10 @@ import (
"github.com/gokrazy/updater"
)
type contextKey int
var BuildTimestampOverride contextKey
const MB = 1024 * 1024
type filePathAndModTime struct {
@@ -1099,7 +1103,7 @@ func filterGoEnv(env []string) []string {
return relevant
}
func (pack *Pack) logicPrepare(programName string, sbomHook func(marshaled []byte, withHash SBOMWithHash)) error {
func (pack *Pack) logicPrepare(ctx context.Context, programName string, sbomHook func(marshaled []byte, withHash SBOMWithHash)) error {
log := pack.Env.Logger()
cfg := pack.Cfg
updateflag.SetUpdate(cfg.InternalCompatibilityFlags.Update)
@@ -1178,6 +1182,9 @@ func (pack *Pack) logicPrepare(programName string, sbomHook func(marshaled []byt
log.Printf("Build target: %s", strings.Join(filterGoEnv(packer.Env()), " "))
pack.buildTimestamp = time.Now().Format(time.RFC3339)
if ts, ok := ctx.Value(BuildTimestampOverride).(string); ok {
pack.buildTimestamp = ts
}
log.Printf("Build timestamp: %s", pack.buildTimestamp)
systemCertsPEM, err := pack.findSystemCertsPEM()
@@ -1630,7 +1637,7 @@ func (pack *Pack) logicBuild(programName string, sbomHook func(marshaled []byte,
return nil
}
func (pack *Pack) logic(programName string, sbomHook func(marshaled []byte, withHash SBOMWithHash)) error {
func (pack *Pack) logic(ctx context.Context, programName string, sbomHook func(marshaled []byte, withHash SBOMWithHash)) error {
dnsCheck := make(chan error)
go func() {
defer close(dnsCheck)
@@ -1646,7 +1653,7 @@ func (pack *Pack) logic(programName string, sbomHook func(marshaled []byte, with
dnsCheck <- nil
}()
if err := pack.logicPrepare(programName, sbomHook); err != nil {
if err := pack.logicPrepare(ctx, programName, sbomHook); err != nil {
return err
}
@@ -1669,6 +1676,7 @@ func (pack *Pack) logic(programName string, sbomHook func(marshaled []byte, with
}
func (pack *Pack) logicWrite(programName string, sbomHook func(marshaled []byte, withHash SBOMWithHash), bindir string, dnsCheck chan error) error {
ctx := context.Background()
log := pack.Env.Logger()
var (
@@ -1718,7 +1726,7 @@ func (pack *Pack) logicWrite(programName string, sbomHook func(marshaled []byte,
}
updateBaseUrl.Path = "/"
target, err = updater.NewTarget(updateBaseUrl.String(), updateHttpClient)
target, err = updater.NewTarget(ctx, updateBaseUrl.String(), updateHttpClient)
if err != nil {
return fmt.Errorf("checking target partuuid support: %v", err)
}
@@ -2035,7 +2043,7 @@ func (pack *Pack) logicWrite(programName string, sbomHook func(marshaled []byte,
return err
}
if err := target.StreamTo("mbr", mbrReader); err != nil {
if err := target.StreamTo(ctx, "mbr", mbrReader); err != nil {
if err == updater.ErrUpdateHandlerNotImplemented {
log.Printf("target does not support updating MBR yet, ignoring")
} else {
@@ -2044,11 +2052,11 @@ func (pack *Pack) logicWrite(programName string, sbomHook func(marshaled []byte,
}
if cfg.InternalCompatibilityFlags.Testboot {
if err := target.Testboot(); err != nil {
if err := target.Testboot(ctx); err != nil {
return fmt.Errorf("enable testboot of non-active partition: %v", err)
}
} else {
if err := target.Switch(); err != nil {
if err := target.Switch(ctx); err != nil {
return fmt.Errorf("switching to non-active partition: %v", err)
}
}
@@ -2057,7 +2065,7 @@ func (pack *Pack) logicWrite(programName string, sbomHook func(marshaled []byte,
canc()
log.Printf("Triggering reboot")
if err := target.Reboot(); err != nil {
if err := target.Reboot(ctx); err != nil {
if errors.Is(err, syscall.ECONNRESET) {
log.Printf("ignoring reboot error: %v", err)
} else {
@@ -2159,6 +2167,7 @@ func (pack *Pack) validateTargetArchMatchesKernel() error {
}
func (pack *Pack) updateWithProgress(prog *progress.Reporter, reader io.Reader, target *updater.Target, logStr string, stream string) error {
ctx := context.Background()
log := pack.Env.Logger()
start := time.Now()
@@ -2170,7 +2179,7 @@ func (pack *Pack) updateWithProgress(prog *progress.Reporter, reader io.Reader,
prog.SetTotal(uint64(st.Size()))
}
}
if err := target.StreamTo(stream, io.TeeReader(reader, &progress.Writer{})); err != nil {
if err := target.StreamTo(ctx, stream, io.TeeReader(reader, &progress.Writer{})); err != nil {
return fmt.Errorf("updating %s: %w", logStr, err)
}
duration := time.Since(start)
@@ -2184,17 +2193,17 @@ func (pack *Pack) updateWithProgress(prog *progress.Reporter, reader io.Reader,
return nil
}
func (pack *Pack) Main(programName string) {
if err := pack.logic(programName, nil); err != nil {
func (pack *Pack) Main(ctx context.Context, programName string) {
if err := pack.logic(ctx, programName, nil); err != nil {
fmt.Fprintf(os.Stderr, "ERROR:\n %s\n", err)
os.Exit(1)
}
}
func (pack *Pack) GenerateSBOM() ([]byte, SBOMWithHash, error) {
func (pack *Pack) GenerateSBOM(ctx context.Context) ([]byte, SBOMWithHash, error) {
var sbom []byte
var sbomWithHash SBOMWithHash
if err := pack.logic("gokrazy gok", func(b []byte, wh SBOMWithHash) {
if err := pack.logic(ctx, "gokrazy gok", func(b []byte, wh SBOMWithHash) {
sbom = b
sbomWithHash = wh
}); err != nil {

View File

@@ -19,7 +19,7 @@ func pollUpdated1(ctx context.Context, updateHttpClient *http.Client, updateBase
return err
}
req = req.WithContext(ctx)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
resp, err := updateHttpClient.Do(req)
if err != nil {
return err