Skip to content

Commit fd2c79a

Browse files
committed
Add an undocumented general "run with RPC service"
Add an undocumented general "run this with an RPC service" command, which right now doesn't do much. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
1 parent 3468444 commit fd2c79a

21 files changed

Lines changed: 3169 additions & 3 deletions

File tree

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export GOLANGCI_LINT_VERSION := 2.8.0
6060
# Note: Uses the -N -l go compiler options to disable compiler optimizations
6161
# and inlining. Using these build options allows you to subsequently
6262
# use source debugging tools like delve.
63-
all: bin/buildah bin/imgtype bin/copy bin/inet bin/tutorial bin/dumpspec bin/passwd bin/crash bin/wait docs
63+
all: bin/buildah bin/imgtype bin/copy bin/inet bin/tutorial bin/dumpspec bin/passwd bin/crash bin/wait bin/grpcnoop docs
6464

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

134+
bin/grpcnoop: tests/rpc/noop/noop.go
135+
$(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/rpc/noop/noop.go
136+
134137
.PHONY: clean
135138
clean:
136139
$(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

cmd/buildah/rpc.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"os/exec"
6+
"path/filepath"
7+
"slices"
8+
9+
"github.com/containers/buildah/internal/rpc/listen"
10+
"github.com/containers/buildah/internal/rpc/noop"
11+
"github.com/sirupsen/logrus"
12+
"github.com/spf13/cobra"
13+
"golang.org/x/sync/errgroup"
14+
"google.golang.org/grpc"
15+
"google.golang.org/grpc/reflection"
16+
)
17+
18+
var (
19+
rpcDescription = "\n Runs a command with a rudimentary RPC server available."
20+
rpcCommand = &cobra.Command{
21+
Use: "rpc",
22+
Short: "Run a command with a rudimentary RPC server available",
23+
Long: rpcDescription,
24+
RunE: rpcCmd,
25+
Hidden: true,
26+
Example: `buildah rpc [-e|--env NAME] [-l|--listen PATH] command ...`,
27+
Args: cobra.MinimumNArgs(1),
28+
}
29+
)
30+
31+
func rpcCmd(c *cobra.Command, args []string) error {
32+
store, err := getStore(c)
33+
if err != nil {
34+
return err
35+
}
36+
37+
socketPath := c.Flag("listen").Value.String()
38+
if socketPath == "" {
39+
socketDir, err := os.MkdirTemp(store.RunRoot(), "buildah-socket")
40+
if err != nil {
41+
return err
42+
}
43+
defer func() {
44+
if err := os.RemoveAll(socketDir); err != nil {
45+
logrus.Errorf("removing %s: %v", socketDir, err)
46+
}
47+
}()
48+
socketPath = filepath.Join(socketDir, "socket")
49+
}
50+
listener, cleanup, err := listen.Listen(socketPath)
51+
if err != nil {
52+
return err
53+
}
54+
logrus.Debugf("listening for rpc requests at %q", socketPath)
55+
defer func() {
56+
if err := cleanup(); err != nil {
57+
logrus.Errorf("cleaning up: %v", err)
58+
}
59+
}()
60+
61+
s := grpc.NewServer()
62+
noop.Register(s)
63+
reflection.Register(s)
64+
65+
var errgroup errgroup.Group
66+
errgroup.Go(func() error {
67+
if err := s.Serve(listener); err != nil {
68+
return err
69+
}
70+
return nil
71+
})
72+
defer func() {
73+
s.GracefulStop() // closes the listening socket
74+
if err := errgroup.Wait(); err != nil {
75+
logrus.Errorf("while waiting for rpc service to shut down: %v", err)
76+
}
77+
}()
78+
cmd := exec.Command(args[0], args[1:]...)
79+
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
80+
envVar := c.Flag("env").Value.String()
81+
if envVar != "" {
82+
cmd.Env = append(slices.Clone(cmd.Environ()), envVar+"="+"unix://"+socketPath)
83+
}
84+
return cmd.Run()
85+
}
86+
87+
func init() {
88+
var rpcOptions struct {
89+
envVar string
90+
listenPath string
91+
}
92+
rpcCommand.SetUsageTemplate(UsageTemplate())
93+
flags := rpcCommand.Flags()
94+
flags.SetInterspersed(false)
95+
flags.StringVarP(&rpcOptions.envVar, "env", "e", "", "set environment `variable` to point to listening socket path")
96+
flags.StringVarP(&rpcOptions.listenPath, "listen", "l", "", "listening socket `path`")
97+
rootCmd.AddCommand(rpcCommand)
98+
}

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ require (
4141
golang.org/x/sync v0.19.0
4242
golang.org/x/sys v0.40.0
4343
golang.org/x/term v0.39.0
44+
google.golang.org/grpc v1.76.0
45+
google.golang.org/protobuf v1.36.11
4446
tags.cncf.io/container-device-interface v1.1.0
4547
)
4648

@@ -132,8 +134,6 @@ require (
132134
golang.org/x/text v0.33.0 // indirect
133135
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 // indirect
134136
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
135-
google.golang.org/grpc v1.76.0 // indirect
136-
google.golang.org/protobuf v1.36.11 // indirect
137137
gopkg.in/yaml.v3 v3.0.1 // indirect
138138
k8s.io/klog v1.0.0 // indirect
139139
sigs.k8s.io/yaml v1.6.0 // indirect

internal/rpc/listen/listen.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package listen
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net"
7+
"os"
8+
"path/filepath"
9+
10+
"github.com/containers/buildah/internal/tmpdir"
11+
)
12+
13+
func Listen(location string) (net.Listener, func() error, error) {
14+
cleanup := func() error { return nil }
15+
if location == "" {
16+
newParentDir, err := os.MkdirTemp(tmpdir.GetTempDir(), "buildah-socket")
17+
if err != nil {
18+
return nil, nil, fmt.Errorf("creating a temporary directory to hold a listening socket: %w", err)
19+
}
20+
location = filepath.Join(newParentDir, "build.sock")
21+
cleanup = func() error { return os.RemoveAll(newParentDir) }
22+
}
23+
l, err := net.ListenUnix("unix", &net.UnixAddr{Net: "unix", Name: location})
24+
if err != nil {
25+
cerr := cleanup()
26+
return nil, nil, errors.Join(err, cerr)
27+
}
28+
closeAndCleanup := func() error {
29+
lerr := l.Close()
30+
if lerr != nil && errors.Is(lerr, net.ErrClosed) {
31+
// if the listening socket was used by an rpc server that was explicitly
32+
// stopped, the rpc server will have closed the socket
33+
lerr = nil
34+
}
35+
cerr := cleanup()
36+
return errors.Join(lerr, cerr)
37+
}
38+
return l, closeAndCleanup, nil
39+
}

internal/rpc/noop/noop.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package noop
2+
3+
import (
4+
"context"
5+
"syscall"
6+
7+
"github.com/containers/buildah/internal/rpc/noop/pb"
8+
"google.golang.org/grpc"
9+
)
10+
11+
type noopServer struct {
12+
pb.UnimplementedNoopServer
13+
}
14+
15+
func (n *noopServer) Noop(_ context.Context, req *pb.NoopRequest) (*pb.NoopResponse, error) {
16+
if req == nil {
17+
return nil, syscall.EINVAL
18+
}
19+
resp := &pb.NoopResponse{}
20+
resp.Ignored = req.Ignored
21+
return resp, nil
22+
}
23+
24+
func Register(s grpc.ServiceRegistrar) {
25+
pb.RegisterNoopServer(s, &noopServer{})
26+
}

internal/rpc/noop/pb/build.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/bash
2+
set -e
3+
cd $(dirname ${BASH_SOURCE[0]})
4+
TOP=../../../..
5+
PATH=${TOP}/tests/tools/build:${PATH}
6+
set -x
7+
for proto in *.proto ; do
8+
protoc \
9+
--go_opt=paths=source_relative --go_out . \
10+
--go-grpc_opt=paths=source_relative --go-grpc_out . \
11+
${proto}
12+
done

internal/rpc/noop/pb/noop.pb.go

Lines changed: 174 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)