Make buildah's runtime directory before every buildah run, so a machine without it is not unhealthy forever (issue 1884)
mesh/merge-gate head b53b0e7e2cc4 merged into trunk 48dd41db91a6 — pass: builds mesh-build-image → ace, shanks; no bus step; every machine composes with th…
mesh/repo-check head b53b0e7e2cc4 merged into trunk 48dd41db91a6 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
mesh/check failed: unit: modules/gitea.test: ✔ the same merge, head or close announced twice has the same id, and another has another (7.92618ms) | b5…
mesh/merge-gate head b53b0e7e2cc4 merged into trunk 48dd41db91a6 — pass: builds mesh-build-image → ace, shanks; no bus step; every machine composes with th…
mesh/repo-check head b53b0e7e2cc4 merged into trunk 48dd41db91a6 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
mesh/check failed: unit: modules/gitea.test: ✔ the same merge, head or close announced twice has the same id, and another has another (7.92618ms) | b5…
The look made <store>/xdg only after `buildah --version`, which refuses first when its XDG_RUNTIME_DIR is missing: the directory was never made on ace.
This commit is contained in:
@@ -4,8 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buildstep"
|
||||
@@ -60,6 +58,11 @@ type Look struct {
|
||||
// missing is a *buildstep.Refusal naming it, so a step ends refused and the health says the same.
|
||||
func (m *Module) look(ctx context.Context) (Look, error) {
|
||||
l := Look{Account: m.Account}
|
||||
// The runtime directory comes first: buildah refuses before anything else when its XDG_RUNTIME_DIR is not there
|
||||
// (issue 1884), so a look that made it only after `buildah --version` never made it at all.
|
||||
if err := m.ensureRuntimeDir(); err != nil {
|
||||
return l, &buildstep.Refusal{Text: err.Error()}
|
||||
}
|
||||
var err error
|
||||
if l.Buildah, err = m.buildahVersion(ctx); err != nil {
|
||||
return l, &buildstep.Refusal{Text: err.Error()}
|
||||
@@ -67,11 +70,6 @@ func (m *Module) look(ctx context.Context) (Look, error) {
|
||||
if l.UIDs, l.GIDs, err = m.ranges(); err != nil {
|
||||
return l, &buildstep.Refusal{Text: err.Error(), Have: l.Buildah}
|
||||
}
|
||||
for _, d := range []string{m.Store, filepath.Join(m.Store, "xdg")} {
|
||||
if err := os.MkdirAll(d, 0o700); err != nil {
|
||||
return l, err
|
||||
}
|
||||
}
|
||||
if err := m.userNamespace(ctx); err != nil {
|
||||
return l, &buildstep.Refusal{Text: err.Error(), Have: l.Buildah}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,25 @@ func (m *Module) storeFlags() []string {
|
||||
// env is what buildah is given beyond the base environment: a runtime directory of its own inside the store, so
|
||||
// nothing is read from the account's runtime directory, and no registry credential file anywhere (the module has none
|
||||
// and REGISTRY_AUTH_FILE is never set).
|
||||
//
|
||||
// Every call makes the runtime directory first if it is not there (issue 1884): a directory removed after the last look
|
||||
// is made again before buildah runs. A failure to make it is not lost: buildah then names the directory in its own
|
||||
// error, and the next look refuses with it.
|
||||
func (m *Module) env() []string {
|
||||
return []string{"XDG_RUNTIME_DIR=" + filepath.Join(m.Store, "xdg")}
|
||||
_ = m.ensureRuntimeDir()
|
||||
return []string{"XDG_RUNTIME_DIR=" + m.runtimeDir()}
|
||||
}
|
||||
|
||||
// runtimeDir is buildah's runtime directory, inside the store.
|
||||
func (m *Module) runtimeDir() string { return filepath.Join(m.Store, "xdg") }
|
||||
|
||||
// ensureRuntimeDir makes the store and the runtime directory inside it, 0700 and under the module's own account (the
|
||||
// process runs as it), when they are not there; it changes nothing that is.
|
||||
func (m *Module) ensureRuntimeDir() error {
|
||||
for _, d := range []string{m.Store, m.runtimeDir()} {
|
||||
if err := os.MkdirAll(d, 0o700); err != nil {
|
||||
return fmt.Errorf("buildah's runtime directory %s cannot be made: %w", m.runtimeDir(), err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
)
|
||||
|
||||
// buildahLike refuses as buildah does when the XDG_RUNTIME_DIR it is given is not there (seen on ace, issue 1884):
|
||||
// "lstat <dir>: no such file or directory", before it does anything else.
|
||||
func buildahLike(t *testing.T, run forgekey.Runner, seen *[]string) forgekey.Runner {
|
||||
t.Helper()
|
||||
return func(ctx context.Context, env []string, name string, args ...string) forgekey.Ran {
|
||||
if name == "buildah" {
|
||||
for _, e := range env {
|
||||
dir, ok := strings.CutPrefix(e, "XDG_RUNTIME_DIR=")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if _, err := os.Lstat(dir); err != nil {
|
||||
*seen = append(*seen, "missing")
|
||||
return forgekey.Ran{Status: 125, Stderr: "Error: lstat " + dir + ": no such file or directory\n"}
|
||||
}
|
||||
*seen = append(*seen, "present")
|
||||
}
|
||||
}
|
||||
return run(ctx, env, name, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// 1884/AC1: given a store directory that exists without its runtime directory, when the health is looked at, then the
|
||||
// module makes the runtime directory (0700) before it runs buildah, and the health is healthy.
|
||||
func TestHealthMakesTheRuntimeDirectoryBeforeBuildah(t *testing.T) {
|
||||
f := newFake(t)
|
||||
m := moduleWith(t, f, newStates())
|
||||
var seen []string
|
||||
m.Run = buildahLike(t, m.Run, &seen)
|
||||
xdg := filepath.Join(m.Store, "xdg")
|
||||
if _, err := os.Stat(xdg); !os.IsNotExist(err) {
|
||||
t.Fatalf("the test needs a store without its runtime directory: %v", err)
|
||||
}
|
||||
a := m.Health(context.Background())
|
||||
if a.State != health.StateHealthy || !a.Healthy {
|
||||
t.Fatalf("health = %+v, want healthy", a)
|
||||
}
|
||||
st, err := os.Stat(xdg)
|
||||
if err != nil {
|
||||
t.Fatalf("the runtime directory was not made: %v", err)
|
||||
}
|
||||
if st.Mode().Perm() != 0o700 || !st.IsDir() {
|
||||
t.Errorf("the runtime directory is %v, want a directory 0700", st.Mode())
|
||||
}
|
||||
for _, s := range seen {
|
||||
if s != "present" {
|
||||
t.Fatalf("buildah ran without its runtime directory: %v", seen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1884/AC2: given a module whose runtime directory was removed after its last look, when a build step runs buildah, then
|
||||
// the runtime directory is made again before buildah runs.
|
||||
func TestEveryBuildahRunMakesTheRuntimeDirectoryAgain(t *testing.T) {
|
||||
f := newFake(t)
|
||||
m := moduleWith(t, f, newStates())
|
||||
var seen []string
|
||||
m.Run = buildahLike(t, m.Run, &seen)
|
||||
if err := m.Check(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.RemoveAll(filepath.Join(m.Store, "xdg")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
seen = nil
|
||||
m.remove("localhost/x:y")
|
||||
if len(seen) != 1 || seen[0] != "present" {
|
||||
t.Fatalf("buildah's runs saw their runtime directory %v, want [present]", seen)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user