Skip to content

Commit 6f2ad96

Browse files
committed
image: support import command
Signed-off-by: ChengyuZhu6 <[email protected]>
1 parent ca6ced0 commit 6f2ad96

File tree

5 files changed

+418
-0
lines changed

5 files changed

+418
-0
lines changed

cmd/nerdctl/image/image.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func Command() *cobra.Command {
4141
PushCommand(),
4242
LoadCommand(),
4343
SaveCommand(),
44+
ImportCommand(),
4445
TagCommand(),
4546
imageRemoveCommand(),
4647
convertCommand(),

cmd/nerdctl/image/image_import.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package image
18+
19+
import (
20+
"fmt"
21+
"io"
22+
"net/http"
23+
"os"
24+
"strings"
25+
26+
"github.com/spf13/cobra"
27+
28+
"github.com/containerd/nerdctl/v2/cmd/nerdctl/completion"
29+
"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
30+
"github.com/containerd/nerdctl/v2/pkg/api/types"
31+
"github.com/containerd/nerdctl/v2/pkg/clientutil"
32+
"github.com/containerd/nerdctl/v2/pkg/cmd/image"
33+
)
34+
35+
func ImportCommand() *cobra.Command {
36+
var cmd = &cobra.Command{
37+
Use: "import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]",
38+
Short: "Import the contents from a tarball to create a filesystem image",
39+
Args: cobra.MinimumNArgs(1),
40+
RunE: importAction,
41+
ValidArgsFunction: imageImportShellComplete,
42+
SilenceUsage: true,
43+
SilenceErrors: true,
44+
}
45+
46+
cmd.Flags().StringP("message", "m", "", "Set commit message for imported image")
47+
cmd.Flags().String("platform", "", "Set platform for imported image (e.g., linux/amd64)")
48+
return cmd
49+
}
50+
51+
func importOptions(cmd *cobra.Command, args []string) (types.ImageImportOptions, error) {
52+
globalOptions, err := helpers.ProcessRootCmdFlags(cmd)
53+
if err != nil {
54+
return types.ImageImportOptions{}, err
55+
}
56+
message, err := cmd.Flags().GetString("message")
57+
if err != nil {
58+
return types.ImageImportOptions{}, err
59+
}
60+
platform, err := cmd.Flags().GetString("platform")
61+
if err != nil {
62+
return types.ImageImportOptions{}, err
63+
}
64+
var reference string
65+
if len(args) > 1 {
66+
reference = args[1]
67+
}
68+
69+
var in io.ReadCloser
70+
src := args[0]
71+
switch {
72+
case src == "-":
73+
in = io.NopCloser(cmd.InOrStdin())
74+
case hasHTTPPrefix(src):
75+
resp, err := http.Get(src)
76+
if err != nil {
77+
return types.ImageImportOptions{}, err
78+
}
79+
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
80+
defer resp.Body.Close()
81+
return types.ImageImportOptions{}, fmt.Errorf("failed to download %s: %s", src, resp.Status)
82+
}
83+
in = resp.Body
84+
default:
85+
f, err := os.Open(src)
86+
if err != nil {
87+
return types.ImageImportOptions{}, err
88+
}
89+
in = f
90+
}
91+
92+
return types.ImageImportOptions{
93+
Stdout: cmd.OutOrStdout(),
94+
Stdin: in,
95+
GOptions: globalOptions,
96+
Source: args[0],
97+
Reference: reference,
98+
Message: message,
99+
Platform: platform,
100+
}, nil
101+
}
102+
103+
func importAction(cmd *cobra.Command, args []string) error {
104+
opt, err := importOptions(cmd, args)
105+
if err != nil {
106+
return err
107+
}
108+
client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), opt.GOptions.Namespace, opt.GOptions.Address)
109+
if err != nil {
110+
return err
111+
}
112+
defer cancel()
113+
defer func() {
114+
if rc, ok := opt.Stdin.(io.ReadCloser); ok {
115+
_ = rc.Close()
116+
}
117+
}()
118+
119+
name, err := image.Import(ctx, client, opt)
120+
if err != nil {
121+
return err
122+
}
123+
_, err = cmd.OutOrStdout().Write([]byte(name + "\n"))
124+
return err
125+
}
126+
127+
func imageImportShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
128+
return completion.ImageNames(cmd)
129+
}
130+
131+
func hasHTTPPrefix(s string) bool {
132+
return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://")
133+
}

cmd/nerdctl/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ Config file ($NERDCTL_TOML): %s
304304
image.PushCommand(),
305305
image.LoadCommand(),
306306
image.SaveCommand(),
307+
image.ImportCommand(),
307308
image.TagCommand(),
308309
image.RmiCommand(),
309310
image.HistoryCommand(),

pkg/api/types/import_types.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package types
18+
19+
import "io"
20+
21+
// ImageImportOptions specifies options for `nerdctl (image) import`.
22+
type ImageImportOptions struct {
23+
Stdout io.Writer
24+
Stdin io.Reader
25+
GOptions GlobalCommandOptions
26+
27+
Source string
28+
Reference string
29+
Message string
30+
Platform string
31+
}

0 commit comments

Comments
 (0)