Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export GOLANGCI_LINT_VERSION := 2.8.0
# Note: Uses the -N -l go compiler options to disable compiler optimizations
# and inlining. Using these build options allows you to subsequently
# use source debugging tools like delve.
all: bin/buildah bin/imgtype bin/copy bin/inet bin/tutorial bin/dumpspec bin/passwd bin/crash bin/wait docs
all: bin/buildah bin/imgtype bin/copy bin/inet bin/tutorial bin/dumpspec bin/passwd bin/crash bin/wait bin/grpcnoop docs

bin/buildah: $(SOURCES) internal/mkcw/embed/entrypoint_amd64.gz
$(GO_BUILD) $(BUILDAH_LDFLAGS) $(GO_GCFLAGS) "$(GOGCFLAGS)" -o $@ $(BUILDFLAGS) ./cmd/buildah
Expand Down Expand Up @@ -131,6 +131,9 @@ bin/inet: tests/inet/inet.go
bin/passwd: tests/passwd/passwd.go
$(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/passwd/passwd.go

bin/grpcnoop: tests/rpc/noop/noop.go
$(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/rpc/noop/noop.go

.PHONY: clean
clean:
$(RM) -r bin tests/testreport/testreport tests/conformance/testdata/mount-targets/true internal/mkcw/embed/entrypoint_amd64 internal/mkcw/embed/entrypoint_arm64 internal/mkcw/embed/entrypoint_ppc64le internal/mkcw/embed/entrypoint_s390x internal/mkcw/embed/*.gz internal/mkcw/embed/asm/*.o
Expand Down
98 changes: 98 additions & 0 deletions cmd/buildah/rpc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package main

import (
"os"
"os/exec"
"path/filepath"
"slices"

"github.com/containers/buildah/internal/rpc/listen"
"github.com/containers/buildah/internal/rpc/noop"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)

var (
rpcDescription = "\n Runs a command with a rudimentary RPC server available."
rpcCommand = &cobra.Command{
Use: "rpc",
Short: "Run a command with a rudimentary RPC server available",
Long: rpcDescription,
RunE: rpcCmd,
Hidden: true,
Example: `buildah rpc [-e|--env NAME] [-l|--listen PATH] command ...`,
Args: cobra.MinimumNArgs(1),
}
)

func rpcCmd(c *cobra.Command, args []string) error {
store, err := getStore(c)
if err != nil {
return err
}

socketPath := c.Flag("listen").Value.String()
if socketPath == "" {
socketDir, err := os.MkdirTemp(store.RunRoot(), "buildah-socket")
if err != nil {
return err
}
defer func() {
if err := os.RemoveAll(socketDir); err != nil {
logrus.Errorf("removing %s: %v", socketDir, err)
}
}()
socketPath = filepath.Join(socketDir, "socket")
}
listener, cleanup, err := listen.Listen(socketPath)
if err != nil {
return err
}
logrus.Debugf("listening for rpc requests at %q", socketPath)
defer func() {
if err := cleanup(); err != nil {
logrus.Errorf("cleaning up: %v", err)
}
}()

s := grpc.NewServer()
noop.Register(s)
reflection.Register(s)

var errgroup errgroup.Group
errgroup.Go(func() error {
if err := s.Serve(listener); err != nil {
return err
}
return nil
})
defer func() {
s.GracefulStop() // closes the listening socket
if err := errgroup.Wait(); err != nil {
logrus.Errorf("while waiting for rpc service to shut down: %v", err)
}
}()
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
envVar := c.Flag("env").Value.String()
if envVar != "" {
cmd.Env = append(slices.Clone(cmd.Environ()), envVar+"="+"unix://"+socketPath)
}
return cmd.Run()
}

func init() {
var rpcOptions struct {
envVar string
listenPath string
}
rpcCommand.SetUsageTemplate(UsageTemplate())
flags := rpcCommand.Flags()
flags.SetInterspersed(false)
flags.StringVarP(&rpcOptions.envVar, "env", "e", "", "set environment `variable` to point to listening socket path")
flags.StringVarP(&rpcOptions.listenPath, "listen", "l", "", "listening socket `path`")
rootCmd.AddCommand(rpcCommand)
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ require (
golang.org/x/sync v0.19.0
golang.org/x/sys v0.41.0
golang.org/x/term v0.40.0
google.golang.org/grpc v1.76.0
google.golang.org/protobuf v1.36.11
tags.cncf.io/container-device-interface v1.1.0
)

Expand Down Expand Up @@ -132,8 +134,6 @@ require (
golang.org/x/text v0.34.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
google.golang.org/grpc v1.76.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog v1.0.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
Expand Down
39 changes: 39 additions & 0 deletions internal/rpc/listen/listen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package listen

import (
"errors"
"fmt"
"net"
"os"
"path/filepath"

"github.com/containers/buildah/internal/tmpdir"
)

func Listen(location string) (net.Listener, func() error, error) {
cleanup := func() error { return nil }
if location == "" {
newParentDir, err := os.MkdirTemp(tmpdir.GetTempDir(), "buildah-socket")
if err != nil {
return nil, nil, fmt.Errorf("creating a temporary directory to hold a listening socket: %w", err)
}
location = filepath.Join(newParentDir, "build.sock")
cleanup = func() error { return os.RemoveAll(newParentDir) }
}
l, err := net.ListenUnix("unix", &net.UnixAddr{Net: "unix", Name: location})
if err != nil {
cerr := cleanup()
return nil, nil, errors.Join(err, cerr)
}
closeAndCleanup := func() error {
lerr := l.Close()
if lerr != nil && errors.Is(lerr, net.ErrClosed) {
// if the listening socket was used by an rpc server that was explicitly
// stopped, the rpc server will have closed the socket
lerr = nil
}
cerr := cleanup()
return errors.Join(lerr, cerr)
}
return l, closeAndCleanup, nil
}
26 changes: 26 additions & 0 deletions internal/rpc/noop/noop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package noop

import (
"context"
"syscall"

"github.com/containers/buildah/internal/rpc/noop/pb"
"google.golang.org/grpc"
)

type noopServer struct {
pb.UnimplementedNoopServer
}

func (n *noopServer) Noop(_ context.Context, req *pb.NoopRequest) (*pb.NoopResponse, error) {
if req == nil {
return nil, syscall.EINVAL
}
resp := &pb.NoopResponse{}
resp.Ignored = req.Ignored
return resp, nil
}

func Register(s grpc.ServiceRegistrar) {
pb.RegisterNoopServer(s, &noopServer{})
}
12 changes: 12 additions & 0 deletions internal/rpc/noop/pb/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash
set -e
cd $(dirname ${BASH_SOURCE[0]})
TOP=../../../..
PATH=${TOP}/tests/tools/build:${PATH}
set -x
for proto in *.proto ; do
protoc \
--go_opt=paths=source_relative --go_out . \
--go-grpc_opt=paths=source_relative --go-grpc_out . \
${proto}
done
174 changes: 174 additions & 0 deletions internal/rpc/noop/pb/noop.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading