Skip to content

Commit e8325d6

Browse files
authored
feat: support build with push and pull with extract from remote (#108)
Signed-off-by: chlins <[email protected]>
1 parent 69047de commit e8325d6

36 files changed

+1583
-381
lines changed

.golangci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ run:
44
skip-dirs:
55
- test/mocks
66

7-
87
linters-settings:
98
gocyclo:
109
min-complexity: 100

.mockery.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,11 @@ packages:
1919
Modelfile:
2020
config:
2121
dir: test/mocks/modelfile
22+
github.com/CloudNativeAI/modctl/pkg/backend/build:
23+
interfaces:
24+
Builder:
25+
config:
26+
dir: test/mocks/backend/build
27+
OutputStrategy:
28+
config:
29+
dir: test/mocks/backend/build

cmd/build.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ func init() {
5252
flags.IntVarP(&buildConfig.Concurrency, "concurrency", "c", buildConfig.Concurrency, "specify the number of concurrent build operations")
5353
flags.StringVarP(&buildConfig.Target, "target", "t", "", "target model artifact name")
5454
flags.StringVarP(&buildConfig.Modelfile, "modelfile", "f", "Modelfile", "model file path")
55+
flags.BoolVarP(&buildConfig.OutputRemote, "output-remote", "", false, "turning on this flag will output model artifact to remote registry directly")
56+
flags.BoolVarP(&buildConfig.PlainHTTP, "plain-http", "", false, "turning on this flag will use plain HTTP instead of HTTPS")
57+
flags.BoolVarP(&buildConfig.Insecure, "insecure", "", false, "turning on this flag will disable TLS verification")
5558

5659
if err := viper.BindPFlags(flags); err != nil {
5760
panic(fmt.Errorf("bind cache list flags to viper: %w", err))
@@ -65,11 +68,7 @@ func runBuild(ctx context.Context, workDir string) error {
6568
return err
6669
}
6770

68-
opts := []backend.Option{
69-
backend.WithConcurrency(buildConfig.Concurrency),
70-
}
71-
72-
if err := b.Build(ctx, buildConfig.Modelfile, workDir, buildConfig.Target, opts...); err != nil {
71+
if err := b.Build(ctx, buildConfig.Modelfile, workDir, buildConfig.Target, buildConfig); err != nil {
7372
return err
7473
}
7574

cmd/login.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,7 @@ func runLogin(ctx context.Context, registry string) error {
8989

9090
fmt.Println("\nLogging In...")
9191

92-
opts := []backend.Option{
93-
backend.WithPlainHTTP(loginConfig.PlainHTTP),
94-
}
95-
96-
if err := b.Login(ctx, registry, loginConfig.Username, loginConfig.Password, opts...); err != nil {
92+
if err := b.Login(ctx, registry, loginConfig.Username, loginConfig.Password, loginConfig); err != nil {
9793
return err
9894
}
9995

cmd/pull.go

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ func init() {
5454
flags.BoolVar(&pullConfig.Insecure, "insecure", false, "use insecure connection for the pull operation and skip TLS verification")
5555
flags.StringVar(&pullConfig.Proxy, "proxy", "", "use proxy for the pull operation")
5656
flags.StringVar(&pullConfig.ExtractDir, "extract-dir", "", "specify the extract dir for extracting the model artifact")
57+
flags.BoolVar(&pullConfig.ExtractFromRemote, "extract-from-remote", false, "turning on this flag will pull and extract the data from remote registry and no longer store model artifact locally, so user must specify extract-dir as the output directory")
5758

5859
if err := viper.BindPFlags(flags); err != nil {
5960
panic(fmt.Errorf("bind cache pull flags to viper: %w", err))
@@ -71,21 +72,7 @@ func runPull(ctx context.Context, target string) error {
7172
return fmt.Errorf("target is required")
7273
}
7374

74-
opts := []backend.Option{
75-
backend.WithInsecure(pullConfig.Insecure),
76-
backend.WithPlainHTTP(pullConfig.PlainHTTP),
77-
backend.WithConcurrency(pullConfig.Concurrency),
78-
}
79-
80-
if pullConfig.Proxy != "" {
81-
opts = append(opts, backend.WithProxy(pullConfig.Proxy))
82-
}
83-
84-
if pullConfig.ExtractDir != "" {
85-
opts = append(opts, backend.WithOutput(pullConfig.ExtractDir))
86-
}
87-
88-
if err := b.Pull(ctx, target, opts...); err != nil {
75+
if err := b.Pull(ctx, target, pullConfig); err != nil {
8976
return err
9077
}
9178

cmd/push.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,7 @@ func runPush(ctx context.Context, target string) error {
6464
return err
6565
}
6666

67-
opts := []backend.Option{
68-
backend.WithPlainHTTP(pushConfig.PlainHTTP),
69-
backend.WithConcurrency(pushConfig.Concurrency),
70-
}
71-
72-
if err := b.Push(ctx, target, opts...); err != nil {
67+
if err := b.Push(ctx, target, pushConfig); err != nil {
7368
return err
7469
}
7570

pkg/backend/backend.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,26 @@ package backend
1919
import (
2020
"context"
2121

22+
"github.com/CloudNativeAI/modctl/pkg/config"
2223
"github.com/CloudNativeAI/modctl/pkg/storage"
2324
)
2425

2526
// Backend is the interface to represent the backend.
2627
type Backend interface {
2728
// Login logs into a registry.
28-
Login(ctx context.Context, registry, username, password string, opts ...Option) error
29+
Login(ctx context.Context, registry, username, password string, cfg *config.Login) error
2930

3031
// Logout logs out from a registry.
3132
Logout(ctx context.Context, registry string) error
3233

3334
// Build builds the user materials into the OCI image which follows the Model Spec.
34-
Build(ctx context.Context, modelfilePath, workDir, target string, opts ...Option) error
35+
Build(ctx context.Context, modelfilePath, workDir, target string, cfg *config.Build) error
3536

3637
// Pull pulls an artifact from a registry.
37-
Pull(ctx context.Context, target string, opts ...Option) error
38+
Pull(ctx context.Context, target string, cfg *config.Pull) error
3839

3940
// Push pushes the image to the registry.
40-
Push(ctx context.Context, target string, opts ...Option) error
41+
Push(ctx context.Context, target string, cfg *config.Push) error
4142

4243
// List lists all the model artifacts.
4344
List(ctx context.Context) ([]*ModelArtifact, error)

pkg/backend/build.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
"github.com/CloudNativeAI/modctl/pkg/backend/build"
2424
"github.com/CloudNativeAI/modctl/pkg/backend/processor"
25+
"github.com/CloudNativeAI/modctl/pkg/config"
2526
"github.com/CloudNativeAI/modctl/pkg/modelfile"
2627

2728
modelspec "github.com/CloudNativeAI/model-spec/specs-go/v1"
@@ -30,13 +31,7 @@ import (
3031
)
3132

3233
// Build builds the user materials into the OCI image which follows the Model Spec.
33-
func (b *backend) Build(ctx context.Context, modelfilePath, workDir, target string, opts ...Option) error {
34-
// apply options.
35-
options := &Options{}
36-
for _, opt := range opts {
37-
opt(options)
38-
}
39-
34+
func (b *backend) Build(ctx context.Context, modelfilePath, workDir, target string, cfg *config.Build) error {
4035
// parse the repo name and tag name from target.
4136
ref, err := ParseReference(target)
4237
if err != nil {
@@ -53,24 +48,39 @@ func (b *backend) Build(ctx context.Context, modelfilePath, workDir, target stri
5348
return fmt.Errorf("tag is required")
5449
}
5550

51+
// using the local output by default.
52+
outputType := build.OutputTypeLocal
53+
if cfg.OutputRemote {
54+
outputType = build.OutputTypeRemote
55+
}
56+
57+
opts := []build.Option{
58+
build.WithPlainHTTP(cfg.PlainHTTP),
59+
build.WithInsecure(cfg.Insecure),
60+
}
61+
builder, err := build.NewBuilder(outputType, b.store, modelfile, repo, tag, opts...)
62+
if err != nil {
63+
return fmt.Errorf("failed to create builder: %w", err)
64+
}
65+
5666
layers := []ocispec.Descriptor{}
57-
layerDescs, err := b.process(ctx, workDir, repo, options, b.getProcessors(modelfile)...)
67+
layerDescs, err := b.process(ctx, builder, workDir, cfg, b.getProcessors(modelfile)...)
5868
if err != nil {
5969
return fmt.Errorf("failed to process files: %w", err)
6070
}
6171

6272
layers = append(layers, layerDescs...)
6373

6474
// build the image config.
65-
configDesc, err := build.BuildConfig(ctx, b.store, modelfile, repo)
75+
configDesc, err := builder.BuildConfig(ctx)
6676
if err != nil {
6777
return fmt.Errorf("failed to build image config: %w", err)
6878
}
6979

7080
fmt.Printf("%s => %s (%s)\n", "Built config", configDesc.Digest, humanize.IBytes(uint64(configDesc.Size)))
7181

7282
// build the image manifest.
73-
manifestDesc, err := build.BuildManifest(ctx, b.store, repo, tag, layers, configDesc, manifestAnnotation())
83+
manifestDesc, err := builder.BuildManifest(ctx, layers, configDesc, manifestAnnotation())
7484
if err != nil {
7585
return fmt.Errorf("failed to build image manifest: %w", err)
7686
}
@@ -102,10 +112,10 @@ func (b *backend) getProcessors(modelfile modelfile.Modelfile) []processor.Proce
102112
}
103113

104114
// process walks the user work directory and process the identified files.
105-
func (b *backend) process(ctx context.Context, workDir string, repo string, opts *Options, processors ...processor.Processor) ([]ocispec.Descriptor, error) {
115+
func (b *backend) process(ctx context.Context, builder build.Builder, workDir string, cfg *config.Build, processors ...processor.Processor) ([]ocispec.Descriptor, error) {
106116
descriptors := []ocispec.Descriptor{}
107117
for _, p := range processors {
108-
descs, err := p.Process(ctx, workDir, repo, processor.WithConcurrency(opts.concurrency))
118+
descs, err := p.Process(ctx, builder, workDir, processor.WithConcurrency(cfg.Concurrency))
109119
if err != nil {
110120
return nil, err
111121
}

0 commit comments

Comments
 (0)