Skip to content

Commit b528c3a

Browse files
authored
chore(kurtosis-devnet): add proper deploy package (#13869)
1 parent 8c45612 commit b528c3a

File tree

10 files changed

+844
-574
lines changed

10 files changed

+844
-574
lines changed

kurtosis-devnet/cmd/main.go

Lines changed: 17 additions & 341 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,18 @@
11
package main
22

33
import (
4-
"bytes"
54
"context"
65
"encoding/json"
76
"fmt"
8-
"io"
97
"log"
108
"os"
119
"path/filepath"
12-
"strings"
1310

14-
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/build"
11+
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/deploy"
1512
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis"
16-
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/engine"
17-
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec"
18-
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/tmpl"
19-
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/util"
2013
"github.com/urfave/cli/v2"
2114
)
2215

23-
const FILESERVER_PACKAGE = "fileserver"
24-
25-
func fileserverURL(path ...string) string {
26-
return fmt.Sprintf("http://%s/%s", FILESERVER_PACKAGE, strings.Join(path, "/"))
27-
}
28-
2916
type config struct {
3017
templateFile string
3118
dataFile string
@@ -57,298 +44,6 @@ func newConfig(c *cli.Context) (*config, error) {
5744
return cfg, nil
5845
}
5946

60-
type engineManager interface {
61-
EnsureRunning() error
62-
}
63-
64-
type Main struct {
65-
cfg *config
66-
newDeployer func(opts ...kurtosis.KurtosisDeployerOptions) (deployer, error)
67-
engineManager engineManager
68-
}
69-
70-
func (m *Main) localDockerImageOption() tmpl.TemplateContextOptions {
71-
dockerBuilder := build.NewDockerBuilder(
72-
build.WithDockerBaseDir(m.cfg.baseDir),
73-
build.WithDockerDryRun(m.cfg.dryRun),
74-
)
75-
76-
imageTag := func(projectName string) string {
77-
return fmt.Sprintf("%s:%s", projectName, m.cfg.enclave)
78-
}
79-
80-
return tmpl.WithFunction("localDockerImage", func(projectName string) (string, error) {
81-
return dockerBuilder.Build(projectName, imageTag(projectName))
82-
})
83-
}
84-
85-
func (m *Main) localContractArtifactsOption(dir string) tmpl.TemplateContextOptions {
86-
contractsBundle := fmt.Sprintf("contracts-bundle-%s.tar.gz", m.cfg.enclave)
87-
contractsBundlePath := func(_ string) string {
88-
return filepath.Join(dir, contractsBundle)
89-
}
90-
contractsURL := fileserverURL(contractsBundle)
91-
92-
contractBuilder := build.NewContractBuilder(
93-
build.WithContractBaseDir(m.cfg.baseDir),
94-
build.WithContractDryRun(m.cfg.dryRun),
95-
)
96-
97-
return tmpl.WithFunction("localContractArtifacts", func(layer string) (string, error) {
98-
bundlePath := contractsBundlePath(layer)
99-
if err := contractBuilder.Build(layer, bundlePath); err != nil {
100-
return "", err
101-
}
102-
103-
log.Printf("%s: contract artifacts available at: %s\n", layer, contractsURL)
104-
return contractsURL, nil
105-
})
106-
}
107-
108-
type PrestateInfo struct {
109-
URL string `json:"url"`
110-
Hashes map[string]string `json:"hashes"`
111-
}
112-
113-
type localPrestateHolder struct {
114-
info *PrestateInfo
115-
baseDir string
116-
buildDir string
117-
dryRun bool
118-
builder *build.PrestateBuilder
119-
}
120-
121-
func newLocalPrestateHolder(baseDir string, buildDir string, dryRun bool) *localPrestateHolder {
122-
return &localPrestateHolder{
123-
baseDir: baseDir,
124-
buildDir: buildDir,
125-
dryRun: dryRun,
126-
builder: build.NewPrestateBuilder(
127-
build.WithPrestateBaseDir(baseDir),
128-
build.WithPrestateDryRun(dryRun),
129-
),
130-
}
131-
}
132-
133-
func (h *localPrestateHolder) GetPrestateInfo() (*PrestateInfo, error) {
134-
if h.info != nil {
135-
return h.info, nil
136-
}
137-
138-
prestatePath := []string{"proofs", "op-program", "cannon"}
139-
prestateURL := fileserverURL(prestatePath...)
140-
141-
// Create build directory with the final path structure
142-
buildDir := filepath.Join(append([]string{h.buildDir}, prestatePath...)...)
143-
if err := os.MkdirAll(buildDir, 0755); err != nil {
144-
return nil, fmt.Errorf("failed to create prestate build directory: %w", err)
145-
}
146-
147-
info := &PrestateInfo{
148-
URL: prestateURL,
149-
Hashes: make(map[string]string),
150-
}
151-
152-
if h.dryRun {
153-
h.info = info
154-
return info, nil
155-
}
156-
157-
// Map of known file prefixes to their keys
158-
fileToKey := map[string]string{
159-
"prestate-proof.json": "prestate",
160-
"prestate-proof-mt64.json": "prestate_mt64",
161-
"prestate-proof-mt.json": "prestate_mt",
162-
"prestate-proof-interop.json": "prestate_interop",
163-
}
164-
165-
// Build all prestate files directly in the target directory
166-
if err := h.builder.Build(buildDir); err != nil {
167-
return nil, fmt.Errorf("failed to build prestates: %w", err)
168-
}
169-
170-
// Find and process all prestate files
171-
matches, err := filepath.Glob(filepath.Join(buildDir, "prestate-proof*.json"))
172-
if err != nil {
173-
return nil, fmt.Errorf("failed to find prestate files: %w", err)
174-
}
175-
176-
// Process each file to rename it to its hash
177-
for _, filePath := range matches {
178-
content, err := os.ReadFile(filePath)
179-
if err != nil {
180-
return nil, fmt.Errorf("failed to read prestate %s: %w", filepath.Base(filePath), err)
181-
}
182-
183-
var data struct {
184-
Pre string `json:"pre"`
185-
}
186-
if err := json.Unmarshal(content, &data); err != nil {
187-
return nil, fmt.Errorf("failed to parse prestate %s: %w", filepath.Base(filePath), err)
188-
}
189-
190-
// Store hash with its corresponding key
191-
if key, exists := fileToKey[filepath.Base(filePath)]; exists {
192-
info.Hashes[key] = data.Pre
193-
}
194-
195-
// Rename files to hash-based names
196-
newFileName := data.Pre + ".json"
197-
hashedPath := filepath.Join(buildDir, newFileName)
198-
if err := os.Rename(filePath, hashedPath); err != nil {
199-
return nil, fmt.Errorf("failed to rename prestate %s: %w", filepath.Base(filePath), err)
200-
}
201-
log.Printf("%s available at: %s/%s\n", filepath.Base(filePath), prestateURL, newFileName)
202-
203-
// Rename the corresponding binary file
204-
binFilePath := strings.Replace(strings.TrimSuffix(filePath, ".json"), "-proof", "", 1) + ".bin.gz"
205-
newBinFileName := data.Pre + ".bin.gz"
206-
binHashedPath := filepath.Join(buildDir, newBinFileName)
207-
if err := os.Rename(binFilePath, binHashedPath); err != nil {
208-
return nil, fmt.Errorf("failed to rename prestate %s: %w", filepath.Base(binFilePath), err)
209-
}
210-
log.Printf("%s available at: %s/%s\n", filepath.Base(binFilePath), prestateURL, newBinFileName)
211-
}
212-
213-
h.info = info
214-
return info, nil
215-
}
216-
217-
func (m *Main) localPrestateOption(dir string) tmpl.TemplateContextOptions {
218-
holder := newLocalPrestateHolder(m.cfg.baseDir, dir, m.cfg.dryRun)
219-
220-
return tmpl.WithFunction("localPrestate", func() (*PrestateInfo, error) {
221-
return holder.GetPrestateInfo()
222-
})
223-
}
224-
225-
func (m *Main) renderTemplate(dir string) (*bytes.Buffer, error) {
226-
opts := []tmpl.TemplateContextOptions{
227-
m.localDockerImageOption(),
228-
m.localContractArtifactsOption(dir),
229-
m.localPrestateOption(dir),
230-
tmpl.WithBaseDir(m.cfg.baseDir),
231-
}
232-
233-
// Read and parse the data file if provided
234-
if m.cfg.dataFile != "" {
235-
data, err := os.ReadFile(m.cfg.dataFile)
236-
if err != nil {
237-
return nil, fmt.Errorf("error reading data file: %w", err)
238-
}
239-
240-
var templateData map[string]interface{}
241-
if err := json.Unmarshal(data, &templateData); err != nil {
242-
return nil, fmt.Errorf("error parsing JSON data: %w", err)
243-
}
244-
245-
opts = append(opts, tmpl.WithData(templateData))
246-
}
247-
248-
// Open template file
249-
tmplFile, err := os.Open(m.cfg.templateFile)
250-
if err != nil {
251-
return nil, fmt.Errorf("error opening template file: %w", err)
252-
}
253-
defer tmplFile.Close()
254-
255-
// Create template context
256-
tmplCtx := tmpl.NewTemplateContext(opts...)
257-
258-
// Process template
259-
buf := bytes.NewBuffer(nil)
260-
if err := tmplCtx.InstantiateTemplate(tmplFile, buf); err != nil {
261-
return nil, fmt.Errorf("error processing template: %w", err)
262-
}
263-
264-
return buf, nil
265-
}
266-
267-
func (m *Main) deploy(ctx context.Context, r io.Reader) error {
268-
// Create a multi reader to output deployment input to stdout
269-
buf := bytes.NewBuffer(nil)
270-
tee := io.TeeReader(r, buf)
271-
272-
// Log the deployment input
273-
log.Println("Deployment input:")
274-
if _, err := io.Copy(os.Stdout, tee); err != nil {
275-
return fmt.Errorf("error copying deployment input: %w", err)
276-
}
277-
278-
opts := []kurtosis.KurtosisDeployerOptions{
279-
kurtosis.WithKurtosisBaseDir(m.cfg.baseDir),
280-
kurtosis.WithKurtosisDryRun(m.cfg.dryRun),
281-
kurtosis.WithKurtosisPackageName(m.cfg.kurtosisPackage),
282-
kurtosis.WithKurtosisEnclave(m.cfg.enclave),
283-
}
284-
285-
d, err := m.newDeployer(opts...)
286-
if err != nil {
287-
return fmt.Errorf("error creating kurtosis deployer: %w", err)
288-
}
289-
290-
spec, err := d.Deploy(ctx, buf)
291-
if err != nil {
292-
return fmt.Errorf("error deploying kurtosis package: %w", err)
293-
}
294-
295-
env, err := d.GetEnvironmentInfo(ctx, spec)
296-
if err != nil {
297-
return fmt.Errorf("error getting environment: %w", err)
298-
}
299-
300-
if err := writeEnvironment(m.cfg.environment, env); err != nil {
301-
return fmt.Errorf("error writing environment: %w", err)
302-
}
303-
304-
return nil
305-
}
306-
307-
func (m *Main) deployFileserver(ctx context.Context, sourceDir string) error {
308-
// Create a temp dir in the fileserver package
309-
baseDir := filepath.Join(m.cfg.baseDir, FILESERVER_PACKAGE)
310-
if err := os.MkdirAll(baseDir, 0755); err != nil {
311-
return fmt.Errorf("error creating base directory: %w", err)
312-
}
313-
tempDir, err := os.MkdirTemp(baseDir, "upload-content")
314-
if err != nil {
315-
return fmt.Errorf("error creating temporary directory: %w", err)
316-
}
317-
defer os.RemoveAll(tempDir)
318-
319-
// Copy build dir contents to tempDir
320-
if err := util.CopyDir(sourceDir, tempDir); err != nil {
321-
return fmt.Errorf("error copying directory: %w", err)
322-
}
323-
324-
buf := bytes.NewBuffer(nil)
325-
buf.WriteString(fmt.Sprintf("source_path: %s\n", filepath.Base(tempDir)))
326-
327-
opts := []kurtosis.KurtosisDeployerOptions{
328-
kurtosis.WithKurtosisBaseDir(m.cfg.baseDir),
329-
kurtosis.WithKurtosisDryRun(m.cfg.dryRun),
330-
kurtosis.WithKurtosisPackageName(FILESERVER_PACKAGE),
331-
kurtosis.WithKurtosisEnclave(m.cfg.enclave),
332-
}
333-
334-
d, err := m.newDeployer(opts...)
335-
if err != nil {
336-
return fmt.Errorf("error creating kurtosis deployer: %w", err)
337-
}
338-
339-
_, err = d.Deploy(ctx, buf)
340-
if err != nil {
341-
return fmt.Errorf("error deploying kurtosis package: %w", err)
342-
}
343-
344-
return nil
345-
}
346-
347-
type deployer interface {
348-
Deploy(ctx context.Context, input io.Reader) (*spec.EnclaveSpec, error)
349-
GetEnvironmentInfo(ctx context.Context, spec *spec.EnclaveSpec) (*kurtosis.KurtosisEnvironment, error)
350-
}
351-
35247
func writeEnvironment(path string, env *kurtosis.KurtosisEnvironment) error {
35348
out := os.Stdout
35449
if path != "" {
@@ -369,49 +64,30 @@ func writeEnvironment(path string, env *kurtosis.KurtosisEnvironment) error {
36964
return nil
37065
}
37166

372-
func (m *Main) run() error {
373-
ctx, cancel := context.WithCancel(context.Background())
374-
defer cancel()
375-
376-
if !m.cfg.dryRun {
377-
if err := m.engineManager.EnsureRunning(); err != nil {
378-
return fmt.Errorf("error ensuring kurtosis engine is running: %w", err)
379-
}
380-
}
67+
func mainAction(c *cli.Context) error {
68+
ctx := context.Background()
38169

382-
tmpDir, err := os.MkdirTemp("", m.cfg.enclave)
70+
cfg, err := newConfig(c)
38371
if err != nil {
384-
return fmt.Errorf("error creating temporary directory: %w", err)
72+
return fmt.Errorf("error parsing config: %w", err)
38573
}
386-
defer os.RemoveAll(tmpDir)
38774

388-
buf, err := m.renderTemplate(tmpDir)
389-
if err != nil {
390-
return fmt.Errorf("error rendering template: %w", err)
391-
}
75+
deployer := deploy.NewDeployer(
76+
deploy.WithKurtosisPackage(cfg.kurtosisPackage),
77+
deploy.WithEnclave(cfg.enclave),
78+
deploy.WithDryRun(cfg.dryRun),
79+
deploy.WithKurtosisBinary(cfg.kurtosisBinary),
80+
deploy.WithTemplateFile(cfg.templateFile),
81+
deploy.WithDataFile(cfg.dataFile),
82+
deploy.WithBaseDir(cfg.baseDir),
83+
)
39284

393-
// TODO: clean up consumers of static server and replace with fileserver
394-
err = m.deployFileserver(ctx, tmpDir)
85+
env, err := deployer.Deploy(ctx, nil)
39586
if err != nil {
396-
return fmt.Errorf("error deploying fileserver: %w", err)
87+
return fmt.Errorf("error deploying environment: %w", err)
39788
}
39889

399-
return m.deploy(ctx, buf)
400-
}
401-
402-
func mainAction(c *cli.Context) error {
403-
cfg, err := newConfig(c)
404-
if err != nil {
405-
return fmt.Errorf("error parsing config: %w", err)
406-
}
407-
m := &Main{
408-
cfg: cfg,
409-
newDeployer: func(opts ...kurtosis.KurtosisDeployerOptions) (deployer, error) {
410-
return kurtosis.NewKurtosisDeployer(opts...)
411-
},
412-
engineManager: engine.NewEngineManager(engine.WithKurtosisBinary(cfg.kurtosisBinary)),
413-
}
414-
return m.run()
90+
return writeEnvironment(cfg.environment, env)
41591
}
41692

41793
func getFlags() []cli.Flag {

0 commit comments

Comments
 (0)