From b7d13a2d094104a2d3a095493281cdb06931ba98 Mon Sep 17 00:00:00 2001 From: maxmcd Date: Sun, 7 Mar 2021 11:22:01 -0500 Subject: [PATCH 1/8] real progress w/ Go --- bramble.lock | 1 + lib/go/build.sh | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/bramble.lock b/bramble.lock index 306a8e95..4ed6a28d 100644 --- a/bramble.lock +++ b/bramble.lock @@ -2,4 +2,5 @@ "http://tarballs.nixos.org/stdenv-linux/x86_64/c5aabb0d603e2c1ea05f5a93b3be82437f5ebf31/bootstrap-tools.tar.xz" = "a5ce9c155ed09397614646c9717fc7cd94b1023d7b76b618d409e4fefd6e9d39" "https://brmbl.s3.amazonaws.com/busybox-x86_64.tar.gz" = "2ae410370b8e9113968ffa6e52f38eea7f17df5f436bd6a69cc41c6ca01541a1" "https://brmbl.s3.amazonaws.com/patchelf.tar.gz" = "67ee6623207754a18d81624d630d9addbf6234ab1e6c44ddba9179621720f960" + "https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz" = "f4ff5b5eb3a3cae1c993723f3eab519c5bae18866b5e5f96fe1102f0cb5c3e52" "https://maxmcd.com/" = "b17315f3eac25dc23a59cc0ec2820c78a0b9890f7fea5a44deaef5a3c6cd9e59" diff --git a/lib/go/build.sh b/lib/go/build.sh index 04e6d0db..e2bc83bc 100644 --- a/lib/go/build.sh +++ b/lib/go/build.sh @@ -5,15 +5,16 @@ export PATH=$stdenv/bin:$busybox/bin export LD_LIBRARY_PATH=$stdenv/lib mkdir -p /var/tmp +mkdir /tmp cp -r $go1_4/go . +include=$(pwd)/go/include cd ./go/src ls -lah ./cmd/dist -export GO_CCFLAGS="-I$stdenv/include-glibc" -export CC="gcc -I$stdenv/include-glibc -Wl,-rpath=$stdenv/lib " -export GOROOT_BOOTSTRAP=$(pwd) +export GO_CCFLAGS="-L $stdenv/lib -I $include -I $stdenv/include-glibc -I $stdenv/include -Wl,-rpath=$stdenv/lib -Wl,--dynamic-linker=$stdenv/lib/ld-linux-x86-64.so.2 " +export CC="gcc -L $stdenv/lib -I $include -I $stdenv/include-glibc -I $stdenv/include -Wl,-rpath=$stdenv/lib -Wl,--dynamic-linker=$stdenv/lib/ld-linux-x86-64.so.2 " +# export GOROOT_BOOTSTRAP=$(pwd) export CGO_ENABLED="0" -which sh sed -i 's/set -e/set -ex/g' ./make.bash # cat ./make.bash bash ./make.bash From 62a34b4e4f2f049a8769c2f38aeafab0b8d31403 Mon Sep 17 00:00:00 2001 From: maxmcd Date: Sun, 7 Mar 2021 14:42:34 -0500 Subject: [PATCH 2/8] use a pty for sandbox, add bramble shell --- Makefile | 3 +- go.mod | 4 +- go.sum | 8 ++- notes/22-bramble-run.md | 0 pkg/bramble/bramble.go | 87 ++++++++++++++++++++++++++------- pkg/bramble/cli.go | 43 +++++++++++----- pkg/bramble/config.go | 2 +- pkg/bramble/integration_test.go | 4 +- pkg/logger/logger.go | 10 +++- pkg/sandbox/sandbox.go | 59 +++++++++++++++++++--- 10 files changed, 175 insertions(+), 45 deletions(-) create mode 100644 notes/22-bramble-run.md diff --git a/Makefile b/Makefile index 030c02c4..c91ea7e1 100644 --- a/Makefile +++ b/Makefile @@ -9,9 +9,10 @@ go_test: install # just use LICENSE as a file we can harmlessly "touch" and use as a cache marker LICENSE: main.go pkg/*/*.go go install + make build_setuid touch LICENSE -install: LICENSE build_setuid +install: LICENSE integration_test: install env BRAMBLE_INTEGRATION_TEST=truthy go test -v ./pkg/bramble/ diff --git a/go.mod b/go.mod index 1637841f..7e2f83d3 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ require ( github.com/BurntSushi/toml v0.3.1 github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 github.com/containerd/console v1.0.0 + github.com/creack/pty v1.1.11 + github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23 github.com/fsouza/go-dockerclient v1.6.5 github.com/go-git/go-git/v5 v5.2.0 github.com/hashicorp/terraform v0.14.4 @@ -22,7 +24,7 @@ require ( go.starlark.net v0.0.0-20200901195727-6e684ef5eeee go.uber.org/zap v1.10.0 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect - golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 + golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e google.golang.org/grpc v1.34.0 // indirect ) diff --git a/go.sum b/go.sum index 5fb781c8..5490de4c 100644 --- a/go.sum +++ b/go.sum @@ -181,7 +181,10 @@ github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/daixiang0/gci v0.2.4/go.mod h1:+AV8KmHTGxxwp/pY84TLQfFKp2vuKXXJVzF3kD/hfR4= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -517,6 +520,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 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= @@ -1006,8 +1010,8 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM= -golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b h1:ggRgirZABFolTmi3sn6Ivd9SipZwLedQ5wR0aAKnFxU= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/notes/22-bramble-run.md b/notes/22-bramble-run.md new file mode 100644 index 00000000..e69de29b diff --git a/pkg/bramble/bramble.go b/pkg/bramble/bramble.go index 05c8f472..b9eeb8e6 100644 --- a/pkg/bramble/bramble.go +++ b/pkg/bramble/bramble.go @@ -132,7 +132,7 @@ func (b *Bramble) buildDerivationIfNew(ctx context.Context, drv *Derivation) (er logger.Print("Building derivation", filename) logger.Debugw(drv.prettyJSON()) - if err = b.buildDerivation(ctx, drv); err != nil { + if err = b.buildDerivation(ctx, drv, false); err != nil { return errors.Wrap(err, "error building "+filename) } return b.writeDerivation(drv) @@ -161,7 +161,7 @@ func (b *Bramble) hashAndMoveFetchURL(ctx context.Context, drv *Derivation, outp return } -func (b *Bramble) buildDerivation(ctx context.Context, drv *Derivation) (err error) { +func (b *Bramble) buildDerivation(ctx context.Context, drv *Derivation, shell bool) (err error) { var task *trace.Task ctx, task = trace.NewTask(ctx, "buildDerivation") defer task.End() @@ -187,13 +187,18 @@ func (b *Bramble) buildDerivation(ctx context.Context, drv *Derivation) (err err if err != nil { return } + + if shell && (drv.Builder == "fetch_url" || drv.Builder == "fetch_git") { + return errors.New("can't spawn a shell with a builtin builder") + } + switch drv.Builder { case "fetch_url": err = b.fetchURLBuilder(ctx, drvCopy, outputPaths) case "fetch_git": err = b.fetchGitBuilder(ctx, drvCopy, outputPaths) default: - err = b.regularBuilder(ctx, drvCopy, buildDir, outputPaths) + err = b.regularBuilder(ctx, drvCopy, buildDir, outputPaths, shell) } if err != nil { return @@ -505,7 +510,8 @@ func (b *Bramble) dockerRegularBuilder(ctx context.Context, drv *Derivation, bui return b.runDockerBuild(ctx, drv.filename(), options) } -func (b *Bramble) regularBuilder(ctx context.Context, drv *Derivation, buildDir string, outputPaths map[string]string) (err error) { +func (b *Bramble) regularBuilder(ctx context.Context, drv *Derivation, buildDir string, + outputPaths map[string]string, shell bool) (err error) { builderLocation := drv.Builder if _, err := os.Stat(builderLocation); err != nil { return errors.Wrap(err, "builder location doesn't exist") @@ -534,7 +540,6 @@ func (b *Bramble) regularBuilder(ctx context.Context, drv *Derivation, buildDir sbx := sandbox.Sandbox{ Path: builderLocation, Args: drv.Args, - Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, UserID: uid, @@ -544,6 +549,10 @@ func (b *Bramble) regularBuilder(ctx context.Context, drv *Derivation, buildDir Dir: filepath.Join(buildDir, drv.BuildContextRelativePath), Mounts: mounts, } + if shell { + sbx.Args = nil + sbx.Stdin = os.Stdin + } return sbx.Run(ctx) } @@ -748,7 +757,7 @@ func (b *Bramble) assembleDerivationDependencyGraph(dos DerivationOutputs) *Acyc return graph } -func (b *Bramble) buildDerivationOutputs(dos DerivationOutputs) (err error) { +func (b *Bramble) buildDerivationOutputs(ctx context.Context, dos DerivationOutputs, skipDerivation *Derivation) (err error) { graph := b.assembleDerivationDependencyGraph(dos) var wg sync.WaitGroup errChan := make(chan error) @@ -757,7 +766,7 @@ func (b *Bramble) buildDerivationOutputs(dos DerivationOutputs) (err error) { if err = graph.Validate(); err != nil { return err } - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(ctx) go func() { graph.Walk(func(v dag.Vertex) (_ tfdiags.Diagnostics) { if errored { @@ -777,9 +786,13 @@ func (b *Bramble) buildDerivationOutputs(dos DerivationOutputs) (err error) { if drv.Output(do.OutputName).Path != "" { return } + if skipDerivation != nil && skipDerivation.filename() == drv.filename() { + return + } wg.Add(1) if err := b.buildDerivationIfNew(ctx, drv); err != nil { - // Passing the error might block, so we need an explicit Done call here. + // Passing the error might block, so we need an explicit Done + // call here. wg.Done() errored = true logger.Print(err) @@ -794,7 +807,8 @@ func (b *Bramble) buildDerivationOutputs(dos DerivationOutputs) (err error) { err = <-errChan cancel() // Call cancel on the context, no-op if nothing is running if err != nil { - // If we receive an error cancel the context and wait for any jobs that are running. + // If we receive an error cancel the context and wait for any jobs that + // are running. wg.Wait() } return err @@ -975,10 +989,11 @@ func (b *Bramble) repl(_ []string) (err error) { return nil } -func (b *Bramble) build(args []string) (err error) { +func (b *Bramble) parseBuildArg(cmd string, args []string) (module, fn string, derivations []*Derivation, err error) { if len(args) == 0 { - logger.Print(`"bramble build" requires 1 argument`) - return flag.ErrHelp + logger.Printfln(`"bramble %s" requires 1 argument`, cmd) + err = flag.ErrHelp + return } if err = b.init(); err != nil { @@ -987,8 +1002,7 @@ func (b *Bramble) build(args []string) (err error) { // parse something like ./tests:foo into the correct module and function // name - module, fn, err := b.parseModuleFuncArgument(args) - if err != nil { + if module, fn, err = b.parseModuleFuncArgument(args); err != nil { return } @@ -999,23 +1013,58 @@ func (b *Bramble) build(args []string) (err error) { } toCall, ok := globals[fn] if !ok { - return errors.Errorf("function %q not found in module %q", fn, module) + err = errors.Errorf("function %q not found in module %q", fn, module) + return } values, err := starlark.Call(&starlark.Thread{}, toCall, nil, nil) if err != nil { - return errors.Wrap(err, "error running") + err = errors.Wrap(err, "error running") + return } // The function must return a single derivation or a list of derivations, or // a tuple of derivations. We turn them into an array. - returnedDerivations := valuesToDerivations(values) + derivations = valuesToDerivations(values) + return +} + +func (b *Bramble) shell(ctx context.Context, args []string) (err error) { + module, fn, derivations, err := b.parseBuildArg("build", args) + if err != nil { + return err + } + if len(derivations) > 1 { + return errors.New(`cannot run "bramble shell" with a function that returns multiple derivations`) + } + shellDerivation := derivations[0] + + if err = b.buildDerivationOutputs(ctx, b.derivationsToDerivationOutputs(derivations), shellDerivation); err != nil { + return + } - if err = b.buildDerivationOutputs(b.derivationsToDerivationOutputs(returnedDerivations)); err != nil { + if err := b.writeConfigMetadata(derivations, module, fn); err != nil { + return err + } + filename := shellDerivation.filename() + logger.Print("Launching shell for derivation", filename) + logger.Debugw(shellDerivation.prettyJSON()) + if err = b.buildDerivation(ctx, shellDerivation, true); err != nil { + return errors.Wrap(err, "error spawning "+filename) + } + return nil +} + +func (b *Bramble) build(ctx context.Context, args []string) (err error) { + module, fn, derivations, err := b.parseBuildArg("build", args) + if err != nil { + return err + } + if err = b.buildDerivationOutputs(ctx, b.derivationsToDerivationOutputs(derivations), nil); err != nil { return } - return b.writeConfigMetadata(returnedDerivations, fn, module) + return b.writeConfigMetadata(derivations, module, fn) } func valuesToDerivations(values starlark.Value) (derivations []*Derivation) { diff --git a/pkg/bramble/cli.go b/pkg/bramble/cli.go index 176b4d61..398c2a7a 100644 --- a/pkg/bramble/cli.go +++ b/pkg/bramble/cli.go @@ -20,10 +20,11 @@ var ( BrambleFunctionBuildHiddenCommand = "__bramble-function-build" ) -func (b *Bramble) createCLI() *ffcli.Command { +func (b *Bramble) createAndParseCLI(args []string) (*ffcli.Command, error) { var ( root *ffcli.Command repl *ffcli.Command + shell *ffcli.Command build *ffcli.Command store *ffcli.Command storeGC *ffcli.Command @@ -39,7 +40,15 @@ func (b *Bramble) createCLI() *ffcli.Command { ShortUsage: "bramble build [options] [module]: [args...]", ShortHelp: "Build a function", LongHelp: "Build a function", - Exec: func(ctx context.Context, args []string) error { return b.build(args) }, + Exec: func(ctx context.Context, args []string) error { return b.build(ctx, args) }, + } + + shell = &ffcli.Command{ + Name: "shell", + ShortUsage: "bramble shell [options] [module]: [args...]", + ShortHelp: "Open a shell from a derivation", + LongHelp: "Open a shell from a derivation", + Exec: func(ctx context.Context, args []string) error { return b.shell(ctx, args) }, } repl = &ffcli.Command{ @@ -86,8 +95,8 @@ func (b *Bramble) createCLI() *ffcli.Command { } root = &ffcli.Command{ - ShortUsage: "bramble [--version] [--help] []", - Subcommands: []*ffcli.Command{build, repl, store, derivation}, + ShortUsage: "bramble [flags] []", + Subcommands: []*ffcli.Command{build, repl, store, shell, derivation}, FlagSet: rootFlagSet, UsageFunc: DefaultUsageFunc, Exec: func(ctx context.Context, args []string) error { @@ -99,10 +108,6 @@ func (b *Bramble) createCLI() *ffcli.Command { }, } - if *verbose { - logger.SetDebugLogger() - } - // Recursively patch all command descriptions and usage functions var fixup func(*ffcli.Command) fixup = func(cmd *ffcli.Command) { @@ -114,7 +119,15 @@ func (b *Bramble) createCLI() *ffcli.Command { } } fixup(root) - return root + + if err := root.Parse(args); err != nil { + return nil, err + } + if *verbose { + logger.SetDebugLogger() + } + + return root, nil } // RunCLI runs the cli with os.Args @@ -123,8 +136,7 @@ func RunCLI() { log.SetOutput(ioutil.Discard) b := &Bramble{} - command := b.createCLI() - if err := command.ParseAndRun(context.Background(), os.Args[1:]); err != nil { + handleErr := func(err error) { if err == errQuiet { os.Exit(1) } @@ -134,6 +146,15 @@ func RunCLI() { fmt.Fprint(os.Stderr, starutil.AnnotateError(err)) os.Exit(1) } + + command, err := b.createAndParseCLI(os.Args[1:]) + if err != nil { + handleErr(err) + } + // TODO, use context to handle interrupt + if err := command.Run(context.Background()); err != nil { + handleErr(err) + } } func DefaultUsageFunc(c *ffcli.Command) string { diff --git a/pkg/bramble/config.go b/pkg/bramble/config.go index a3240672..dd06bd38 100644 --- a/pkg/bramble/config.go +++ b/pkg/bramble/config.go @@ -58,7 +58,7 @@ type LockFile struct { URLHashes map[string]string } -func (b *Bramble) writeConfigMetadata(derivations []*Derivation, fn, module string) (err error) { +func (b *Bramble) writeConfigMetadata(derivations []*Derivation, module, fn string) (err error) { outputs := []string{} for _, drv := range b.inputDerivations { outputs = append(outputs, drv.Filename+":"+drv.OutputName) diff --git a/pkg/bramble/integration_test.go b/pkg/bramble/integration_test.go index 43856857..17babf37 100644 --- a/pkg/bramble/integration_test.go +++ b/pkg/bramble/integration_test.go @@ -2,6 +2,7 @@ package bramble import ( "bufio" + "context" "fmt" "io" "io/ioutil" @@ -88,13 +89,14 @@ func assembleModules(t *testing.T) []string { func runBrambleRun(args []string) error { // ensure $GOPATH/bin is in PATH + // we set this so we can use the setuid binary, maybe there is a better way path := os.Getenv("PATH") gobin := filepath.Join(os.Getenv("GOPATH"), "bin") if !strings.Contains(path, gobin) { os.Setenv("PATH", path+":"+gobin) } b := Bramble{} - return b.build(args) + return b.build(context.Background(), args) } func TestIntegrationRunAlmostAllPublicFunctions(t *testing.T) { diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 2dce7cea..1b4f1d45 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -12,14 +12,21 @@ func newDebugLogger() *zap.SugaredLogger { } var ( - Logger = newInfoLogger() + Logger = newDebugLogger() Debugw = Logger.Debugw Debug = Logger.Debug Info = Logger.Info ) +func resetGlobals() { + Debugw = Logger.Debugw + Debug = Logger.Debug + Info = Logger.Info +} + func SetDebugLogger() { Logger = newDebugLogger() + resetGlobals() } func newInfoLogger() *zap.SugaredLogger { @@ -31,6 +38,7 @@ func newInfoLogger() *zap.SugaredLogger { func SetInfoLogger() { Logger = newInfoLogger() + resetGlobals() } func Print(a ...interface{}) { diff --git a/pkg/sandbox/sandbox.go b/pkg/sandbox/sandbox.go index f5017bd8..936c7663 100644 --- a/pkg/sandbox/sandbox.go +++ b/pkg/sandbox/sandbox.go @@ -6,12 +6,15 @@ import ( "encoding/json" "fmt" "io" + "log" "os" "os/exec" "os/signal" "path/filepath" "syscall" + "github.com/creack/pty" + "github.com/docker/docker/pkg/term" "github.com/maxmcd/bramble/pkg/logger" "github.com/maxmcd/bramble/pkg/store" "github.com/maxmcd/gosh/shell" @@ -168,6 +171,11 @@ func parseSerializedArg(arg string) (s Sandbox, err error) { // Run runs the sandbox until execution has been completed func (s Sandbox) Run(ctx context.Context) (err error) { + + if term.IsTerminal(os.Stdin.Fd()) { + fmt.Println("is terminal") + } + serialized, err := s.serializeArg() if err != nil { return err @@ -209,7 +217,8 @@ func (s Sandbox) Run(ctx context.Context) (err error) { } return err } - return nil + return nil // Start the command with a pty. + } func (s Sandbox) newNamespaceStep() (err error) { @@ -242,18 +251,52 @@ func (s Sandbox) newNamespaceStep() (err error) { // interrupt will be caught be the child process and the process // will exiting, causing this process to exit ignoreInterrupt() + cmd := &exec.Cmd{ - Path: selfExe, - Args: []string{setupStepArg, serialized}, - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, + Path: selfExe, + Args: []string{setupStepArg, serialized}, SysProcAttr: &syscall.SysProcAttr{ - Pdeathsig: unix.SIGTERM, // ??? + // maybe sigint will allow the child more time to clean up its mounts???? + Pdeathsig: unix.SIGINT, Cloneflags: cloneFlags, }, } - return errors.Wrap(cmd.Run(), "error running newNamespace") + + // We must use a pty here to enable interactive input. If we naively pass + // os.Stdin to an exec.Cmd then we run into issues with the parent and + // child terminals getting confused about who is supposed to process various + // control signals. + // We can then just set to raw and copy the bytes across. We could remove + // the pty entirely for jobs that don't pass a terminal as a stdin. + ptmx, err := pty.Start(cmd) + if err != nil { + return errors.Wrap(err, "error starting pty") + } + defer func() { _ = ptmx.Close() }() + // Handle pty resize + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGWINCH) + go func() { + for range ch { + if err := pty.InheritSize(os.Stdin, ptmx); err != nil { + log.Printf("error resizing pty: %s", err) + } + } + }() + ch <- syscall.SIGWINCH // Initial resize. + + // only handle stdin and set raw if it's an interactive terminal + if os.Stdin != nil && term.IsTerminal(os.Stdin.Fd()) { + oldState, err := term.MakeRaw(os.Stdin.Fd()) + if err != nil { + return err + } + // restore when complete + defer func() { _ = term.RestoreTerminal(os.Stdin.Fd(), oldState) }() + go func() { _, _ = io.Copy(ptmx, os.Stdin) }() + } + _, _ = io.Copy(os.Stdout, ptmx) + return errors.Wrap(cmd.Wait(), "error running newNamespace") } func (s Sandbox) setupStep() (err error) { From 1e926926f91f3c38ec33ae6d3f8b08cee3ae0ca5 Mon Sep 17 00:00:00 2001 From: maxmcd Date: Sun, 7 Mar 2021 14:46:38 -0500 Subject: [PATCH 3/8] remove interactive shell shim --- pkg/sandbox/sandbox.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/pkg/sandbox/sandbox.go b/pkg/sandbox/sandbox.go index 936c7663..9e20aa58 100644 --- a/pkg/sandbox/sandbox.go +++ b/pkg/sandbox/sandbox.go @@ -396,20 +396,6 @@ func (s Sandbox) runExecStep() { Stderr: os.Stderr, } if err := cmd.Run(); err != nil { - path := "/home/maxm/bramble/bramble_store_padding/bramble_/ieqjuyrfv7lmdb2bur76jgjcrd33j7na/bin/sh" - cmd := exec.Cmd{ - Path: path, - Dir: s.Dir, - Args: []string{path}, - Env: os.Environ(), - - // We don't use the passed sandbox stdio because - // it's been passed to the very first run command - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, - } - cmd.Run() fmt.Println(err.Error()) os.Exit(1) } From 91f1a7b2ae8fea6c993a570cef632d3379b141e3 Mon Sep 17 00:00:00 2001 From: maxmcd Date: Sun, 7 Mar 2021 14:48:14 -0500 Subject: [PATCH 4/8] remove function sandbox functionality --- go.mod | 1 - go.sum | 2 - pkg/sandbox/sandbox.go | 99 +++++++----------------------------------- 3 files changed, 15 insertions(+), 87 deletions(-) diff --git a/go.mod b/go.mod index 7e2f83d3..96028d16 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/hashicorp/terraform v0.14.4 github.com/imdario/mergo v0.3.11 // indirect github.com/jaguilar/vt100 v0.0.0-20201024211400-81de19cb81a4 - github.com/maxmcd/gosh v0.2.1-0.20210228220323-59420ac4567a github.com/mholt/archiver/v3 v3.3.1-0.20200626164424-d44471c49aa7 github.com/moby/moby v1.13.1 github.com/morikuni/aec v1.0.0 diff --git a/go.sum b/go.sum index 5490de4c..47d25a0c 100644 --- a/go.sum +++ b/go.sum @@ -574,8 +574,6 @@ github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpe github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/maxmcd/archiver/v3 v3.3.2-0.20200926140316-5fd9d38b8b8b h1:15C098O6LZ6+6bQT0aR8xsihI/E9wOnTdx4BDWsq5uY= github.com/maxmcd/archiver/v3 v3.3.2-0.20200926140316-5fd9d38b8b8b/go.mod h1:wZCaCDpKnb7vsqOlgW3WO756DciCRSCOZCVMkXkrxfs= -github.com/maxmcd/gosh v0.2.1-0.20210228220323-59420ac4567a h1:3CbQNZrLMH+bskfzm3zwD8mVZzVT4a9iUqz/plrpGcQ= -github.com/maxmcd/gosh v0.2.1-0.20210228220323-59420ac4567a/go.mod h1:/79yP5yHdovdNxLUSD8MaswCgQe7Fl1gV0Js1nLmiq8= github.com/maxmcd/starlark-go v0.0.0-20201021154825-b2f805d0d122 h1:5ntUQ6qQLi3wcHdxEVWud2Q5z11Pj6cQFA6OHbCvbRU= github.com/maxmcd/starlark-go v0.0.0-20201021154825-b2f805d0d122/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU= github.com/miekg/dns v1.0.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= diff --git a/pkg/sandbox/sandbox.go b/pkg/sandbox/sandbox.go index 9e20aa58..aa56d436 100644 --- a/pkg/sandbox/sandbox.go +++ b/pkg/sandbox/sandbox.go @@ -16,8 +16,6 @@ import ( "github.com/creack/pty" "github.com/docker/docker/pkg/term" "github.com/maxmcd/bramble/pkg/logger" - "github.com/maxmcd/bramble/pkg/store" - "github.com/maxmcd/gosh/shell" "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -74,63 +72,6 @@ func entrypoint() (err error) { } } -// DebugFunction is a sandbox function that launches a rudimentary shell -var DebugFunction = RegisterFunction(func() { - shell.Run() -}) - -// RunDebug launches a rudimentary shell within a sandbox -func RunDebug() (err error) { - store, err := store.NewStore() - if err != nil { - return err - } - chrootPath, err := store.TempBuildDir() - if err != nil { - return err - } - s := &Sandbox{ - ChrootPath: chrootPath, - Function: DebugFunction, - Stdin: os.Stdin, - Stderr: os.Stderr, - Stdout: os.Stdout, - Mounts: []string{store.StorePath + ":ro"}, - } - return s.Run(context.Background()) -} - -type Function struct { - index int -} - -func (f Function) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Index int `json:"index"` - }{ - Index: f.index, - }) -} -func (f *Function) UnmarshalJSON(data []byte) error { - aux := struct { - Index int `json:"index"` - }{} - err := json.Unmarshal(data, &aux) - f.index = aux.Index - return err -} - -var registeredFunctions = []func(){} - -// RegisterFunction can be used to register a Go function that you want to run -// in a sandbox instead of an external command. RegisterFunction must be -// called at the very beginning of your executable. -func RegisterFunction(fn func()) Function { - index := len(registeredFunctions) + 1 // zero is null - registeredFunctions = append(registeredFunctions, fn) - return Function{index: index} -} - // Sandbox defines a command or function that you want to run in a sandbox type Sandbox struct { Stdin io.Reader `json:"-"` @@ -145,10 +86,6 @@ type Sandbox struct { UserID int GroupID int - // Function can reference a function that has been created with - // RegisterFunction. TODO: fix overloading of function and Path - // functionality - Function Function // Bind mounts or directories the process should have access too. These // should be absolute paths. If a mount is intended to be readonly add // ":ro" to the end of the path like `/tmp:ro` @@ -378,26 +315,20 @@ func interruptContext() context.Context { } func (s Sandbox) runExecStep() { - if s.Function.index != 0 { - // TODO: env - // TODO: dir - registeredFunctions[s.Function.index-1]() - } else { - cmd := exec.Cmd{ - Path: s.Path, - Dir: s.Dir, - Args: append([]string{s.Path}, s.Args...), - Env: os.Environ(), - - // We don't use the passed sandbox stdio because - // it's been passed to the very first run command - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, - } - if err := cmd.Run(); err != nil { - fmt.Println(err.Error()) - os.Exit(1) - } + cmd := exec.Cmd{ + Path: s.Path, + Dir: s.Dir, + Args: append([]string{s.Path}, s.Args...), + Env: os.Environ(), + + // We don't use the passed sandbox stdio because + // it's been passed to the very first run command + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + } + if err := cmd.Run(); err != nil { + fmt.Println(err.Error()) + os.Exit(1) } } From 232f17dde19f28138991ff3869bc02817707ca12 Mon Sep 17 00:00:00 2001 From: maxmcd Date: Sun, 7 Mar 2021 18:58:09 -0500 Subject: [PATCH 5/8] failed attempt at using minijail --- notes/06-sandboxing.md | 5 ++++- notes/06-sandboxing/minijail.sh | 34 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 notes/06-sandboxing/minijail.sh diff --git a/notes/06-sandboxing.md b/notes/06-sandboxing.md index dec54728..7ad837b9 100644 --- a/notes/06-sandboxing.md +++ b/notes/06-sandboxing.md @@ -23,8 +23,11 @@ If bramble is going to have this level of flexibility it should be sandboxed by - https://github.com/NixOS/nix/issues/1429 - nix disallows setuid - https://bazel.build/designs/2016/06/02/sandboxing.html Support docker as an option, would work only for linux or just for running things in docker. +- https://unix.stackexchange.com/questions/6433/how-to-jail-a-process-without-being-root interesting notes ## Linux -Default mounts: https://github.com/opencontainers/runtime-tools/blob/a7974a4078764ec41acf5feaa05f07854af44aa6/generate/generate.go#L174-L211 +- Default mounts: https://github.com/opencontainers/runtime-tools/blob/a7974a4078764ec41acf5feaa05f07854af44aa6/generate/generate.go#L174-L211 +- Create dev/null https://www.commandlinefu.com/commands/view/24199/create-devnull-if-accidentally-deleted-or-for-a-chroot +- container linux with mknod examples https://github.com/cloudify-incubator/cloudify-rest-go-client/blob/f8139d8e38b0909fae3e4212eb05497483c0e5b8/container/container_linux.go diff --git a/notes/06-sandboxing/minijail.sh b/notes/06-sandboxing/minijail.sh new file mode 100644 index 00000000..fe50cfe6 --- /dev/null +++ b/notes/06-sandboxing/minijail.sh @@ -0,0 +1,34 @@ +set -x +args=( + -T static # we want minijail to assume a static binary in case the binary can't use LD_PRELOAD + # -U # new user namespace + -C /tmp/bramble-chroot-713833583 # our chroot location + # -N # new cgroup namespace + # -p # new pid namespace + --mount-dev # mount a minimal dev + -u maxm # set user id + -e + -g users # set group id + # -v # new vfs namespace ??? + -b /home/maxm/bramble/bramble_store_padding/bramble_ + -b /home/maxm/bramble/bramble_store_padding/bramble_/bramble_build_directory121331829 1 + -b /home/maxm/bramble/bramble_store_padding/bramble_/bramble_build_directory463911248 1 + /home/maxm/bramble/bramble_store_padding/bramble_/ieqjuyrfv7lmdb2bur76jgjcrd33j7na/bin/sh +) +mkdir -p /tmp/bramble-chroot-713833583/proc +mkdir -p /tmp/bramble-chroot-713833583/dev +mkdir -p /tmp/bramble-chroot-713833583/var +# use this args hack so that we can comment +sudo strace minijail0 "${args[@]}" + + +# this doesn't work, seems hung up on directory permissions unless +# I make my homedir readable +# even after that it exists with a "file not found" error that I don't understand + +# with strace it seems to be having trouble finding a library: +# ¯\_(ツ)_/¯ +# +# stat("/nix/store/gafigwfaimlziam6qhw1m8dz4h952g1n-glibc-2.32-35/lib/x86_64", 0x7ffd90b68830) = -1 ENOENT (No such file or directory) +# openat(AT_FDCWD, "/nix/store/ah4h9wpsz8yvrfnlk7ldm97q131b1di7-libcap-2.46-lib/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) +# wait4(27918, libminijail[27918]: execve(1) failed: No such file or directory From b06d9a2de28b0552be6a6197451c1a90ae2f0a20 Mon Sep 17 00:00:00 2001 From: maxmcd Date: Tue, 9 Mar 2021 21:55:38 -0500 Subject: [PATCH 6/8] add /dev /dev/null /dev/random /dev/urandom; add rust-like logging parsing --- lib/go/build.sh | 1 - lib/go/default.bramble | 6 +- pkg/logger/logger.go | 94 +++++++++++++++++++++++++++++- pkg/logger/logger_test.go | 117 ++++++++++++++++++++++++++++++++++++++ pkg/sandbox/chrootenv.go | 38 +++++++++++-- 5 files changed, 247 insertions(+), 9 deletions(-) create mode 100644 pkg/logger/logger_test.go diff --git a/lib/go/build.sh b/lib/go/build.sh index e2bc83bc..b341d284 100644 --- a/lib/go/build.sh +++ b/lib/go/build.sh @@ -5,7 +5,6 @@ export PATH=$stdenv/bin:$busybox/bin export LD_LIBRARY_PATH=$stdenv/lib mkdir -p /var/tmp -mkdir /tmp cp -r $go1_4/go . include=$(pwd)/go/include diff --git a/lib/go/default.bramble b/lib/go/default.bramble index 80b5ae25..ba0b0c2a 100644 --- a/lib/go/default.bramble +++ b/lib/go/default.bramble @@ -5,11 +5,13 @@ load("github.com/maxmcd/bramble/lib") def _bootstrap(): go1_4 = std.fetch_url("https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz") - + path = "%s/bin:%s/bin" % (nix_seed.stdenv(), lib.busybox()) return derivation( name="go-1.4", builder=lib.busybox().out + "/bin/sh", args=["./build.sh"], sources=["./build.sh"], - env=dict(go1_4=go1_4, stdenv=nix_seed.stdenv(), busybox=lib.busybox()), + env=dict( + go1_4=go1_4, stdenv=nix_seed.stdenv(), busybox=lib.busybox(), PATH=path + ), ) diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 1b4f1d45..d6478a0a 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -2,15 +2,28 @@ package logger import ( "fmt" + "os" + "strings" "go.uber.org/zap" + "go.uber.org/zap/buffer" + "go.uber.org/zap/zapcore" ) func newDebugLogger() *zap.SugaredLogger { - logger, _ := zap.NewDevelopment() + cfg := zap.NewDevelopmentConfig() + cfg.Encoding = "conditional" + _ = zap.RegisterEncoder("conditional", newModuleEncoder) + cfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel) + logger, _ := cfg.Build() return logger.Sugar() } +func init() { + // BRAMBLE_LOG=level=module + os.Getenv("BRAMBLE_LOG") +} + var ( Logger = newDebugLogger() Debugw = Logger.Debugw @@ -29,8 +42,87 @@ func SetDebugLogger() { resetGlobals() } +func newModuleEncoder(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) { + me := moduleEncoder{ + Encoder: zapcore.NewJSONEncoder(cfg), + level: zapcore.ErrorLevel, + modules: map[string]zapcore.Level{}, + } + val := os.Getenv("BRAMBLE_LOG") + if val == "" { + return me, nil + } + lower := strings.ToLower(val) + + stringToLevel := func(str string) (zapcore.Level, bool) { + for _, lvl := range []zapcore.Level{ + zapcore.DebugLevel, + zapcore.InfoLevel, + zapcore.WarnLevel, + zapcore.ErrorLevel, + zapcore.PanicLevel, + zapcore.FatalLevel} { + if str == lvl.String() { + return lvl, true + } + } + if str == "off" { + return zapcore.DebugLevel - 1, true + } + return 0, false + } + + // "error,hello=warn" + for _, match := range strings.Split(lower, ",") { + lvl, found := stringToLevel(match) + switch { + case found: // this is just a level + me.level = lvl + case !strings.Contains(match, "="): // no equal and no level, so just a module name + me.modules[match] = zapcore.DebugLevel + default: // it's the module=level syntax, ignore if malformed + parts := strings.Split(match, "=") + if len(parts) == 2 { + module, lvlString := parts[0], parts[1] + if lvl, found := stringToLevel(lvlString); found { + me.modules[module] = lvl + } + } + } + } + return me, nil +} + +type moduleEncoder struct { + zapcore.Encoder + level zapcore.Level + modules map[string]zapcore.Level +} + +func (me moduleEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) { + line, err := me.Encoder.EncodeEntry(entry, fields) + effectiveLevel := me.level + moduleWithFileAndLine := entry.Caller.TrimmedPath() + if moduleWithFileAndLine != "undefined" { + idx := strings.IndexRune(moduleWithFileAndLine, '/') + if idx > 0 { + moduleName := moduleWithFileAndLine[:idx] + if lvl, found := me.modules[moduleName]; found { + effectiveLevel = lvl + } + } + } + if entry.Level < effectiveLevel { + line.Reset() + return line, err + } + return line, err +} + func newInfoLogger() *zap.SugaredLogger { cfg := zap.NewDevelopmentConfig() + cfg.Encoding = "conditional" + _ = zap.RegisterEncoder("conditional", newModuleEncoder) cfg.Level = zap.NewAtomicLevelAt(zap.InfoLevel) logger, _ := cfg.Build() return logger.Sugar() diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go new file mode 100644 index 00000000..0619d1bf --- /dev/null +++ b/pkg/logger/logger_test.go @@ -0,0 +1,117 @@ +package logger + +import ( + "os" + "reflect" + "strings" + "testing" + + "go.uber.org/zap/zapcore" +) + +func Test_newInfoLogger(t *testing.T) { + logger := newDebugLogger() + logger.Info("hi") + logger.Debug("hi") + + for _, match := range strings.Split("foo", ",") { + strings.Split(match, "=") + } +} + +func TestMatches(t *testing.T) { + // "hello" + // "warn" + // "WARN" + // "info" + // "INFO" + // "hello=debug" + // "hello=DEBUG" + // "hello,std::option" + // "error,hello=warn" + // "error,hello=off" + // "off" + // "OFF" +} + +func Test_newModuleEncoder(t *testing.T) { + tests := []struct { + arg string + want moduleEncoder + }{ + { + arg: "hello", + want: moduleEncoder{ + level: zapcore.ErrorLevel, + modules: map[string]zapcore.Level{ + "hello": zapcore.DebugLevel, + }, + }, + }, { + arg: "warn", + want: moduleEncoder{level: zapcore.WarnLevel}, + }, { + arg: "WARN", + want: moduleEncoder{level: zapcore.WarnLevel}, + }, { + arg: "hello=debug", + want: moduleEncoder{ + level: zapcore.ErrorLevel, + modules: map[string]zapcore.Level{ + "hello": zapcore.DebugLevel, + }, + }, + }, { + arg: "off", + want: moduleEncoder{level: zapcore.DebugLevel - 1}, + }, { + arg: "OFF", + want: moduleEncoder{level: zapcore.DebugLevel - 1}, + }, { + arg: "hello,std::option", + want: moduleEncoder{ + level: zapcore.ErrorLevel, + modules: map[string]zapcore.Level{ + "hello": zapcore.DebugLevel, + // TODO: pkg paths in go + "std::option": zapcore.DebugLevel, + }, + }, + }, { + arg: "info,hello=warn", + want: moduleEncoder{ + level: zapcore.InfoLevel, + modules: map[string]zapcore.Level{ + "hello": zapcore.WarnLevel, + }, + }, + }, { + arg: "error,hello=off", + want: moduleEncoder{ + level: zapcore.ErrorLevel, + modules: map[string]zapcore.Level{ + "hello": zapcore.DebugLevel - 1, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.arg, func(t *testing.T) { + os.Setenv("BRAMBLE_LOG", tt.arg) + got, _ := newModuleEncoder(zapcore.EncoderConfig{}) + me := got.(moduleEncoder) + meStripped := moduleEncoder{ + level: me.level, + modules: me.modules, + } + // make deepequal happy since we don't allocate the empty map + // in the test cases + if tt.want.modules == nil && len(meStripped.modules) == 0 { + meStripped.modules = nil + } + if !reflect.DeepEqual(meStripped, tt.want) { + t.Errorf("newModuleEncoder() = %v, want %v", meStripped, tt.want) + } + }) + } +} diff --git a/pkg/sandbox/chrootenv.go b/pkg/sandbox/chrootenv.go index fc7ffde8..d65b3272 100644 --- a/pkg/sandbox/chrootenv.go +++ b/pkg/sandbox/chrootenv.go @@ -37,13 +37,44 @@ func newChroot(location string, mounts []string) *chroot { } } +func makedev(major int, minor int) int { + return (minor & 0xff) | (major&0xfff)<<8 +} + func (chr *chroot) Init() (err error) { if chr.initialized { return errors.New("chroot env already initialized") } chr.initialized = true - if err := os.Mkdir(filepath.Join(chr.location, "proc"), 0755); err != nil { - return err + + // reset file mode creation mask to zero + syscall.Umask(0) + + for _, dir := range []string{"proc", "dev", "tmp"} { + if err := os.Mkdir(filepath.Join(chr.location, dir), 0755); err != nil { + return err + } + } + if err = os.Chmod(filepath.Join(chr.location, "tmp"), 0777); err != nil { + return errors.Wrap(err, "chmod tmp 0777") + } + + if err := syscall.Mknod(filepath.Join(chr.location, "dev", "/null"), + syscall.S_IFCHR|0666, makedev(1, 3)); err != nil { + return errors.Wrap(err, "mknod /dev/null") + } + if err := syscall.Mknod(filepath.Join(chr.location, "dev", "/random"), + syscall.S_IFCHR|0666, makedev(1, 8)); err != nil { + return errors.Wrap(err, "mknod /dev/random") + } + if err := syscall.Mknod(filepath.Join(chr.location, "dev", "/urandom"), + syscall.S_IFCHR|0666, makedev(1, 9)); err != nil { + return errors.Wrap(err, "mknod /dev/urandom") + } + + // mount proc + if err := syscall.Mount("proc", filepath.Join(chr.location, "proc"), "proc", 0, ""); err != nil { + return errors.Wrap(err, "proc mount") } for _, mount := range chr.mounts { src, ro, valid := parseMount(mount) @@ -84,9 +115,6 @@ func (chr *chroot) Init() (err error) { if err := os.Chdir("/"); err != nil { return errors.Wrap(err, "chdir") } - if err := syscall.Mount("proc", "proc", "proc", 0, ""); err != nil { - return errors.Wrap(err, "proc mount") - } return nil } From cbb27eaa911a1ad70c2666ecd9132b4bb9764e57 Mon Sep 17 00:00:00 2001 From: maxmcd Date: Wed, 10 Mar 2021 12:45:19 -0500 Subject: [PATCH 7/8] working Go compile! --- lib/go/build.sh | 14 +++++++++--- pkg/bramble/cli.go | 6 +---- pkg/logger/logger.go | 48 ++++++++------------------------------- pkg/logger/logger_test.go | 15 ------------ pkg/sandbox/sandbox.go | 3 +-- 5 files changed, 23 insertions(+), 63 deletions(-) diff --git a/lib/go/build.sh b/lib/go/build.sh index b341d284..855abf5f 100644 --- a/lib/go/build.sh +++ b/lib/go/build.sh @@ -6,14 +6,22 @@ export LD_LIBRARY_PATH=$stdenv/lib mkdir -p /var/tmp -cp -r $go1_4/go . +cp -r $go1_4/go $out +cd $out include=$(pwd)/go/include -cd ./go/src +cd $out/go/src ls -lah ./cmd/dist -export GO_CCFLAGS="-L $stdenv/lib -I $include -I $stdenv/include-glibc -I $stdenv/include -Wl,-rpath=$stdenv/lib -Wl,--dynamic-linker=$stdenv/lib/ld-linux-x86-64.so.2 " +export GO_LDFLAGS="-L $stdenv/lib -I $include -I $stdenv/include-glibc -I $stdenv/include" export CC="gcc -L $stdenv/lib -I $include -I $stdenv/include-glibc -I $stdenv/include -Wl,-rpath=$stdenv/lib -Wl,--dynamic-linker=$stdenv/lib/ld-linux-x86-64.so.2 " # export GOROOT_BOOTSTRAP=$(pwd) export CGO_ENABLED="0" sed -i 's/set -e/set -ex/g' ./make.bash # cat ./make.bash bash ./make.bash + +# this works for a few things, but has trouble finding the network, resolve.conf +# syslog, no go in PATH, +# stat testdata/libmach8db: no such file or directory +# stat /Users/rsc/bin/xed: no such file or directory +# stat /usr/local/bin/arm-linux-elf-objdump: no such file or directory +# ../bin/go test all diff --git a/pkg/bramble/cli.go b/pkg/bramble/cli.go index 398c2a7a..1bf3aeb4 100644 --- a/pkg/bramble/cli.go +++ b/pkg/bramble/cli.go @@ -32,7 +32,6 @@ func (b *Bramble) createAndParseCLI(args []string) (*ffcli.Command, error) { derivationBuild *ffcli.Command rootFlagSet = flag.NewFlagSet("bramble", flag.ExitOnError) version = rootFlagSet.Bool("version", false, "version") - verbose = rootFlagSet.Bool("v", false, "print verbose logs") ) build = &ffcli.Command{ @@ -123,9 +122,6 @@ func (b *Bramble) createAndParseCLI(args []string) (*ffcli.Command, error) { if err := root.Parse(args); err != nil { return nil, err } - if *verbose { - logger.SetDebugLogger() - } return root, nil } @@ -143,7 +139,7 @@ func RunCLI() { if err == flag.ErrHelp { os.Exit(127) } - fmt.Fprint(os.Stderr, starutil.AnnotateError(err)) + logger.Print(starutil.AnnotateError(err)) os.Exit(1) } diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index d6478a0a..4187c670 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -10,41 +10,27 @@ import ( "go.uber.org/zap/zapcore" ) -func newDebugLogger() *zap.SugaredLogger { +func newLogger() *zap.SugaredLogger { cfg := zap.NewDevelopmentConfig() - cfg.Encoding = "conditional" - _ = zap.RegisterEncoder("conditional", newModuleEncoder) + cfg.Encoding = "module" + _ = zap.RegisterEncoder("module", newModuleEncoder) + // this must be at debug level because we handle the level ourselves cfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel) logger, _ := cfg.Build() return logger.Sugar() } -func init() { - // BRAMBLE_LOG=level=module - os.Getenv("BRAMBLE_LOG") -} - var ( - Logger = newDebugLogger() + Logger = newLogger() Debugw = Logger.Debugw Debug = Logger.Debug Info = Logger.Info + Warn = Logger.Warn ) -func resetGlobals() { - Debugw = Logger.Debugw - Debug = Logger.Debug - Info = Logger.Info -} - -func SetDebugLogger() { - Logger = newDebugLogger() - resetGlobals() -} - func newModuleEncoder(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) { me := moduleEncoder{ - Encoder: zapcore.NewJSONEncoder(cfg), + Encoder: zapcore.NewConsoleEncoder(cfg), level: zapcore.ErrorLevel, modules: map[string]zapcore.Level{}, } @@ -113,29 +99,15 @@ func (me moduleEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) } } if entry.Level < effectiveLevel { - line.Reset() + line.Reset() // return nothing return line, err } return line, err } -func newInfoLogger() *zap.SugaredLogger { - cfg := zap.NewDevelopmentConfig() - cfg.Encoding = "conditional" - _ = zap.RegisterEncoder("conditional", newModuleEncoder) - cfg.Level = zap.NewAtomicLevelAt(zap.InfoLevel) - logger, _ := cfg.Build() - return logger.Sugar() -} - -func SetInfoLogger() { - Logger = newInfoLogger() - resetGlobals() -} - func Print(a ...interface{}) { - fmt.Println(a...) + fmt.Fprintln(os.Stderr, a...) } func Printfln(format string, a ...interface{}) { - fmt.Printf(format+"\n", a...) + fmt.Fprintf(os.Stderr, format+"\n", a...) } diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go index 0619d1bf..0b034937 100644 --- a/pkg/logger/logger_test.go +++ b/pkg/logger/logger_test.go @@ -19,21 +19,6 @@ func Test_newInfoLogger(t *testing.T) { } } -func TestMatches(t *testing.T) { - // "hello" - // "warn" - // "WARN" - // "info" - // "INFO" - // "hello=debug" - // "hello=DEBUG" - // "hello,std::option" - // "error,hello=warn" - // "error,hello=off" - // "off" - // "OFF" -} - func Test_newModuleEncoder(t *testing.T) { tests := []struct { arg string diff --git a/pkg/sandbox/sandbox.go b/pkg/sandbox/sandbox.go index aa56d436..9c4f317d 100644 --- a/pkg/sandbox/sandbox.go +++ b/pkg/sandbox/sandbox.go @@ -108,9 +108,8 @@ func parseSerializedArg(arg string) (s Sandbox, err error) { // Run runs the sandbox until execution has been completed func (s Sandbox) Run(ctx context.Context) (err error) { - if term.IsTerminal(os.Stdin.Fd()) { - fmt.Println("is terminal") + logger.Debug("is terminal") } serialized, err := s.serializeArg() From bf95505b597ba18e09f0c118fa0e82432f8c1665 Mon Sep 17 00:00:00 2001 From: maxmcd Date: Wed, 10 Mar 2021 12:49:09 -0500 Subject: [PATCH 8/8] fix logging test --- pkg/logger/logger_test.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go index 0b034937..a1dc6dbc 100644 --- a/pkg/logger/logger_test.go +++ b/pkg/logger/logger_test.go @@ -3,22 +3,11 @@ package logger import ( "os" "reflect" - "strings" "testing" "go.uber.org/zap/zapcore" ) -func Test_newInfoLogger(t *testing.T) { - logger := newDebugLogger() - logger.Info("hi") - logger.Debug("hi") - - for _, match := range strings.Split("foo", ",") { - strings.Split(match, "=") - } -} - func Test_newModuleEncoder(t *testing.T) { tests := []struct { arg string