@@ -99,7 +99,126 @@ func TestExtractImageLayer(t *testing.T) {
9999 }
100100 })
101101
102- t .Run ("invalid media type" , func (t * testing.T ) {
102+ t .Run ("plugin binary in earlier layer" , func (t * testing.T ) {
103+ // Replicates an image built with multiple COPY instructions
104+ // (Dockerfile.plugin): the plugin .so lands in an early layer,
105+ // followed by manifest/schema layers that contain no .so. The
106+ // last layer is therefore NOT the plugin layer.
107+ exp := "this is plugin binary"
108+ pluginLayer , err := newMockLayer (tt .mediaType , map [string ][]byte {
109+ "plugin.so" : []byte (exp ),
110+ })
111+ if err != nil {
112+ t .Fatal (err )
113+ }
114+ manifestLayer , err := newMockLayer (tt .mediaType , map [string ][]byte {
115+ "manifest.yaml" : []byte ("name: example-go" ),
116+ })
117+ if err != nil {
118+ t .Fatal (err )
119+ }
120+ schemaLayer , err := newMockLayer (tt .mediaType , map [string ][]byte {
121+ "config.schema.json" : []byte ("{}" ),
122+ })
123+ if err != nil {
124+ t .Fatal (err )
125+ }
126+
127+ // Append in build order: plugin first, then the manifests.
128+ img := empty .Image
129+ for _ , l := range []v1.Layer {pluginLayer , manifestLayer , schemaLayer } {
130+ img , err = mutate .Append (img , mutate.Addendum {Layer : l })
131+ if err != nil {
132+ t .Fatal (err )
133+ }
134+ }
135+
136+ dest := filepath .Join (t .TempDir (), "plugin.so" )
137+ if extractImageLayerErr := extractImageLayer (img , dest , tt .mediaType ); extractImageLayerErr != nil {
138+ t .Fatalf ("extractImageLayer failed: %v" , extractImageLayerErr )
139+ }
140+ b , err := os .ReadFile (dest ) //nolint:gosec // Test code reads from temp dir.
141+ if err != nil {
142+ t .Fatalf ("failed to read written plugin: %v" , err )
143+ }
144+ if string (b ) != exp {
145+ t .Fatalf ("got %s, but want %s" , string (b ), exp )
146+ }
147+ })
148+
149+ t .Run ("no plugin binary in any layer" , func (t * testing.T ) {
150+ manifestLayer , err := newMockLayer (tt .mediaType , map [string ][]byte {
151+ "manifest.yaml" : []byte ("name: example-go" ),
152+ })
153+ if err != nil {
154+ t .Fatal (err )
155+ }
156+ schemaLayer , err := newMockLayer (tt .mediaType , map [string ][]byte {
157+ "config.schema.json" : []byte ("{}" ),
158+ })
159+ if err != nil {
160+ t .Fatal (err )
161+ }
162+
163+ img := empty .Image
164+ for _ , l := range []v1.Layer {manifestLayer , schemaLayer } {
165+ img , err = mutate .Append (img , mutate.Addendum {Layer : l })
166+ if err != nil {
167+ t .Fatal (err )
168+ }
169+ }
170+
171+ dest := filepath .Join (t .TempDir (), "plugin.so" )
172+ err = extractImageLayer (img , dest , tt .mediaType )
173+ if err == nil || ! strings .Contains (err .Error (), "not found" ) {
174+ t .Fatalf ("extractImageLayer should fail with not found, got: %v" , err )
175+ }
176+ })
177+
178+ t .Run ("skips layer with unexpected media type" , func (t * testing.T ) {
179+ // A layer whose media type does not match (e.g. a config layer)
180+ // must be skipped, not treated as fatal, so the plugin in a
181+ // matching layer is still found. Build order: plugin first, then
182+ // a mismatched layer last (searched first).
183+ exp := "this is plugin binary"
184+ pluginLayer , err := newMockLayer (tt .mediaType , map [string ][]byte {
185+ "plugin.so" : []byte (exp ),
186+ })
187+ if err != nil {
188+ t .Fatal (err )
189+ }
190+ mismatchedLayer , err := newMockLayer (types .DockerPluginConfig , map [string ][]byte {
191+ "config.json" : []byte ("{}" ),
192+ })
193+ if err != nil {
194+ t .Fatal (err )
195+ }
196+
197+ img := empty .Image
198+ for _ , l := range []v1.Layer {pluginLayer , mismatchedLayer } {
199+ img , err = mutate .Append (img , mutate.Addendum {Layer : l })
200+ if err != nil {
201+ t .Fatal (err )
202+ }
203+ }
204+
205+ dest := filepath .Join (t .TempDir (), "plugin.so" )
206+ if extractImageLayerErr := extractImageLayer (img , dest , tt .mediaType ); extractImageLayerErr != nil {
207+ t .Fatalf ("extractImageLayer failed: %v" , extractImageLayerErr )
208+ }
209+ b , err := os .ReadFile (dest ) //nolint:gosec // Test code reads from temp dir.
210+ if err != nil {
211+ t .Fatalf ("failed to read written plugin: %v" , err )
212+ }
213+ if string (b ) != exp {
214+ t .Fatalf ("got %s, but want %s" , string (b ), exp )
215+ }
216+ })
217+
218+ t .Run ("only unexpected media type layers" , func (t * testing.T ) {
219+ // When no layer has the expected media type, the search finds no
220+ // plugin binary and reports not found (rather than aborting on
221+ // the first mismatch).
103222 l , err := newMockLayer (types .DockerPluginConfig , nil )
104223 if err != nil {
105224 t .Fatal (err )
@@ -110,8 +229,8 @@ func TestExtractImageLayer(t *testing.T) {
110229 }
111230 dest := filepath .Join (t .TempDir (), "plugin.so" )
112231 err = extractImageLayer (img , dest , tt .mediaType )
113- if err == nil || ! strings .Contains (err .Error (), "invalid media type " ) {
114- t .Fatal ("extractImageLayer should fail due to invalid media type" )
232+ if err == nil || ! strings .Contains (err .Error (), "not found " ) {
233+ t .Fatalf ("extractImageLayer should fail with not found, got: %v" , err )
115234 }
116235 })
117236 })
0 commit comments