Skip to content

Commit 0aaa4d5

Browse files
committed
storage/cmd: new commands json-rpc-server and apply-splitfdstream
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
1 parent 440830c commit 0aaa4d5

1 file changed

Lines changed: 202 additions & 0 deletions

File tree

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
//go:build linux
2+
3+
package main
4+
5+
import (
6+
"bytes"
7+
"fmt"
8+
"os"
9+
"os/signal"
10+
"path/filepath"
11+
"syscall"
12+
13+
"go.podman.io/storage"
14+
graphdriver "go.podman.io/storage/drivers"
15+
"go.podman.io/storage/pkg/archive"
16+
"go.podman.io/storage/pkg/mflag"
17+
"go.podman.io/storage/pkg/splitfdstream"
18+
)
19+
20+
const defaultJSONRPCSocket = "json-rpc.sock"
21+
22+
var (
23+
splitfdstreamSocket = ""
24+
applyFdstreamSocket = ""
25+
applyFdstreamParent = ""
26+
applyFdstreamMountLabel = ""
27+
)
28+
29+
// splitFDStreamDiffer implements graphdriver.Differ for splitfdstream data
30+
type splitFDStreamDiffer struct {
31+
streamData []byte
32+
fds []*os.File
33+
store storage.Store
34+
}
35+
36+
func (d *splitFDStreamDiffer) ApplyDiff(dest string, options *archive.TarOptions, differOpts *graphdriver.DifferOptions) (graphdriver.DriverWithDifferOutput, error) {
37+
driver, err := d.store.GraphDriver()
38+
if err != nil {
39+
return graphdriver.DriverWithDifferOutput{}, fmt.Errorf("failed to get graph driver: %w", err)
40+
}
41+
42+
splitDriver, ok := driver.(splitfdstream.SplitFDStreamDriver)
43+
if !ok {
44+
return graphdriver.DriverWithDifferOutput{}, fmt.Errorf("driver %s does not support splitfdstream", driver.String())
45+
}
46+
47+
opts := &splitfdstream.ApplySplitFDStreamOpts{
48+
Stream: bytes.NewReader(d.streamData),
49+
FileDescriptors: d.fds,
50+
StagingDir: dest,
51+
}
52+
53+
size, err := splitDriver.ApplySplitFDStream(opts)
54+
if err != nil {
55+
return graphdriver.DriverWithDifferOutput{}, fmt.Errorf("failed to apply splitfdstream to staging dir %s: %w", dest, err)
56+
}
57+
58+
return graphdriver.DriverWithDifferOutput{
59+
Target: dest,
60+
Size: size,
61+
}, nil
62+
}
63+
64+
func (d *splitFDStreamDiffer) Close() error {
65+
return nil
66+
}
67+
68+
func splitfdstreamServer(flags *mflag.FlagSet, action string, m storage.Store, args []string) (int, error) {
69+
driver, err := m.GraphDriver()
70+
if err != nil {
71+
return 1, fmt.Errorf("failed to get graph driver: %w", err)
72+
}
73+
74+
server := splitfdstream.NewJSONRPCServer(driver)
75+
76+
socketPath := splitfdstreamSocket
77+
if socketPath == "" {
78+
socketPath = filepath.Join(m.RunRoot(), defaultJSONRPCSocket)
79+
}
80+
81+
if err := server.Start(socketPath); err != nil {
82+
return 1, fmt.Errorf("failed to start server: %w", err)
83+
}
84+
defer func() { _ = server.Stop() }()
85+
86+
fmt.Printf("%s\n", socketPath)
87+
88+
// Wait for interrupt signal
89+
sigCh := make(chan os.Signal, 1)
90+
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
91+
<-sigCh
92+
93+
return 0, nil
94+
}
95+
96+
func applySplitfdstream(flags *mflag.FlagSet, action string, m storage.Store, args []string) (int, error) {
97+
layerID := args[0]
98+
99+
socketPath := applyFdstreamSocket
100+
if socketPath == "" {
101+
socketPath = filepath.Join(m.RunRoot(), defaultJSONRPCSocket)
102+
}
103+
104+
defer func() {
105+
if _, err := m.Shutdown(false); err != nil {
106+
fmt.Fprintf(os.Stderr, "warning: failed to shutdown storage: %v\n", err)
107+
}
108+
}()
109+
110+
client, err := splitfdstream.NewJSONRPCClient(socketPath)
111+
if err != nil {
112+
return 1, fmt.Errorf("failed to connect to server: %w", err)
113+
}
114+
defer client.Close()
115+
116+
// Get splitfdstream data from remote server
117+
streamData, fds, err := client.GetSplitFDStream(layerID, "")
118+
if err != nil {
119+
return 1, fmt.Errorf("failed to get splitfdstream from server: %w", err)
120+
}
121+
122+
// Close received FDs when done
123+
defer func() {
124+
for _, fd := range fds {
125+
fd.Close()
126+
}
127+
}()
128+
129+
// Create a custom differ for splitfdstream data
130+
differ := &splitFDStreamDiffer{
131+
streamData: streamData,
132+
fds: fds,
133+
store: m,
134+
}
135+
defer differ.Close()
136+
137+
// Prepare the staged layer
138+
diffOptions := &graphdriver.ApplyDiffWithDifferOpts{}
139+
diffOutput, err := m.PrepareStagedLayer(diffOptions, differ)
140+
if err != nil {
141+
return 1, fmt.Errorf("failed to prepare staged layer: %w", err)
142+
}
143+
144+
// Apply the staged layer to create the final layer
145+
applyArgs := storage.ApplyStagedLayerOptions{
146+
ID: layerID,
147+
ParentLayer: applyFdstreamParent,
148+
MountLabel: applyFdstreamMountLabel,
149+
Writeable: false,
150+
LayerOptions: &storage.LayerOptions{},
151+
DiffOutput: diffOutput,
152+
DiffOptions: diffOptions,
153+
}
154+
155+
layer, err := m.ApplyStagedLayer(applyArgs)
156+
if err != nil {
157+
// Clean up the staged layer on failure
158+
if cleanupErr := m.CleanupStagedLayer(diffOutput); cleanupErr != nil {
159+
fmt.Fprintf(os.Stderr, "warning: failed to cleanup staged layer: %v\n", cleanupErr)
160+
}
161+
return 1, fmt.Errorf("failed to apply staged layer: %w", err)
162+
}
163+
164+
// Output the result
165+
if jsonOutput {
166+
return outputJSON(map[string]interface{}{"id": layer.ID, "size": diffOutput.Size})
167+
}
168+
fmt.Printf("%s\n", layer.ID)
169+
return 0, nil
170+
}
171+
172+
func init() {
173+
commands = append(commands, command{
174+
names: []string{"json-rpc-server"},
175+
optionsHelp: "[options]",
176+
usage: "Start a JSON-RPC server",
177+
minArgs: 0,
178+
maxArgs: 0,
179+
action: splitfdstreamServer,
180+
addFlags: func(flags *mflag.FlagSet, cmd *command) {
181+
flags.StringVar(&splitfdstreamSocket, []string{"-socket"}, "",
182+
"Path to UNIX socket")
183+
},
184+
})
185+
commands = append(commands, command{
186+
names: []string{"apply-splitfdstream"},
187+
optionsHelp: "[options] layerID",
188+
usage: "Fetch a layer from remote server and apply it locally",
189+
minArgs: 1,
190+
maxArgs: 1,
191+
action: applySplitfdstream,
192+
addFlags: func(flags *mflag.FlagSet, cmd *command) {
193+
flags.StringVar(&applyFdstreamSocket, []string{"-socket"}, "",
194+
"Path to remote UNIX socket")
195+
flags.StringVar(&applyFdstreamParent, []string{"-parent"}, "",
196+
"Parent layer ID for the new layer")
197+
flags.StringVar(&applyFdstreamMountLabel, []string{"-mount-label"}, "",
198+
"SELinux mount label for the layer")
199+
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
200+
},
201+
})
202+
}

0 commit comments

Comments
 (0)