-
Notifications
You must be signed in to change notification settings - Fork 237
Handle error gracefully when we can't retrieve an image #251
Changes from 2 commits
8621bc5
ee51ff8
6a7a550
7024d3b
265b622
9c47911
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,15 +17,16 @@ limitations under the License. | |
package cmd | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"strings" | ||
"sync" | ||
|
||
"github.com/GoogleContainerTools/container-diff/cmd/util/output" | ||
"github.com/GoogleContainerTools/container-diff/differs" | ||
pkgutil "github.com/GoogleContainerTools/container-diff/pkg/util" | ||
"github.com/GoogleContainerTools/container-diff/util" | ||
"github.com/pkg/errors" | ||
"github.com/sirupsen/logrus" | ||
"github.com/spf13/cobra" | ||
) | ||
|
@@ -69,33 +70,60 @@ func checkFilenameFlag(_ []string) error { | |
return errors.New("Please include --types=file with the --filename flag") | ||
} | ||
|
||
func processImage(imageName string, imageMap map[string]*pkgutil.Image, wg *sync.WaitGroup, errChan chan<- error) { | ||
defer wg.Done() | ||
image, err := getImageForName(imageName) | ||
if image.Image == nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2 questions:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok this logic isn't great so I'm gonna rework it a bit. Digging into the go-containerregistry code, if we get an error returned back from any of the three sources (tar/daemon/remote), the image returned will always be The reason I was checking for TLDR answers for your questions: 1) yes, and 2) not possible |
||
errChan <- fmt.Errorf("error retrieving image %s: %s", imageName, err.Error()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm interested in why call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. technically the format types don't match ( |
||
return | ||
} | ||
if err != nil { | ||
logrus.Warningf("diff may be inaccurate: %s", err) | ||
} | ||
imageMap[imageName] = &image | ||
} | ||
|
||
func diffImages(image1Arg, image2Arg string, diffArgs []string) error { | ||
diffTypes, err := differs.GetAnalyzers(diffArgs) | ||
if err != nil { | ||
return err | ||
return errors.Wrap(err, "getting analyzers") | ||
} | ||
|
||
var wg sync.WaitGroup | ||
wg.Add(2) | ||
|
||
logrus.Infof("starting diff on images %s and %s, using differs: %s\n", image1Arg, image2Arg, diffArgs) | ||
|
||
imageMap := map[string]*pkgutil.Image{ | ||
image1Arg: {}, | ||
image2Arg: {}, | ||
imageMap := map[string]*pkgutil.Image{} | ||
errChan := make(chan error, 2) | ||
|
||
go processImage(image1Arg, imageMap, &wg, errChan) | ||
go processImage(image2Arg, imageMap, &wg, errChan) | ||
|
||
wg.Wait() | ||
close(errChan) | ||
|
||
errs := []string{} | ||
for { | ||
err, ok := <-errChan | ||
if !ok { | ||
break | ||
} | ||
errs = append(errs, err.Error()) | ||
} | ||
|
||
if len(errs) > 0 { | ||
return errors.New(strings.Join(errs, "\n")) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this logic to collect together errors into one error seems like a good candidate for being moved into a separate function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed, done |
||
|
||
img1, ok := imageMap[image1Arg] | ||
if !ok { | ||
return fmt.Errorf("cannot find image %s", image1Arg) | ||
} | ||
// TODO: fix error handling here | ||
for imageArg := range imageMap { | ||
go func(imageName string, imageMap map[string]*pkgutil.Image) { | ||
defer wg.Done() | ||
image, err := getImageForName(imageName) | ||
imageMap[imageName] = &image | ||
if err != nil { | ||
logrus.Warningf("Diff may be inaccurate: %s", err) | ||
} | ||
}(imageArg, imageMap) | ||
img2, ok := imageMap[image2Arg] | ||
if !ok { | ||
return fmt.Errorf("cannot find image %s", image2Arg) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what would it mean if processImage returned for both images without errors, but one or both of these checks failed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed these since this logic is duplicated elsewhere |
||
wg.Wait() | ||
|
||
if noCache && !save { | ||
defer pkgutil.CleanupImage(*imageMap[image1Arg]) | ||
|
@@ -104,8 +132,8 @@ func diffImages(image1Arg, image2Arg string, diffArgs []string) error { | |
|
||
logrus.Info("computing diffs") | ||
req := differs.DiffRequest{ | ||
Image1: *imageMap[image1Arg], | ||
Image2: *imageMap[image2Arg], | ||
Image1: *img1, | ||
Image2: *img2, | ||
DiffTypes: diffTypes} | ||
diffs, err := req.GetDiff() | ||
if err != nil { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a name for this function that might be a bit more specific to what this function is doing? I wasn't sure what
processImage
meant when I was reading this.It seems like the purpose of this function is to look up sometthing about the image (im not sure what tho - are we actually getting the bytes of the image itself, or looking up some metadata or something else?) and put that something into
imageMap
So some other ideas:
AddImageToMap
GetMetadataForImage
FindImageSource
DownloadImage
(You might have some other name ideas tho since you probably know what exactly
getImageForName
is doing)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is essentially just a concurrent wrapper around
getImageForName
, which retrieves a reference to an image and a reader for the actual bytes of the image. I can change this togetImage
(theimageMap
isn't really a useful struct here so I don't want to refer to it), but the reason I called itprocessImage
is becausegetImageForName
actually does a bit of image processing, i.e. unpacks the image filesystem to the local filesystem. I should probably just add some comments explaining what each function does :)