Skip to content

Commit 3f9653f

Browse files
authored
fix: add nil checks for some functions (#327)
Signed-off-by: JasmineTang <jasminetang@microsoft.com>
1 parent bedc4c4 commit 3f9653f

File tree

4 files changed

+129
-7
lines changed

4 files changed

+129
-7
lines changed

copy.go

+4
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,16 @@ type CopyOptions struct {
5858
// WithTargetPlatform configures opts.MapRoot to select the manifest whose
5959
// platform matches the given platform. When MapRoot is provided, the platform
6060
// selection will be applied on the mapped root node.
61+
// - If the given platform is nil, no platform selection will be applied.
6162
// - If the root node is a manifest, it will remain the same if platform
6263
// matches, otherwise ErrNotFound will be returned.
6364
// - If the root node is a manifest list, it will be mapped to the first
6465
// matching manifest if exists, otherwise ErrNotFound will be returned.
6566
// - Otherwise ErrUnsupported will be returned.
6667
func (opts *CopyOptions) WithTargetPlatform(p *ocispec.Platform) {
68+
if p == nil {
69+
return
70+
}
6771
mapRoot := opts.MapRoot
6872
opts.MapRoot = func(ctx context.Context, src content.ReadOnlyStorage, root ocispec.Descriptor) (desc ocispec.Descriptor, err error) {
6973
if mapRoot != nil {

copy_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,33 @@ func TestCopy_WithTargetPlatformOptions(t *testing.T) {
10581058
if err.Error() != expected {
10591059
t.Fatalf("Copy() error = %v, wantErr %v", err, expected)
10601060
}
1061+
1062+
// test copy with no platform filter and nil opts.MapRoot
1063+
// opts.MapRoot should be nil
1064+
opts = oras.CopyOptions{}
1065+
opts.WithTargetPlatform(nil)
1066+
if opts.MapRoot != nil {
1067+
t.Fatal("opts.MapRoot not equal to nil when platform is not provided")
1068+
}
1069+
1070+
// test copy with no platform filter and custom opts.MapRoot
1071+
// should return ErrNotFound
1072+
opts = oras.CopyOptions{
1073+
MapRoot: func(ctx context.Context, src content.ReadOnlyStorage, root ocispec.Descriptor) (ocispec.Descriptor, error) {
1074+
if root.MediaType == "test" {
1075+
return root, nil
1076+
} else {
1077+
return ocispec.Descriptor{}, errdef.ErrNotFound
1078+
}
1079+
},
1080+
CopyGraphOptions: oras.DefaultCopyGraphOptions,
1081+
}
1082+
opts.WithTargetPlatform(nil)
1083+
1084+
_, err = oras.Copy(ctx, src, ref, dst, "", opts)
1085+
if !errors.Is(err, errdef.ErrNotFound) {
1086+
t.Fatalf("Copy() error = %v, wantErr %v", err, errdef.ErrNotFound)
1087+
}
10611088
}
10621089

10631090
func TestCopy_RestoreDuplicates(t *testing.T) {

extendedcopy.go

+12-7
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,11 @@ func findRoots(ctx context.Context, storage content.ReadOnlyGraphStorage, node o
176176
return roots, nil
177177
}
178178

179-
// FilterAnnotation will configure opts.FindPredecessors to filter the predecessors
180-
// whose annotation matches a given regex pattern. For performance consideration,
181-
// when using both FilterArtifactType and FilterAnnotation, it's recommended to call
182-
// FilterArtifactType first.
179+
// FilterAnnotation will configure opts.FindPredecessors to filter the
180+
// predecessors whose annotation matches a given regex pattern. A predecessor is
181+
// kept if the key is in its annotation and matches the regex if present.
182+
// For performance consideration, when using both FilterArtifactType and
183+
// FilterAnnotation, it's recommended to call FilterArtifactType first.
183184
func (opts *ExtendedCopyGraphOptions) FilterAnnotation(key string, regex *regexp.Regexp) {
184185
fp := opts.FindPredecessors
185186
opts.FindPredecessors = func(ctx context.Context, src content.ReadOnlyGraphStorage, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
@@ -223,7 +224,7 @@ func (opts *ExtendedCopyGraphOptions) FilterAnnotation(key string, regex *regexp
223224
}
224225
}
225226
}
226-
if regex.MatchString(p.Annotations[key]) {
227+
if value, ok := p.Annotations[key]; ok && (regex == nil || regex.MatchString(value)) {
227228
filtered = append(filtered, p)
228229
}
229230
}
@@ -232,10 +233,14 @@ func (opts *ExtendedCopyGraphOptions) FilterAnnotation(key string, regex *regexp
232233
}
233234

234235
// FilterArtifactType will configure opts.FindPredecessors to filter the predecessors
235-
// whose artifact type matches a given regex pattern. For performance consideration,
236-
// when using both FilterArtifactType and FilterAnnotation, it's recommended to call
236+
// whose artifact type matches a given regex pattern. When the regex pattern is nil,
237+
// no artifact type filter will be applied. For performance consideration, when using both
238+
// FilterArtifactType and FilterAnnotation, it's recommended to call
237239
// FilterArtifactType first.
238240
func (opts *ExtendedCopyGraphOptions) FilterArtifactType(regex *regexp.Regexp) {
241+
if regex == nil {
242+
return
243+
}
239244
fp := opts.FindPredecessors
240245
opts.FindPredecessors = func(ctx context.Context, src content.ReadOnlyGraphStorage, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
241246
var predecessors []ocispec.Descriptor

extendedcopy_test.go

+86
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,33 @@ func TestExtendedCopyGraph_FilterAnnotationWithRegex(t *testing.T) {
673673
copiedIndice := []int{0, 2, 3}
674674
uncopiedIndice := []int{1, 4, 5}
675675
verifyCopy(dst, copiedIndice, uncopiedIndice)
676+
677+
// test FilterAnnotation with key unavailable in predecessors' annotation
678+
// should return nothing
679+
dst = memory.New()
680+
opts = oras.ExtendedCopyGraphOptions{}
681+
exp = "black."
682+
regex = regexp.MustCompile(exp)
683+
opts.FilterAnnotation("bar1", regex)
684+
685+
if err := oras.ExtendedCopyGraph(ctx, src, dst, descs[0], opts); err != nil {
686+
t.Fatalf("ExtendedCopyGraph() error = %v, wantErr %v", err, false)
687+
}
688+
copiedIndice = []int{0}
689+
uncopiedIndice = []int{1, 2, 3, 4, 5}
690+
verifyCopy(dst, copiedIndice, uncopiedIndice)
691+
692+
//test FilterAnnotation with key available in predecessors' annotation, regex equal to nil
693+
//should return all predecessors with the provided key
694+
dst = memory.New()
695+
opts = oras.ExtendedCopyGraphOptions{}
696+
opts.FilterAnnotation("bar", nil)
697+
if err := oras.ExtendedCopyGraph(ctx, src, dst, descs[0], opts); err != nil {
698+
t.Fatalf("ExtendedCopyGraph() error = %v, wantErr %v", err, false)
699+
}
700+
copiedIndice = []int{0, 1, 2, 3, 4, 5}
701+
uncopiedIndice = []int{}
702+
verifyCopy(dst, copiedIndice, uncopiedIndice)
676703
}
677704

678705
func TestExtendedCopyGraph_FilterAnnotationWithMultipleRegex(t *testing.T) {
@@ -746,6 +773,39 @@ func TestExtendedCopyGraph_FilterAnnotationWithMultipleRegex(t *testing.T) {
746773
copiedIndice := []int{0, 2}
747774
uncopiedIndice := []int{1, 3, 4, 5, 6}
748775
verifyCopy(dst, copiedIndice, uncopiedIndice)
776+
777+
// test extended copy by descs[0] with three annotation filters, nil included
778+
dst = memory.New()
779+
opts = oras.ExtendedCopyGraphOptions{}
780+
exp1 = "black."
781+
exp2 = ".pink|red"
782+
regex1 = regexp.MustCompile(exp1)
783+
regex2 = regexp.MustCompile(exp2)
784+
opts.FilterAnnotation("bar", regex1)
785+
opts.FilterAnnotation("bar", nil)
786+
opts.FilterAnnotation("bar", regex2)
787+
if err := oras.ExtendedCopyGraph(ctx, src, dst, descs[0], opts); err != nil {
788+
t.Fatalf("ExtendedCopyGraph() error = %v, wantErr %v", err, false)
789+
}
790+
copiedIndice = []int{0, 2}
791+
uncopiedIndice = []int{1, 3, 4, 5, 6}
792+
verifyCopy(dst, copiedIndice, uncopiedIndice)
793+
794+
// test extended copy by descs[0] with two annotation filters, the second filter has an unavailable key
795+
dst = memory.New()
796+
opts = oras.ExtendedCopyGraphOptions{}
797+
exp1 = "black."
798+
exp2 = ".pink|red"
799+
regex1 = regexp.MustCompile(exp1)
800+
regex2 = regexp.MustCompile(exp2)
801+
opts.FilterAnnotation("bar", regex1)
802+
opts.FilterAnnotation("test", regex2)
803+
if err := oras.ExtendedCopyGraph(ctx, src, dst, descs[0], opts); err != nil {
804+
t.Fatalf("ExtendedCopyGraph() error = %v, wantErr %v", err, false)
805+
}
806+
copiedIndice = []int{0}
807+
uncopiedIndice = []int{1, 2, 3, 4, 5, 6}
808+
verifyCopy(dst, copiedIndice, uncopiedIndice)
749809
}
750810

751811
func TestExtendedCopyGraph_FilterAnnotationWithRegexNoAnnotationInDescriptor(t *testing.T) {
@@ -887,6 +947,14 @@ func TestExtendedCopyGraph_FilterArtifactTypeWithRegex(t *testing.T) {
887947
copiedIndice := []int{0, 1, 3, 4}
888948
uncopiedIndice := []int{2, 5}
889949
verifyCopy(dst, copiedIndice, uncopiedIndice)
950+
951+
// test extended copy by descs[0] with no regex
952+
// type matches exp.
953+
opts = oras.ExtendedCopyGraphOptions{}
954+
opts.FilterArtifactType(nil)
955+
if opts.FindPredecessors != nil {
956+
t.Fatal("FindPredecessors not nil!")
957+
}
890958
}
891959

892960
func TestExtendedCopyGraph_FilterArtifactTypeWithMultipleRegex(t *testing.T) {
@@ -962,6 +1030,24 @@ func TestExtendedCopyGraph_FilterArtifactTypeWithMultipleRegex(t *testing.T) {
9621030
copiedIndice := []int{0, 3, 4}
9631031
uncopiedIndice := []int{1, 2, 5}
9641032
verifyCopy(dst, copiedIndice, uncopiedIndice)
1033+
1034+
// test extended copy by descs[0], include the predecessors whose artifact
1035+
// type matches exp1 and exp2 and nil
1036+
exp1 = ".foo|bar."
1037+
exp2 = "bad."
1038+
dst = memory.New()
1039+
opts = oras.ExtendedCopyGraphOptions{}
1040+
regex1 = regexp.MustCompile(exp1)
1041+
regex2 = regexp.MustCompile(exp2)
1042+
opts.FilterArtifactType(regex1)
1043+
opts.FilterArtifactType(regex2)
1044+
opts.FilterArtifactType(nil)
1045+
if err := oras.ExtendedCopyGraph(ctx, src, dst, descs[0], opts); err != nil {
1046+
t.Errorf("ExtendedCopyGraph() error = %v, wantErr %v", err, false)
1047+
}
1048+
copiedIndice = []int{0, 3, 4}
1049+
uncopiedIndice = []int{1, 2, 5}
1050+
verifyCopy(dst, copiedIndice, uncopiedIndice)
9651051
}
9661052

9671053
func TestExtendedCopyGraph_FilterArtifactTypeByReferrersWithMultipleRegex(t *testing.T) {

0 commit comments

Comments
 (0)