Skip to content

Commit 4a33017

Browse files
committed
feat: support the upload command and optimize the log
Signed-off-by: chlins <[email protected]>
1 parent f54fd22 commit 4a33017

File tree

21 files changed

+203
-93
lines changed

21 files changed

+203
-93
lines changed

cmd/modelfile/modelfile.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
package modelfile
1818

1919
import (
20-
"github.com/sirupsen/logrus"
21-
2220
"github.com/spf13/cobra"
2321
"github.com/spf13/viper"
2422
)
@@ -32,8 +30,6 @@ var RootCmd = &cobra.Command{
3230
SilenceUsage: true,
3331
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
3432
RunE: func(cmd *cobra.Command, args []string) error {
35-
logrus.Debug("modctl modelfile is running")
36-
3733
return nil
3834
},
3935
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ require (
2424
github.com/vbauerster/mpb/v8 v8.10.2
2525
golang.org/x/crypto v0.39.0
2626
golang.org/x/sync v0.15.0
27+
golang.org/x/sys v0.33.0
2728
google.golang.org/grpc v1.73.0
2829
oras.land/oras-go/v2 v2.6.0
2930
)
@@ -111,7 +112,6 @@ require (
111112
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
112113
go.uber.org/multierr v1.11.0 // indirect
113114
golang.org/x/net v0.40.0 // indirect
114-
golang.org/x/sys v0.33.0 // indirect
115115
golang.org/x/term v0.32.0 // indirect
116116
golang.org/x/text v0.26.0 // indirect
117117
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect

pkg/backend/attach.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ var (
6060

6161
// Attach attaches user materials into the model artifact which follows the Model Spec.
6262
func (b *backend) Attach(ctx context.Context, filepath string, cfg *config.Attach) error {
63-
logrus.Infof("attaching file %s, cfg: %+v", filepath, cfg)
63+
logrus.Infof("attach: starting attach operation for file %s [config: %+v]", filepath, cfg)
6464
srcManifest, err := b.getManifest(ctx, cfg.Source, cfg.OutputRemote, cfg.PlainHTTP, cfg.Insecure)
6565
if err != nil {
6666
return fmt.Errorf("failed to get source manifest: %w", err)
@@ -71,7 +71,7 @@ func (b *backend) Attach(ctx context.Context, filepath string, cfg *config.Attac
7171
return fmt.Errorf("failed to get source model config: %w", err)
7272
}
7373

74-
logrus.Infof("source model config: %+v", srcModelConfig)
74+
logrus.Infof("attach: loaded source model config [%+v]", srcModelConfig)
7575

7676
var foundLayer *ocispec.Descriptor
7777
for _, layer := range srcManifest.Layers {
@@ -87,7 +87,7 @@ func (b *backend) Attach(ctx context.Context, filepath string, cfg *config.Attac
8787
}
8888
}
8989

90-
logrus.Infof("found original layer: %+v", foundLayer)
90+
logrus.Infof("attach: found existing layer for file %s [%+v]", filepath, foundLayer)
9191

9292
layers := srcManifest.Layers
9393
if foundLayer != nil {
@@ -123,7 +123,7 @@ func (b *backend) Attach(ctx context.Context, filepath string, cfg *config.Attac
123123
layers = append(layers, newLayers...)
124124
sortLayers(layers)
125125

126-
logrus.Infof("new sorted layers: %+v", layers)
126+
logrus.Debugf("attach: generated sorted layers [layers: %+v]", layers)
127127

128128
diffIDs := []godigest.Digest{}
129129
for _, layer := range layers {
@@ -145,7 +145,7 @@ func (b *backend) Attach(ctx context.Context, filepath string, cfg *config.Attac
145145
Name: srcModelConfig.Descriptor.Name,
146146
}
147147

148-
logrus.Infof("new model config: %+v", modelConfig)
148+
logrus.Infof("attach: built model config [%+v]", modelConfig)
149149

150150
configDesc, err := builder.BuildConfig(ctx, layers, modelConfig, hooks.NewHooks(
151151
hooks.WithOnStart(func(name string, size int64, reader io.Reader) io.Reader {
@@ -178,6 +178,7 @@ func (b *backend) Attach(ctx context.Context, filepath string, cfg *config.Attac
178178
return fmt.Errorf("failed to build model manifest: %w", err)
179179
}
180180

181+
logrus.Infof("attach: successfully attached file %s", filepath)
181182
return nil
182183
}
183184

pkg/backend/build.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const (
4646

4747
// Build builds the user materials into the model artifact which follows the Model Spec.
4848
func (b *backend) Build(ctx context.Context, modelfilePath, workDir, target string, cfg *config.Build) error {
49-
logrus.Infof("building model artifact: %s, cfg: %+v", target, cfg)
49+
logrus.Infof("build: starting build operation for target %s [config: %+v]", target, cfg)
5050
// parse the repo name and tag name from target.
5151
ref, err := ParseReference(target)
5252
if err != nil {
@@ -99,7 +99,7 @@ func (b *backend) Build(ctx context.Context, modelfilePath, workDir, target stri
9999

100100
layers = append(layers, layerDescs...)
101101

102-
logrus.Infof("model artifact layers: %+v", layers)
102+
logrus.Infof("build: processed layers for artifact [count: %d, layers: %+v]", len(layers), layers)
103103

104104
revision := sourceInfo.Commit
105105
if revision != "" && sourceInfo.Dirty {
@@ -118,7 +118,7 @@ func (b *backend) Build(ctx context.Context, modelfilePath, workDir, target stri
118118
SourceRevision: revision,
119119
}
120120

121-
logrus.Infof("model artifact config: %+v", modelConfig)
121+
logrus.Infof("build: built model config [family: %s, name: %s, format: %s]", modelConfig.Family, modelConfig.Name, modelConfig.Format)
122122

123123
var configDesc ocispec.Descriptor
124124
// Build the model config.
@@ -157,6 +157,7 @@ func (b *backend) Build(ctx context.Context, modelfilePath, workDir, target stri
157157
return fmt.Errorf("failed to build model manifest: %w", err)
158158
}
159159

160+
logrus.Infof("build: successfully built model artifact %s", target)
160161
return nil
161162
}
162163

pkg/backend/build/builder.go

Lines changed: 91 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"io"
2626
"os"
2727
"path/filepath"
28+
"strconv"
2829
"sync"
2930
"syscall"
3031
"time"
@@ -35,6 +36,7 @@ import (
3536
spec "github.com/opencontainers/image-spec/specs-go"
3637
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
3738
"github.com/sirupsen/logrus"
39+
"golang.org/x/sys/unix"
3840

3941
buildconfig "github.com/CloudNativeAI/modctl/pkg/backend/build/config"
4042
"github.com/CloudNativeAI/modctl/pkg/backend/build/hooks"
@@ -147,37 +149,91 @@ func (ab *abstractBuilder) BuildLayer(ctx context.Context, mediaType, workDir, p
147149
return ocispec.Descriptor{}, fmt.Errorf("failed to create codec: %w", err)
148150
}
149151

150-
logrus.Infof("building file %s...", relPath)
152+
logrus.Debugf("builder: starting build layer for file %s", relPath)
151153

152154
// Encode the content by codec depends on the media type.
153155
reader, err := codec.Encode(path, workDirPath)
154156
if err != nil {
155157
return ocispec.Descriptor{}, fmt.Errorf("failed to encode file: %w", err)
156158
}
157159

158-
logrus.Infof("calculating digest for %s...", relPath)
159-
// Calculate the digest of the encoded content.
160-
hash := sha256.New()
161-
size, err := io.Copy(hash, reader)
162-
if err != nil {
163-
return ocispec.Descriptor{}, fmt.Errorf("failed to copy content to hash: %w", err)
164-
}
165-
166-
digest := fmt.Sprintf("sha256:%x", hash.Sum(nil))
167-
logrus.Infof("calculated digest for %s: %s", relPath, digest)
160+
var (
161+
digest string
162+
size int64
163+
)
168164

169-
// Seek the reader to the beginning if supported,
170-
// otherwise we needs to re-encode the content again.
171-
if seeker, ok := reader.(io.ReadSeeker); ok {
172-
logrus.Infof("seeking %s reader to beginning...", relPath)
173-
if _, err := seeker.Seek(0, io.SeekStart); err != nil {
174-
return ocispec.Descriptor{}, fmt.Errorf("failed to seek reader: %w", err)
165+
// Try to retrieve the sha256 hash from the file xatrrs.
166+
var xattrSha256 []byte
167+
if sz, err := unix.Getxattr(path, xattrSha256Key(mediaType), xattrSha256); err != nil {
168+
logrus.Warnf("builder: failed to get xattr sha256 for file %s: %v", relPath, err)
169+
} else {
170+
xattrSha256 = make([]byte, sz)
171+
if _, err := unix.Getxattr(path, xattrSha256Key(mediaType), xattrSha256); err != nil {
172+
logrus.Warnf("builder: failed to get xattr sha256 for file %s: %v", relPath, err)
173+
} else {
174+
digest = string(xattrSha256)
175+
logrus.Infof("builder: retrieved sha256 hash from xattr for file %s [digest: %s]", relPath, digest)
175176
}
177+
}
178+
179+
// Try to retrieve the size from the file xatrrs.
180+
var xattrSize []byte
181+
if sz, err := unix.Getxattr(path, xattrSizeKey(mediaType), xattrSize); err != nil {
182+
logrus.Warnf("builder: failed to get xattr size for file %s: %v", relPath, err)
176183
} else {
177-
logrus.Infof("%s reader is not seekable, re-encoding...", relPath)
178-
reader, err = codec.Encode(path, workDirPath)
184+
xattrSize = make([]byte, sz)
185+
if _, err := unix.Getxattr(path, xattrSizeKey(mediaType), xattrSize); err != nil {
186+
logrus.Warnf("builder: failed to get xattr size for file %s: %v", relPath, err)
187+
} else {
188+
size, err = strconv.ParseInt(string(xattrSize), 10, 64)
189+
if err != nil {
190+
logrus.Warnf("Builder: failed to parse size from xattr for %s: %v", relPath, err)
191+
return ocispec.Descriptor{}, fmt.Errorf("failed to parse size from xattr: %w", err)
192+
}
193+
logrus.Infof("builder: retrieved size from xattr for file %s [size: %d]", relPath, size)
194+
}
195+
}
196+
197+
// Calculate the digest of the encoded content as not retrieved from xattrs.
198+
if digest == "" {
199+
logrus.Infof("builder: calculating digest for file %s", relPath)
200+
// Calculate the digest of the encoded content.
201+
hash := sha256.New()
202+
size, err = io.Copy(hash, reader)
179203
if err != nil {
180-
return ocispec.Descriptor{}, fmt.Errorf("failed to encode file: %w", err)
204+
return ocispec.Descriptor{}, fmt.Errorf("failed to copy content to hash: %w", err)
205+
}
206+
207+
digest = fmt.Sprintf("sha256:%x", hash.Sum(nil))
208+
logrus.Infof("builder: calculated digest for file %s [digest: %s]", relPath, digest)
209+
210+
// Seek the reader to the beginning if supported,
211+
// otherwise we needs to re-encode the content again.
212+
if seeker, ok := reader.(io.ReadSeeker); ok {
213+
logrus.Debugf("builder: seeking reader to beginning for file %s", relPath)
214+
if _, err := seeker.Seek(0, io.SeekStart); err != nil {
215+
return ocispec.Descriptor{}, fmt.Errorf("failed to seek reader: %w", err)
216+
}
217+
} else {
218+
logrus.Debugf("builder: reader not seekable, re-encoding file %s", relPath)
219+
reader, err = codec.Encode(path, workDirPath)
220+
if err != nil {
221+
return ocispec.Descriptor{}, fmt.Errorf("failed to encode file: %w", err)
222+
}
223+
}
224+
225+
// Set the digest to the xattrs.
226+
if err := unix.Setxattr(path, xattrSha256Key(mediaType), []byte(digest), 0); err != nil {
227+
logrus.Warnf("builder: failed to set xattr digest for file %s: %v", relPath, err)
228+
} else {
229+
logrus.Infof("builder: setted xattr digest for file %s [digest: %s]", relPath, digest)
230+
}
231+
232+
// Set the size to the xattrs.
233+
if err := unix.Setxattr(path, xattrSizeKey(mediaType), []byte(strconv.FormatInt(size, 10)), 0); err != nil {
234+
logrus.Warnf("builder: failed to set xattr size for file %s: %v", relPath, err)
235+
} else {
236+
logrus.Infof("builder: setted xattr size for file %s [size: %d]", relPath, size)
181237
}
182238
}
183239

@@ -224,7 +280,7 @@ func (ab *abstractBuilder) BuildLayer(ctx context.Context, mediaType, workDir, p
224280
return desc, fmt.Errorf("failed to marshal metadata: %w", err)
225281
}
226282

227-
logrus.Infof("retrieved file %s metadata: %s", relPath, string(metadataStr))
283+
logrus.Infof("builder: retrieved metadata for file %s [metadata: %s]", relPath, string(metadataStr))
228284

229285
// Apply the metadata to the descriptor annotation.
230286
if desc.Annotations == nil {
@@ -368,3 +424,17 @@ func getFileMetadata(path string) (modelspec.FileMetadata, error) {
368424

369425
return metadata, nil
370426
}
427+
428+
func xattrSha256Key(mediaType string) string {
429+
// Uniformity between linux and mac platforms is simplified by adding the prefix 'user.',
430+
// because the key may be unlimited under mac,
431+
// but on linux, in some cases, the user can only manipulate the user space.
432+
return fmt.Sprintf("user.%s.sha256", mediaType)
433+
}
434+
435+
func xattrSizeKey(mediaType string) string {
436+
// Uniformity between linux and mac platforms is simplified by adding the prefix 'user.',
437+
// because the key may be unlimited under mac,
438+
// but on linux, in some cases, the user can only manipulate the user space.
439+
return fmt.Sprintf("user.%s.size", mediaType)
440+
}

pkg/backend/build/remote.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ func (ro *remoteOutput) OutputLayer(ctx context.Context, mediaType, relPath, dig
7070

7171
if exist {
7272
// In case the reader is from PipeReader, we need to read the whole reader to avoid the pipe being blocked.
73-
io.Copy(io.Discard, reader)
73+
if _, ok := reader.(*io.PipeReader); ok {
74+
io.Copy(io.Discard, reader)
75+
}
76+
7477
hooks.OnComplete(relPath, desc)
7578
return desc, nil
7679
}

pkg/backend/extract.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const (
4040

4141
// Extract extracts the model artifact.
4242
func (b *backend) Extract(ctx context.Context, target string, cfg *config.Extract) error {
43-
logrus.Infof("extracting model artifact: %s, cfg: %+v", target, cfg)
43+
logrus.Infof("extract: starting extract operation for target %s [config: %+v]", target, cfg)
4444
// parse the repository and tag from the target.
4545
ref, err := ParseReference(target)
4646
if err != nil {
@@ -59,7 +59,7 @@ func (b *backend) Extract(ctx context.Context, target string, cfg *config.Extrac
5959
return fmt.Errorf("failed to unmarshal the manifest: %w", err)
6060
}
6161

62-
logrus.Infof("manifest: %s", string(manifestRaw))
62+
logrus.Debugf("extract: loaded manifest for target %s [manifest: %s]", target, string(manifestRaw))
6363

6464
return exportModelArtifact(ctx, b.store, manifest, repo, cfg)
6565
}
@@ -69,7 +69,7 @@ func exportModelArtifact(ctx context.Context, store storage.Storage, manifest oc
6969
g, ctx := errgroup.WithContext(ctx)
7070
g.SetLimit(cfg.Concurrency)
7171

72-
logrus.Infof("extracting %d layers in total...", len(manifest.Layers))
72+
logrus.Infof("extract: processing layers for target %s [count: %d]", repo, len(manifest.Layers))
7373
for _, layer := range manifest.Layers {
7474
g.Go(func() error {
7575
select {
@@ -78,7 +78,7 @@ func exportModelArtifact(ctx context.Context, store storage.Storage, manifest oc
7878
default:
7979
}
8080

81-
logrus.Infof("extracting layer %s...", layer.Digest.String())
81+
logrus.Debugf("extract: processing layer %s", layer.Digest.String())
8282
// pull the blob from the storage.
8383
reader, err := store.PullBlob(ctx, repo, layer.Digest.String())
8484
if err != nil {
@@ -91,13 +91,18 @@ func exportModelArtifact(ctx context.Context, store storage.Storage, manifest oc
9191
return fmt.Errorf("failed to extract layer %s: %w", layer.Digest.String(), err)
9292
}
9393

94-
logrus.Infof("extracted layer %s successfully", layer.Digest.String())
94+
logrus.Debugf("extract: successfully processed layer %s", layer.Digest.String())
9595

9696
return nil
9797
})
9898
}
9999

100-
return g.Wait()
100+
if err := g.Wait(); err != nil {
101+
return err
102+
}
103+
104+
logrus.Infof("extract: successfully extracted model artifact %s", repo)
105+
return nil
101106
}
102107

103108
// extractLayer extracts the layer to the output directory.

pkg/backend/fetch.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import (
3434

3535
// Fetch fetches partial files to the output.
3636
func (b *backend) Fetch(ctx context.Context, target string, cfg *config.Fetch) error {
37-
logrus.Infof("fetching partial files, target: %s, cfg: %+v", target, cfg)
37+
logrus.Infof("fetch: starting fetch operation for target %s [config: %+v]", target, cfg)
3838
// parse the repository and tag from the target.
3939
ref, err := ParseReference(target)
4040
if err != nil {
@@ -59,7 +59,7 @@ func (b *backend) Fetch(ctx context.Context, target string, cfg *config.Fetch) e
5959
return fmt.Errorf("failed to decode the manifest: %w", err)
6060
}
6161

62-
logrus.Infof("manifest: %+v", manifest)
62+
logrus.Debugf("fetch: loaded manifest for target %s [manifest: %+v]", target, manifest)
6363

6464
layers := []ocispec.Descriptor{}
6565
// filter the layers by patterns.
@@ -89,7 +89,7 @@ func (b *backend) Fetch(ctx context.Context, target string, cfg *config.Fetch) e
8989
g, ctx := errgroup.WithContext(ctx)
9090
g.SetLimit(cfg.Concurrency)
9191

92-
logrus.Infof("fetching %d layers in total...", len(layers))
92+
logrus.Infof("fetch: processing matched layers [count: %d]", len(layers))
9393
for _, layer := range layers {
9494
g.Go(func() error {
9595
select {
@@ -98,16 +98,20 @@ func (b *backend) Fetch(ctx context.Context, target string, cfg *config.Fetch) e
9898
default:
9999
}
100100

101-
logrus.Infof("fetching layer %s...", layer.Digest)
101+
logrus.Debugf("fetch: processing layer %s", layer.Digest)
102102
if err := pullAndExtractFromRemote(ctx, pb, internalpb.NormalizePrompt("Fetching blob"), client, cfg.Output, layer); err != nil {
103103
return err
104104
}
105105

106-
logrus.Infof("layer %s fetched successfully", layer.Digest)
106+
logrus.Debugf("fetch: successfully processed layer %s", layer.Digest)
107107
return nil
108108
})
109109
}
110110

111-
logrus.Infof("fetched %d layers in total successfully", len(layers))
112-
return g.Wait()
111+
if err := g.Wait(); err != nil {
112+
return err
113+
}
114+
115+
logrus.Infof("fetch: successfully fetched layers [count: %d]", len(layers))
116+
return nil
113117
}

0 commit comments

Comments
 (0)