From 4bb26727805f9879f9d1d6fa2db1fa59f922a98f Mon Sep 17 00:00:00 2001 From: Adam Hughes <9903835+tri-adam@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:29:53 +0000 Subject: [PATCH] feat: accept nil match.Matcher Allow the caller to pass a nil value for a match.Matcher in the public APIs, with the semantics of "match all". --- pkg/sif/image.go | 9 ++++-- pkg/sif/image_test.go | 6 ++++ pkg/sif/index.go | 9 ++++-- pkg/sif/index_test.go | 6 ++++ pkg/sif/match.go | 9 ++++++ .../TestRemoveManifests/NilMatcher.golden | Bin 0 -> 25831 bytes .../TestReplace/ReplaceImageNilMatcher.golden | Bin 0 -> 12335 bytes .../TestReplace/ReplaceIndexNilMatcher.golden | Bin 0 -> 12575 bytes pkg/sif/update.go | 27 +++++++++++++----- pkg/sif/update_test.go | 17 +++++++++++ 10 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 pkg/sif/match.go create mode 100644 pkg/sif/testdata/TestRemoveManifests/NilMatcher.golden create mode 100644 pkg/sif/testdata/TestReplace/ReplaceImageNilMatcher.golden create mode 100644 pkg/sif/testdata/TestReplace/ReplaceIndexNilMatcher.golden diff --git a/pkg/sif/image.go b/pkg/sif/image.go index 3835ac3..bee9068 100644 --- a/pkg/sif/image.go +++ b/pkg/sif/image.go @@ -28,15 +28,18 @@ var ( errMultipleMatchingImages = errors.New("multiple images match criteria") ) -// Image returns a single Image stored in f, that is selected by the provided -// Matcher. If more than one image matches, or no image matches, an error is -// returned. +// Image returns a single Image stored in f, that is selected by m. If m is nil, all manifests are +// selected. If more than one image matches, or no image matches, an error is returned. func (f *OCIFileImage) Image(m match.Matcher, _ ...Option) (v1.Image, error) { ri, err := f.RootIndex() if err != nil { return nil, err } + if m == nil { + m = matchAll + } + matches, err := partial.FindImages(ri, m) if err != nil { return nil, err diff --git a/pkg/sif/image_test.go b/pkg/sif/image_test.go index c274570..433e6b8 100644 --- a/pkg/sif/image_test.go +++ b/pkg/sif/image_test.go @@ -106,6 +106,12 @@ func Test_OCIFileImage_Image(t *testing.T) { wantImage: nil, wantErr: true, }, + { + name: "NilMatcher", + matcher: nil, + wantImage: nil, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/sif/index.go b/pkg/sif/index.go index ee17ae2..480c177 100644 --- a/pkg/sif/index.go +++ b/pkg/sif/index.go @@ -72,15 +72,18 @@ var ( errMultipleMatchingIndices = errors.New("multiple indices match criteria") ) -// Index returns a single ImageIndex stored in f, that is selected by the provided -// Matcher. If more than one index matches, or no index matches, an error is -// returned. +// Index returns a single ImageIndex stored in f, that is selected by m. If m is nil, all manifests +// are selected. If more than one index matches, or no image matches, an error is returned. func (f *OCIFileImage) Index(m match.Matcher, _ ...Option) (v1.ImageIndex, error) { ri, err := f.RootIndex() if err != nil { return nil, err } + if m == nil { + m = matchAll + } + matches, err := partial.FindIndexes(ri, m) if err != nil { return nil, err diff --git a/pkg/sif/index_test.go b/pkg/sif/index_test.go index 53afe5e..fa08116 100644 --- a/pkg/sif/index_test.go +++ b/pkg/sif/index_test.go @@ -163,6 +163,12 @@ func Test_OCIFileImage_Index(t *testing.T) { wantIndex: nil, wantErr: true, }, + { + name: "NilMatcher", + matcher: nil, + wantIndex: nil, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/sif/match.go b/pkg/sif/match.go new file mode 100644 index 0000000..38eaa91 --- /dev/null +++ b/pkg/sif/match.go @@ -0,0 +1,9 @@ +// Copyright 2024 Sylabs Inc. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 + +package sif + +import v1 "github.com/google/go-containerregistry/pkg/v1" + +func matchAll(v1.Descriptor) bool { return true } diff --git a/pkg/sif/testdata/TestRemoveManifests/NilMatcher.golden b/pkg/sif/testdata/TestRemoveManifests/NilMatcher.golden new file mode 100644 index 0000000000000000000000000000000000000000..dc375f5350ba10247516b4c38cc6d96af2998dee GIT binary patch literal 25831 zcmeI#ze@u#6u|Lx)XBf$&`CKjY47Kx2ucShi-U{ga;c59*W%Tph`USwSN#jFSK2yM za8l^-4TKkx_ek>Do)Eu}o9^ZP_4!rzLIhF-!O!Hq^5d(nYq5@p>x`pK=D}()@2z98 zig%lHA_52?fB*srAb57(x?Vn zg;5r&)I>Uu8K#pQIx7gN*nf5+wSCYm%hYX z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5cpGp zH?J}SJJPrIsdD4e%hZWCvW3&V*NOEqPfsR8XY{l=JDrtUNmLNOC_=vYp)IUdkQ8)1lbI=n7LC-x2JUco&d%J& zIp=r(u*{sz<^K8D(fvg4vyVR6y)}pQ970zQzNxzZ*G8j}OYPcp#4Xv#^~sn1dsEw) z*tzy>fU=5!BA^H;0*Zhlpa>`eihv@Z2q*%IfFf|C39ReNG@B3x^WUO&El+Ya!ir zB_b|?3msqM1xM3l*xZ3E3#k=i%!KrGAqjYEtS&e*ds#r)o1>(=Y0jyTuxnD?bU86W zP=@2$#AVE|ZLTBNkfuWo+k-noxLkJx^(=>(29_3~RCw6sO_R@R3uIYHZ4h-Pq{+=m z3P)2RWpRIN7N#S0&mvf-I;KoI5)*_RizAy8DohJ;>|kj)29l0J33WYp zwe4Ti#CvlWXefTooC(R=lEii?4KcH;L`W7!OoEzBM;N*bzgCvbsEMU;1*XE5LNbP# z!Cm19KoL*`6ahs*5l{qf z1cCbxB>#?WZ=7HF@^}lmb4~r!r$2rFMMr-7Q&YG19a@+U-g{`mnSTG^c6{W_{zHcg zd_%{}tcfW1jsdw924tSG~-&Vi-+;b=QZ7a1dyfptyy|`HXas0wu zW9g66FTVE7>?a+)Q>Q=f*%LnS$d04mj(p&mYsQZ}R(^Z!#@CnbV9!@SoSpsVLR9_X z;mLQ--M7>`ad^|4m%iGN%QYG=d_LWtgUm)X60))%2^I~8LMv%nL1UpHk>GHF5Yjg< zx19);mfIc}Q1)w45Dsgly)b<+DhDBq4M~*;m11{=^of-$l6vW-uueKTo)%Opq8h=i z2)yK>k$kdpKJ}VM`9S7%ZLFuD1vMB5v%(;VBzzy$qTzu!i+(aJ!W#5&U&>yeL*HI4 zEfNWCn#_ZfC3NN?Dh;S7LcK`m#I~8M3sbi!l!!c?BPb^sg18pPmaXdy8Mbh>p0PO2 zs`UH7_jRDCIGQE|xV+*gA|uJEvu#UPxB_*GcekNFpe^lX`$KLS9ofnyeZ-~#CUPt`IF zIHaIsC9ELlB)DX^0DzRmh=j$y5q|&@P9im~73uXBh(Rp>d7A>}r!f607q4D{G~29L YR4YpR-?lo*94e7l@6q)ZqTb%7 literal 0 HcmV?d00001 diff --git a/pkg/sif/testdata/TestReplace/ReplaceIndexNilMatcher.golden b/pkg/sif/testdata/TestReplace/ReplaceIndexNilMatcher.golden new file mode 100644 index 0000000000000000000000000000000000000000..cdec4e36dd19296defc996d4a4033e03d9cb7d13 GIT binary patch literal 12575 zcmeI1U1%It6vsC)ZH*tWl|D!dx(wC_lWgwXnYnW(g2bd&f+i>_B_x#OekN11yXo#E zn8a8O1Z|6=XsPDK2SpK4TB}c?#!5vHsrXQ=_+o2N1XJ3A4~p%2XJ@7a;zuMFci|4R zb7tm#oO6En&oHwmmHPXww`=EM_x%rcb*6xs0$_FL#{9FF%H?t@wwvM}w_=co|wwiz@pb2OKnt&#t31|YEfF_^`XabsmCUCt8+}xf@)zv94>US}8HX47R zpARq8me}M2|C)`OtL9i5A<#i>4)z$P(uEue3<(3KO@FA%~1-Sp}N$T*lPkx{yB=8tL7LB$s)*h3BepW z(&m7=*d&;F(i5hGiRDnJRO1TZQ054eNXI0c2@hJHAQrYs)#OuafmDQagNiy4(u*xo z3U9?iN^Fe?N!Zi`OuEuWTu{%pxMbY5Fp=t+#F(Ij0^vwk>5P~*vJr$5A)c1A>jccECMXgaxG~|3QcGV1TblMip3C4Ah7pYqgR$}vjdXK^`K_=(Sk^E1JK()4_I$ljc6VL=S0Zl*?&;&FA zO+XXS1T+CnKoiget`C7b58YUqxS{;>mkS3!Y2UK(yKgp~edyTHZ=e_U>~-?_{<%f6>%;XzuWwxM!eoc6SGOVE)&heFuAg z|MBZHQ^y|d{k;FfH%>j>*!S{*hwFCSeV~5sm9J2`jf^ydBN&b>bW%jK=htv#q;@YK@ct0%5{6Ib8d zcKn5*p6x#c`<51uoNlq+zOwYh*^g#;%a$iMJpVnu(0D{;R4y;xc49H5lAAV2k&XC) zV8M7%7;T2pY_Osrf#B)_0ARLM_FVw9RrY?>VZ<*5h2o^qHlT)&2P1`|>RTjvUdUy; za%5PnWs=y-AXW3kgTrBkTu$Tzbt_O_GHNH8Xy#1pH3KpQnK6x-JU`P=Cda}oM#-comQ)M(r5yCRY8x=(I+1j3DhY&w2`nJTKwa35Wn+q{=L(x!2%FL~ zEz?AlL&!V`1fU+Yp=k;xjXg7AoO$UFD~~fX(?*Gnh#c9iJo<&a(FU81oZvp$6~#iv z#zuWc0@c^=nV2_DnmMe%wpN@z3vpJIYr6>j23D~r^q$XM6VJ(xXa024z)wF)d zKRaP2)0F%v1;m0@$YNNzP;~yIf|;Q>#GpeZtRd$pxM;N$04cKpDQ1VK{4teq6e)yg zb)@U95i7C$>t!l1KZfb&xp;g9(qyqhQLQQMe_HA&b5-45o%woeQU6!we@!o5W(TZJ LcD?nIFxfu<101YK literal 0 HcmV?d00001 diff --git a/pkg/sif/update.go b/pkg/sif/update.go index 20fa460..d51301b 100644 --- a/pkg/sif/update.go +++ b/pkg/sif/update.go @@ -461,31 +461,40 @@ func (f *OCIFileImage) RemoveBlob(hash v1.Hash) error { sif.OptDeleteCompact(true)) } -// RemoveManifests modifies the SIF file associated with f so that its RootIndex -// no longer holds manifests selected by matcher. Any blobs in the SIF that are -// no longer referenced are removed from the SIF. +// RemoveManifests modifies the SIF file associated with f so that its +// RootIndex no longer holds manifests selected by matcher. If m is nil, all +// manifests are selected. Any blobs in the SIF that are no longer referenced +// are removed from the SIF. func (f *OCIFileImage) RemoveManifests(matcher match.Matcher) error { ri, err := f.RootIndex() if err != nil { return err } + + if matcher == nil { + matcher = matchAll + } + return f.UpdateRootIndex(mutate.RemoveManifests(ri, matcher)) } // ReplaceImage writes img to the SIF, replacing any existing manifest that is -// selected by the matcher. Any blobs in the SIF that are no longer referenced -// are removed from the SIF. +// selected by the matcher. If m is nil, all manifests are selected. Any blobs +// in the SIF that are no longer referenced are removed from the SIF. func (f *OCIFileImage) ReplaceImage(img v1.Image, matcher match.Matcher, opts ...AppendOpt) error { return f.replace(img, matcher, opts...) } // ReplaceIndex writes ii to the SIF, replacing any existing manifest that is -// selected by the matcher. Any blobs in the SIF that are no longer referenced -// are removed from the SIF. +// selected by the matcher. If m is nil, all manifests are selected. Any blobs +// in the SIF that are no longer referenced are removed from the SIF. func (f *OCIFileImage) ReplaceIndex(ii v1.ImageIndex, matcher match.Matcher, opts ...AppendOpt) error { return f.replace(ii, matcher, opts...) } +// replace writes add to the SIF, replacing any existing manifest that is +// selected by the matcher. If m is nil, all manifests are selected. Any blobs +// in the SIF that are no longer referenced are removed from the SIF. func (f *OCIFileImage) replace(add mutate.Appendable, matcher match.Matcher, opts ...AppendOpt) error { ao := appendOpts{ tempDir: os.TempDir(), @@ -496,6 +505,10 @@ func (f *OCIFileImage) replace(add mutate.Appendable, matcher match.Matcher, opt } } + if matcher == nil { + matcher = matchAll + } + ri, err := f.RootIndex() if err != nil { return err diff --git a/pkg/sif/update_test.go b/pkg/sif/update_test.go index 2cb1ca0..7000e43 100644 --- a/pkg/sif/update_test.go +++ b/pkg/sif/update_test.go @@ -404,6 +404,11 @@ func TestRemoveManifests(t *testing.T) { base: "hello-world-docker-v2-manifest-list", matcher: match.Platforms(v1.Platform{OS: "linux", Architecture: "m68k"}), }, + { + name: "NilMatcher", + base: "hello-world-docker-v2-manifest-list", + matcher: nil, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -478,6 +483,12 @@ func TestReplace(t *testing.T) { replacement: replaceImage, matcher: match.Platforms(v1.Platform{OS: "linux", Architecture: "m68k"}), }, + { + name: "ReplaceImageNilMatcher", + base: "hello-world-docker-v2-manifest", + replacement: replaceImage, + matcher: nil, + }, { name: "ReplaceIndexManifest", base: "hello-world-docker-v2-manifest", @@ -496,6 +507,12 @@ func TestReplace(t *testing.T) { replacement: replaceIndex, matcher: match.Platforms(v1.Platform{OS: "linux", Architecture: "m68k"}), }, + { + name: "ReplaceIndexNilMatcher", + base: "hello-world-docker-v2-manifest", + replacement: replaceIndex, + matcher: nil, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {