Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions cmd/attach.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright 2025 The CNAI Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cmd

import (
"context"
"fmt"
"time"

"github.com/briandowns/spinner"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/CloudNativeAI/modctl/pkg/backend"
"github.com/CloudNativeAI/modctl/pkg/config"
)

var attachConfig = config.NewAttach()

// attachCmd represents the modctl command for attach.
var attachCmd = &cobra.Command{
Use: "attach [flags] <file>",
Short: "A command line tool for modctl attach",
Args: cobra.ExactArgs(1),
DisableAutoGenTag: true,
SilenceUsage: true,
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
RunE: func(cmd *cobra.Command, args []string) error {
if err := attachConfig.Validate(); err != nil {
return err
}

return runAttach(context.Background(), args[0])
},
}

// init initializes build command.
func init() {
flags := attachCmd.Flags()
flags.StringVarP(&attachConfig.Source, "source", "s", "", "source model artifact name")
flags.StringVarP(&attachConfig.Target, "target", "t", "", "target model artifact name")
flags.BoolVarP(&attachConfig.OutputRemote, "output-remote", "", false, "turning on this flag will output model artifact to remote registry directly")
flags.BoolVarP(&attachConfig.PlainHTTP, "plain-http", "", false, "turning on this flag will use plain HTTP instead of HTTPS")
flags.BoolVarP(&attachConfig.Insecure, "insecure", "", false, "turning on this flag will disable TLS verification")
flags.BoolVarP(&attachConfig.Force, "force", "f", false, "turning on this flag will force the attach, which will overwrite the layer if it already exists with same filepath")
flags.BoolVar(&attachConfig.Nydusify, "nydusify", false, "[EXPERIMENTAL] nydusify the model artifact")
flags.MarkHidden("nydusify")

if err := viper.BindPFlags(flags); err != nil {
panic(fmt.Errorf("bind cache list flags to viper: %w", err))
}
}

// runAttach runs the attach modctl.
func runAttach(ctx context.Context, filepath string) error {
b, err := backend.New(rootConfig.StoargeDir)
if err != nil {
return err
}

if err := b.Attach(ctx, filepath, attachConfig); err != nil {
return err
}

fmt.Printf("Successfully attached model artifact: %s\n", attachConfig.Target)

// nydusify the model artifact if needed.
if attachConfig.Nydusify {
sp := spinner.New(spinner.CharSets[39], 100*time.Millisecond, spinner.WithSuffix("Nydusifying..."))
sp.Start()
defer sp.Stop()

nydusName, err := b.Nydusify(ctx, attachConfig.Target)
if err != nil {
err = fmt.Errorf("failed to nydusify %s: %w", attachConfig.Target, err)
sp.FinalMSG = err.Error()
return err
}

sp.FinalMSG = fmt.Sprintf("Successfully nydusify model artifact: %s", nydusName)
}

return nil
}
12 changes: 10 additions & 2 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ package cmd
import (
"context"
"fmt"
"time"

"github.com/CloudNativeAI/modctl/pkg/backend"
"github.com/CloudNativeAI/modctl/pkg/config"
"github.com/briandowns/spinner"

"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -78,12 +80,18 @@ func runBuild(ctx context.Context, workDir string) error {

// nydusify the model artifact if needed.
if buildConfig.Nydusify {
sp := spinner.New(spinner.CharSets[39], 100*time.Millisecond, spinner.WithSuffix("Nydusifying..."))
sp.Start()
defer sp.Stop()

nydusName, err := b.Nydusify(ctx, buildConfig.Target)
if err != nil {
return fmt.Errorf("failed to nydusify %s: %w", buildConfig.Target, err)
err = fmt.Errorf("failed to nydusify %s: %w", buildConfig.Target, err)
sp.FinalMSG = err.Error()
return err
}

fmt.Printf("Successfully nydusify model artifact: %s\n", nydusName)
sp.FinalMSG = fmt.Sprintf("Successfully nydusify model artifact: %s", nydusName)
}

return nil
Expand Down
12 changes: 10 additions & 2 deletions cmd/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ package cmd
import (
"context"
"fmt"
"time"

"github.com/CloudNativeAI/modctl/pkg/backend"
"github.com/CloudNativeAI/modctl/pkg/config"

"github.com/briandowns/spinner"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand Down Expand Up @@ -75,12 +77,18 @@ func runPush(ctx context.Context, target string) error {

// nydusify the model artifact if needed.
if pushConfig.Nydusify {
sp := spinner.New(spinner.CharSets[39], 100*time.Millisecond, spinner.WithSuffix("Nydusifying..."))
sp.Start()
defer sp.Stop()

nydusName, err := b.Nydusify(ctx, target)
if err != nil {
return fmt.Errorf("failed to nydusify %s: %w", target, err)
err = fmt.Errorf("failed to nydusify %s: %w", target, err)
sp.FinalMSG = err.Error()
return err
}

fmt.Printf("Successfully nydusify model artifact: %s\n", nydusName)
sp.FinalMSG = fmt.Sprintf("Successfully nydusify model artifact: %s", nydusName)
}

return nil
Expand Down
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,6 @@ func init() {
rootCmd.AddCommand(extractCmd)
rootCmd.AddCommand(tagCmd)
rootCmd.AddCommand(fetchCmd)
rootCmd.AddCommand(attachCmd)
rootCmd.AddCommand(modelfile.RootCmd)
}
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.24.1

require (
github.com/CloudNativeAI/model-spec v0.0.3
github.com/briandowns/spinner v1.23.2
github.com/distribution/distribution/v3 v3.0.0-rc.3
github.com/distribution/reference v0.6.0
github.com/dustin/go-humanize v1.0.1
Expand All @@ -29,6 +30,7 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
Expand All @@ -40,6 +42,8 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.8 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w=
github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
Expand All @@ -29,6 +31,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
Expand Down Expand Up @@ -81,6 +85,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
Expand Down Expand Up @@ -218,6 +226,7 @@ golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
Loading