@@ -3,7 +3,6 @@ package docker
3
3
import (
4
4
"archive/tar"
5
5
"context"
6
- "errors"
7
6
"fmt"
8
7
"io"
9
8
"io/ioutil"
@@ -22,6 +21,9 @@ import (
22
21
"github.com/docker/docker/registry"
23
22
"github.com/mitchellh/copystructure"
24
23
24
+ "github.com/pkg/errors"
25
+
26
+ "github.com/cnabio/cnab-go/bundle"
25
27
"github.com/cnabio/cnab-go/driver"
26
28
)
27
29
@@ -187,6 +189,17 @@ func (d *Driver) exec(op *driver.Operation) (driver.OperationResult, error) {
187
189
return driver.OperationResult {}, err
188
190
}
189
191
}
192
+
193
+ ii , err := d .inspectImage (ctx , op .Image )
194
+ if err != nil {
195
+ return driver.OperationResult {}, err
196
+ }
197
+
198
+ err = d .validateImageDigest (op .Image , ii .RepoDigests )
199
+ if err != nil {
200
+ return driver.OperationResult {}, errors .Wrap (err , "image digest validation failed" )
201
+ }
202
+
190
203
var env []string
191
204
for k , v := range op .Environment {
192
205
env = append (env , fmt .Sprintf ("%s=%v" , k , v ))
@@ -206,16 +219,7 @@ func (d *Driver) exec(op *driver.Operation) (driver.OperationResult, error) {
206
219
}
207
220
208
221
resp , err := cli .Client ().ContainerCreate (ctx , & d .containerCfg , & d .containerHostCfg , nil , "" )
209
- switch {
210
- case client .IsErrNotFound (err ):
211
- fmt .Fprintf (cli .Err (), "Unable to find image '%s' locally\n " , op .Image .Image )
212
- if err := pullImage (ctx , cli , op .Image .Image ); err != nil {
213
- return driver.OperationResult {}, err
214
- }
215
- if resp , err = cli .Client ().ContainerCreate (ctx , & d .containerCfg , & d .containerHostCfg , nil , "" ); err != nil {
216
- return driver.OperationResult {}, fmt .Errorf ("cannot create container: %v" , err )
217
- }
218
- case err != nil :
222
+ if err != nil {
219
223
return driver.OperationResult {}, fmt .Errorf ("cannot create container: %v" , err )
220
224
}
221
225
@@ -385,3 +389,55 @@ func generateTar(files map[string]string) (io.Reader, error) {
385
389
386
390
// ConfigurationOption is an option used to customize docker driver container and host config
387
391
type ConfigurationOption func (* container.Config , * container.HostConfig ) error
392
+
393
+ // inspectImage inspects the operation image and returns an object of types.ImageInspect,
394
+ // pulling the image if not found locally
395
+ func (d * Driver ) inspectImage (ctx context.Context , image bundle.InvocationImage ) (types.ImageInspect , error ) {
396
+ ii , _ , err := d .dockerCli .Client ().ImageInspectWithRaw (ctx , image .Image )
397
+ switch {
398
+ case client .IsErrNotFound (err ):
399
+ fmt .Fprintf (d .dockerCli .Err (), "Unable to find image '%s' locally\n " , image .Image )
400
+ if err := pullImage (ctx , d .dockerCli , image .Image ); err != nil {
401
+ return ii , err
402
+ }
403
+ if ii , _ , err = d .dockerCli .Client ().ImageInspectWithRaw (ctx , image .Image ); err != nil {
404
+ return ii , errors .Wrapf (err , "cannot inspect image %s" , image .Image )
405
+ }
406
+ case err != nil :
407
+ return ii , errors .Wrapf (err , "cannot inspect image %s" , image .Image )
408
+ }
409
+
410
+ return ii , nil
411
+ }
412
+
413
+ // validateImageDigest validates the operation image digest, if exists, against
414
+ // the supplied repoDigests
415
+ func (d * Driver ) validateImageDigest (image bundle.InvocationImage , repoDigests []string ) error {
416
+ if image .Digest == "" {
417
+ return nil
418
+ }
419
+
420
+ switch count := len (repoDigests ); {
421
+ case count == 0 :
422
+ return fmt .Errorf ("image %s has no repo digests" , image .Image )
423
+ case count > 1 :
424
+ return fmt .Errorf ("image %s has more than one repo digest" , image .Image )
425
+ }
426
+
427
+ // RepoDigests are of the form 'imageName@sha256:<sha256>'; we parse out the digest itself for comparison
428
+ repoDigest := repoDigests [0 ]
429
+ ref , err := reference .ParseNormalizedNamed (repoDigest )
430
+ if err != nil {
431
+ return fmt .Errorf ("unable to parse repo digest %s" , repoDigest )
432
+ }
433
+ digestRef , ok := ref .(reference.Digested )
434
+ if ! ok {
435
+ return fmt .Errorf ("unable to parse repo digest %s" , repoDigest )
436
+ }
437
+ digest := digestRef .Digest ().String ()
438
+
439
+ if digest == image .Digest {
440
+ return nil
441
+ }
442
+ return fmt .Errorf ("content digest mismatch: image %s has digest %s but the value should be %s according to the bundle file" , image .Image , digest , image .Digest )
443
+ }
0 commit comments