Skip to content

Commit

Permalink
chore: more wip - making progress
Browse files Browse the repository at this point in the history
  • Loading branch information
nixpig committed Sep 15, 2024
1 parent ec83f10 commit af56f17
Show file tree
Hide file tree
Showing 10 changed files with 393 additions and 157 deletions.
137 changes: 100 additions & 37 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package cmd

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"

"github.com/nixpig/brownie/internal/commands"
"github.com/nixpig/brownie/pkg"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
)

Expand All @@ -12,10 +19,11 @@ var Root = &cobra.Command{
Short: "An experimental Linux container runtime.",
Long: "An experimental Linux container runtime; working towards OCI Runtime Spec compliance.",
Example: "",
Version: "0.0.1",
}

func createCmd() *cobra.Command {
var create = &cobra.Command{
func createCmd(log *zerolog.Logger) *cobra.Command {
create := &cobra.Command{
Use: "create [flags] CONTAINER_ID",
Short: "Create a container",
Args: cobra.ExactArgs(1),
Expand Down Expand Up @@ -45,7 +53,7 @@ func createCmd() *cobra.Command {
PIDFile: pidFile,
}

return commands.Create(opts)
return commands.Create(opts, log)
},
}

Expand All @@ -57,16 +65,24 @@ func createCmd() *cobra.Command {
return create
}

var Start = &cobra.Command{
Use: "start [flags] CONTAINER_ID",
Short: "Start a container",
Args: cobra.ExactArgs(1),
Example: " brownie start busybox",
RunE: func(cmd *cobra.Command, args []string) error {
containerID := args[0]
func startCmd(log *zerolog.Logger) *cobra.Command {
start := &cobra.Command{
Use: "start [flags] CONTAINER_ID",
Short: "Start a container",
Args: cobra.ExactArgs(1),
Example: " brownie start busybox",
RunE: func(cmd *cobra.Command, args []string) error {
containerID := args[0]

return commands.Start(containerID)
},
opts := &commands.StartOpts{
ID: containerID,
}

return commands.Start(opts, log)
},
}

return start
}

var Kill = &cobra.Command{
Expand Down Expand Up @@ -94,41 +110,88 @@ var Delete = &cobra.Command{
},
}

var Fork = &cobra.Command{
Use: "fork [flags] CONTAINER_ID INIT_SOCK_ADDR CONTAINER_SOCK_ADDR",
Short: "Fork container process\n\n \033[31m ⚠ FOR INTERNAL USE ONLY - DO NOT RUN DIRECTLY ⚠ \033[0m",
Args: cobra.ExactArgs(3),
Example: "\n -- FOR INTERNAL USE ONLY --",
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
containerID := args[0]
initSockAddr := args[1]
containerSockAddr := args[2]
func forkCmd(log *zerolog.Logger) *cobra.Command {
fork := &cobra.Command{
Use: "fork [flags] CONTAINER_ID INIT_SOCK_ADDR CONTAINER_SOCK_ADDR",
Short: "Fork container process\n\n \033[31m ⚠ FOR INTERNAL USE ONLY - DO NOT RUN DIRECTLY ⚠ \033[0m",
Args: cobra.ExactArgs(4),
Example: "\n -- FOR INTERNAL USE ONLY --",
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
stage := args[0]
containerID := args[1]
initSockAddr := args[2]
containerSockAddr := args[3]

commands.Fork(containerID, initSockAddr, containerSockAddr)
},
if commands.ForkStage(stage) != commands.ForkIntermediate && commands.ForkStage(stage) != commands.ForkDetached {
return errors.New("invalidate fork stage")
}

opts := &commands.ForkOpts{
ID: containerID,
InitSockAddr: initSockAddr,
ContainerSockAddr: containerSockAddr,
Stage: commands.ForkStage(stage),
}

return commands.Fork(opts, log)
},
}

return fork
}

var QueryState = &cobra.Command{
Use: "state [flags] CONTAINER_ID",
Short: "Query a container state",
Args: cobra.ExactArgs(1),
Example: " brownie state busybox",
RunE: func(cmd *cobra.Command, args []string) error {
containerID := args[0]
func stateCmd(log *zerolog.Logger) *cobra.Command {
state := &cobra.Command{
Use: "state [flags] CONTAINER_ID",
Short: "Query a container state",
Args: cobra.ExactArgs(1),
Example: " brownie state busybox",
RunE: func(cmd *cobra.Command, args []string) error {
containerID := args[0]

return commands.QueryState(containerID)
},
opts := &commands.StateOpts{
ID: containerID,
}

state, err := commands.State(opts, log)
if err != nil {
e := cmd.ErrOrStderr()
e.Write([]byte(err.Error()))
return err
}

var prettified bytes.Buffer
json.Indent(&prettified, []byte(state), "", " ")

fmt.Fprint(cmd.OutOrStdout(), prettified.String())
return nil
},
}

return state
}

func init() {
logfile, err := os.OpenFile(
filepath.Join(pkg.BrownieRootDir, "logs", "brownie.log"),
os.O_APPEND|os.O_CREATE|os.O_WRONLY,
0644,
)
if err != nil {
fmt.Println("open log file: %w", err)
os.Exit(1)
}

log := zerolog.New(logfile).With().Timestamp().Logger()

Root.AddCommand(
createCmd(),
Start,
QueryState,
createCmd(&log),
startCmd(&log),
stateCmd(&log),
Delete,
Kill,
Fork,
forkCmd(&log),
)

Root.CompletionOptions.HiddenDefaultCmd = true
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ require (

require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/rs/zerolog v1.33.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/sys v0.12.0 // indirect
)
15 changes: 15 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
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-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
Expand All @@ -16,5 +27,9 @@ golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
57 changes: 15 additions & 42 deletions internal/commands/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ package commands

import (
"encoding/json"
"errors"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"syscall"

"github.com/nixpig/brownie/internal"
"github.com/nixpig/brownie/pkg"
"github.com/opencontainers/runtime-spec/specs-go"
cp "github.com/otiai10/copy"
"github.com/rs/zerolog"
)

type CreateOpts struct {
Expand All @@ -23,7 +22,7 @@ type CreateOpts struct {
PIDFile string
}

func Create(opts *CreateOpts) error {
func Create(opts *CreateOpts, log *zerolog.Logger) error {
containerPath := filepath.Join(pkg.BrownieRootDir, "containers", opts.ID)

if stat, _ := os.Stat(containerPath); stat != nil {
Expand Down Expand Up @@ -92,55 +91,23 @@ func Create(opts *CreateOpts) error {
"/proc/self/exe",
[]string{
"fork",
string(ForkIntermediate),
opts.ID,
initSockAddr,
containerSockAddr,
}...)

if spec.Linux == nil {
return errors.New("not a linux container")
}
var cloneFlags uintptr
for _, ns := range spec.Linux.Namespaces {
ns := internal.LinuxNamespace(ns)
flag, err := ns.ToFlag()
if err != nil {
return err
}

cloneFlags = cloneFlags | flag
}

// apply configuration, e.g. devices, proc, etc...
forkCmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: cloneFlags,
Unshareflags: syscall.CLONE_NEWNS,
UidMappings: []syscall.SysProcIDMap{
{
ContainerID: 0,
HostID: int(spec.Process.User.UID),
Size: 1,
},
},
GidMappings: []syscall.SysProcIDMap{
{
ContainerID: 0,
HostID: int(spec.Process.User.GID),
Size: 1,
},
},
}

forkCmd.Start()
pid := forkCmd.Process.Pid
if err := forkCmd.Process.Release(); err != nil {
return fmt.Errorf("detach from process: %w", err)
forkCmd.Stdout = os.Stdout
forkCmd.Stderr = os.Stderr
if err := forkCmd.Run(); err != nil {
return fmt.Errorf("fork: %w", err)
}

// wait for container to be ready
if err := os.RemoveAll(initSockAddr); err != nil {
return err
}

listener, err := net.Listen("unix", initSockAddr)
if err != nil {
return fmt.Errorf("failed to listen on init socket: %w", err)
Expand All @@ -161,12 +128,18 @@ func Create(opts *CreateOpts) error {
continue
}

if string(b[:n]) == "ready" {
if len(b) > 0 {
log.Info().Str("msg", string(b)).Msg("received")
}

if len(b) >= 5 && string(b[:5]) == "ready" {
log.Info().Msg("received 'ready' message")
break
}
}

state.Status = specs.StateCreated
pid := forkCmd.Process.Pid
state.Pid = pid
if err := internal.SaveState(state); err != nil {
return fmt.Errorf("save created state: %w", err)
Expand Down
Loading

0 comments on commit af56f17

Please sign in to comment.