Skip to content

Commit b334950

Browse files
committed
manifest: support nerdctl manifest rm command
support nerdctl manifest rm command Signed-off-by: ChengyuZhu6 <[email protected]>
1 parent c6e2f70 commit b334950

File tree

6 files changed

+136
-0
lines changed

6 files changed

+136
-0
lines changed

cmd/nerdctl/manifest/manifest.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func Command() *cobra.Command {
3636
InspectCommand(),
3737
CreateCommand(),
3838
AnnotateCommand(),
39+
RemoveCommand(),
3940
)
4041

4142
return cmd
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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 manifest
18+
19+
import (
20+
"errors"
21+
22+
"github.com/spf13/cobra"
23+
24+
"github.com/containerd/nerdctl/v2/cmd/nerdctl/completion"
25+
"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
26+
"github.com/containerd/nerdctl/v2/pkg/cmd/manifest"
27+
)
28+
29+
func RemoveCommand() *cobra.Command {
30+
var cmd = &cobra.Command{
31+
Use: "rm INDEX/MANIFESTLIST [INDEX/MANIFESTLIST...]",
32+
Short: "Remove one or more index/manifest lists",
33+
Args: cobra.MinimumNArgs(1),
34+
RunE: removeAction,
35+
ValidArgsFunction: removeShellComplete,
36+
SilenceUsage: true,
37+
SilenceErrors: true,
38+
}
39+
return cmd
40+
}
41+
42+
func removeAction(cmd *cobra.Command, refs []string) error {
43+
globalOptions, err := helpers.ProcessRootCmdFlags(cmd)
44+
if err != nil {
45+
return err
46+
}
47+
var errs []error
48+
for _, ref := range refs {
49+
err := manifest.Remove(cmd.Context(), ref, globalOptions)
50+
if err != nil {
51+
errs = append(errs, err)
52+
}
53+
}
54+
return errors.Join(errs...)
55+
}
56+
57+
func removeShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
58+
return completion.ImageNames(cmd)
59+
}

pkg/cmd/manifest/rm.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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 manifest
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"strings"
23+
24+
"github.com/containerd/nerdctl/v2/pkg/api/types"
25+
"github.com/containerd/nerdctl/v2/pkg/manifeststore"
26+
"github.com/containerd/nerdctl/v2/pkg/manifestutil"
27+
"github.com/containerd/nerdctl/v2/pkg/referenceutil"
28+
)
29+
30+
func Remove(ctx context.Context, ref string, options types.GlobalCommandOptions) error {
31+
parsedRef, err := referenceutil.Parse(ref)
32+
if err != nil {
33+
return fmt.Errorf("failed to parse reference: %w", err)
34+
}
35+
manifestStore, err := manifeststore.NewStore(options.DataRoot)
36+
if err != nil {
37+
return fmt.Errorf("failed to create manifest store: %w", err)
38+
}
39+
_, err = manifestStore.GetList(parsedRef)
40+
if err != nil {
41+
if strings.Contains(err.Error(), "not found") {
42+
return manifestutil.NewNoSuchManifestError(parsedRef.String())
43+
}
44+
return err
45+
}
46+
err = manifestStore.Remove(parsedRef)
47+
if err != nil {
48+
return fmt.Errorf("failed to remove manifest list: %w", err)
49+
}
50+
return nil
51+
}

pkg/manifeststore/manifeststore.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ type Store interface {
3333
GetList(listRef *referenceutil.ImageReference) ([]*manifesttypes.DockerManifestEntry, error)
3434
// Save saves a manifest as part of a index or local manifest list
3535
Save(listRef, manifestRef *referenceutil.ImageReference, manifest *manifesttypes.DockerManifestEntry) error
36+
// Remove removes a index or local manifest list
37+
Remove(listRef *referenceutil.ImageReference) error
3638
}
3739

3840
type manifestStore struct {
@@ -103,6 +105,13 @@ func (s *manifestStore) Save(listRef, manifestRef *referenceutil.ImageReference,
103105
})
104106
}
105107

108+
func (s *manifestStore) Remove(listRef *referenceutil.ImageReference) error {
109+
return s.store.WithLock(func() error {
110+
listPath := makeFilesafeName(listRef.String())
111+
return s.store.Delete(listPath)
112+
})
113+
}
114+
106115
func (s *manifestStore) getManifestFromPath(listPath, manifestPath string) (*manifesttypes.DockerManifestEntry, error) {
107116
data, err := s.store.Get(listPath, manifestPath)
108117
if err != nil {

pkg/manifestutil/manifestutils.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,20 @@ var manifestParsers = map[string]manifestParser{
4545
ocispec.MediaTypeImageIndex: parseOCIIndex,
4646
}
4747

48+
// NoSuchManifestError represents an error when a manifest is not found
49+
type NoSuchManifestError struct {
50+
Ref string
51+
}
52+
53+
func (e *NoSuchManifestError) Error() string {
54+
return fmt.Sprintf("No such manifest: %s", e.Ref)
55+
}
56+
57+
// NewNoSuchManifestError creates a new NoSuchManifestError
58+
func NewNoSuchManifestError(ref string) error {
59+
return &NoSuchManifestError{Ref: ref}
60+
}
61+
4862
// ParseManifest parses manifest data based on media type
4963
func ParseManifest(mediaType string, data []byte) (interface{}, error) {
5064
if parser, exists := manifestParsers[mediaType]; exists {

pkg/testutil/images.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ alpine:
1515
manifest: "sha256:e103c1b4bf019dc290bcc7aca538dc2bf7a9d0fc836e186f5fa34945c5168310"
1616
config: "sha256:49f356fa4513676c5e22e3a8404aad6c7262cc7aaed15341458265320786c58c"
1717
raw: "ewogICAic2NoZW1hVmVyc2lvbiI6IDIsCiAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5kaXN0cmlidXRpb24ubWFuaWZlc3QudjIranNvbiIsCiAgICJjb25maWciOiB7CiAgICAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5jb250YWluZXIuaW1hZ2UudjEranNvbiIsCiAgICAgICJzaXplIjogMTQ3MiwKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6NDlmMzU2ZmE0NTEzNjc2YzVlMjJlM2E4NDA0YWFkNmM3MjYyY2M3YWFlZDE1MzQxNDU4MjY1MzIwNzg2YzU4YyIKICAgfSwKICAgImxheWVycyI6IFsKICAgICAgewogICAgICAgICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLAogICAgICAgICAic2l6ZSI6IDI4MTE5NDcsCiAgICAgICAgICJkaWdlc3QiOiAic2hhMjU2OmNhM2NkNDJhN2M5NTI1ZjZjZTNkNjRjMWE3MDk4MjYxM2E4MjM1ZjBjYzA1N2VjOTI0NDA1MjkyMTg1M2VmMTUiCiAgICAgIH0KICAgXQp9"
18+
linux/arm64:
19+
manifest: "sha256:071fa5de01a240dbef5be09d69f8fef2f89d68445d9175393773ee389b6f5935"
1820

1921
busybox:
2022
ref: "ghcr.io/containerd/busybox"

0 commit comments

Comments
 (0)