Skip to content

Commit 212d70b

Browse files
authored
chore(devnet-sdk): refactor devnet descriptor recording (ethereum-optimism#15403)
This centralizes the logic recording the devnet descriptor in devnet-sdk. We introduce a higher-level filesystem interface that enables the easy manipulation (read/write) of that descriptor, instead of replicating parts of that same logic in various components.
1 parent 11ace4c commit 212d70b

File tree

18 files changed

+599
-512
lines changed

18 files changed

+599
-512
lines changed

devnet-sdk/devstack/sysext/orchestrator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func NewOrchestrator(p devtest.P) *Orchestrator {
3939
}
4040
env, err := env.LoadDevnetFromURL(url)
4141
p.Require().NoError(err, "Error loading devnet environment")
42-
orch := &Orchestrator{env: &env.Config, p: p}
42+
orch := &Orchestrator{env: env.Env, p: p}
4343

4444
return orch
4545
}

devnet-sdk/kt/fs/devnet_fs.go

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package fs
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"log"
9+
"strings"
10+
11+
"github.com/ethereum-optimism/optimism/devnet-sdk/descriptors"
12+
)
13+
14+
const (
15+
DevnetEnvArtifactNamePrefix = "devnet-descriptor-"
16+
DevnetEnvArtifactPath = "env.json"
17+
)
18+
19+
type DevnetFS struct {
20+
*EnclaveFS
21+
}
22+
23+
type DevnetFSDescriptorOption func(*options)
24+
25+
type options struct {
26+
artifactName string
27+
artifactPath string
28+
}
29+
30+
func newOptions() *options {
31+
return &options{
32+
artifactPath: DevnetEnvArtifactPath,
33+
}
34+
}
35+
36+
func WithArtifactName(name string) DevnetFSDescriptorOption {
37+
return func(o *options) {
38+
o.artifactName = name
39+
}
40+
}
41+
42+
func WithArtifactPath(path string) DevnetFSDescriptorOption {
43+
return func(o *options) {
44+
o.artifactPath = path
45+
}
46+
}
47+
48+
func NewDevnetFS(fs *EnclaveFS) *DevnetFS {
49+
return &DevnetFS{EnclaveFS: fs}
50+
}
51+
52+
func (fs *DevnetFS) GetDevnetDescriptor(ctx context.Context, opts ...DevnetFSDescriptorOption) (*descriptors.DevnetEnvironment, error) {
53+
options := newOptions()
54+
for _, opt := range opts {
55+
opt(options)
56+
}
57+
58+
if options.artifactName == "" {
59+
if err := fs.loadLatestDevnetDescriptorName(ctx, options); err != nil {
60+
return nil, err
61+
}
62+
}
63+
64+
artifact, err := fs.GetArtifact(ctx, options.artifactName)
65+
if err != nil {
66+
return nil, fmt.Errorf("error getting artifact: %w", err)
67+
}
68+
69+
var buf bytes.Buffer
70+
writer := NewArtifactFileWriter(options.artifactPath, &buf)
71+
72+
if err := artifact.ExtractFiles(writer); err != nil {
73+
return nil, fmt.Errorf("error extracting file from artifact: %w", err)
74+
}
75+
76+
var env descriptors.DevnetEnvironment
77+
if err := json.Unmarshal(buf.Bytes(), &env); err != nil {
78+
return nil, fmt.Errorf("error unmarshalling environment: %w", err)
79+
}
80+
81+
return &env, nil
82+
}
83+
84+
func (fs *DevnetFS) UploadDevnetDescriptor(ctx context.Context, env *descriptors.DevnetEnvironment, opts ...DevnetFSDescriptorOption) error {
85+
envBuf := bytes.NewBuffer(nil)
86+
enc := json.NewEncoder(envBuf)
87+
enc.SetIndent("", " ")
88+
if err := enc.Encode(env); err != nil {
89+
return fmt.Errorf("error encoding environment: %w", err)
90+
}
91+
92+
options := newOptions()
93+
for _, opt := range opts {
94+
opt(options)
95+
}
96+
97+
if options.artifactName == "" {
98+
if err := fs.loadNextDevnetDescriptorName(ctx, options); err != nil {
99+
return fmt.Errorf("error getting next devnet descriptor: %w", err)
100+
}
101+
}
102+
103+
if err := fs.PutArtifact(ctx, options.artifactName, NewArtifactFileReader(options.artifactPath, envBuf)); err != nil {
104+
return fmt.Errorf("error putting environment artifact: %w", err)
105+
}
106+
107+
return nil
108+
}
109+
110+
func (fs *DevnetFS) loadLatestDevnetDescriptorName(ctx context.Context, options *options) error {
111+
names, err := fs.GetAllArtifactNames(ctx)
112+
if err != nil {
113+
return fmt.Errorf("error getting artifact names: %w", err)
114+
}
115+
116+
var maxSuffix int = -1
117+
var maxName string
118+
for _, name := range names {
119+
_, suffix, found := strings.Cut(name, DevnetEnvArtifactNamePrefix)
120+
if !found {
121+
continue
122+
}
123+
124+
// Parse the suffix as a number
125+
var num int
126+
if _, err := fmt.Sscanf(suffix, "%d", &num); err != nil {
127+
continue // Skip if suffix is not a valid number
128+
}
129+
130+
// Update maxName if this number is larger
131+
if num > maxSuffix {
132+
maxSuffix = num
133+
maxName = name
134+
}
135+
}
136+
137+
if maxName == "" {
138+
return fmt.Errorf("no descriptor found with valid numerical suffix")
139+
}
140+
141+
options.artifactName = maxName
142+
return nil
143+
}
144+
145+
func (fs *DevnetFS) loadNextDevnetDescriptorName(ctx context.Context, options *options) error {
146+
artifactNames, err := fs.GetAllArtifactNames(ctx)
147+
if err != nil {
148+
return fmt.Errorf("error getting artifact names: %w", err)
149+
}
150+
151+
maxNum := -1
152+
for _, artifactName := range artifactNames {
153+
if !strings.HasPrefix(artifactName, DevnetEnvArtifactNamePrefix) {
154+
continue
155+
}
156+
157+
numStr := strings.TrimPrefix(artifactName, DevnetEnvArtifactNamePrefix)
158+
num := 0
159+
if _, err := fmt.Sscanf(numStr, "%d", &num); err != nil {
160+
log.Printf("Warning: invalid devnet descriptor format: %s", artifactName)
161+
continue
162+
}
163+
164+
if num > maxNum {
165+
maxNum = num
166+
}
167+
}
168+
169+
options.artifactName = fmt.Sprintf("%s%d", DevnetEnvArtifactNamePrefix, maxNum+1)
170+
return nil
171+
}

0 commit comments

Comments
 (0)