Skip to content
This repository was archived by the owner on Mar 27, 2024. It is now read-only.

Remove Docker dependency for retrieving image filesystem #89

Closed
wants to merge 10 commits into from

Conversation

nkubala
Copy link
Contributor

@nkubala nkubala commented Sep 18, 2017

This PR removes the dependency on the Docker API client for retrieving an image from a remote registry or a local docker daemon, and converting it to an in-memory filesystem. The Docker client is still needed to retrieve image history, and will be used automatically by the container-diff tool itself.

The main motivation for this is for code that uses container-diff as a library to convert both Docker images and tarballs into convenient representations of filesystems, without relying on a functional Docker daemon on the host machine.

By default, the Image Prepper in this code will now default to looking for images in the local Docker daemon first, followed by checking a cloud registry. Because this is not necessarily intended behavior when using the container-diff CLI (for example, if you wanted to compare both local and remote versions of an image), the container-diff CLI now requires the prefix daemon:// to specify an image contained in a local Docker daemon.

This also removes support for specifying images by their Docker "Image ID" (e.g. f5ad8aa3b589), since this is not supported by the containers/image library.

cmd/analyze.go Outdated
analyzeTypes, err := differs.GetAnalyzers(analyzerArgs)
if err != nil {
glog.Error(err.Error())
return errors.New("Could not perform image analysis")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you wrap the previous error instead of making a new one? No need to log there either.

if err != nil {
return "", err
errStr = "\nUnable to remove " + path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, wrap the error you get instead of making a new one.

cmd/diff.go Outdated
prepper = pkgutil.LOCAL
imageName = strings.Replace(imageName, "daemon://", "", -1)
} else if pkgutil.IsTar(imageName) {
prepper = pkgutil.TAR
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change this to pass an actual type or function instead of a string?

@@ -41,11 +41,21 @@ func unpackTar(tr *tar.Reader, path string) error {
if strings.Contains(header.Name, ".wh.") {
rmPath := filepath.Join(path, header.Name)
newName := strings.Replace(rmPath, ".wh.", "", 1)
err := os.Remove(rmPath)
if _, err := os.Stat(rmPath); err == nil {
err := os.Remove(rmPath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: collapse this into the if

cmd/analyze.go Outdated
var prepper string
if strings.HasPrefix(imageName, "daemon://") {
// force local daemon if we have the corresponding prefix
prepper = pkgutil.LOCAL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just CamelCase for constants

}
return nil
}

func processImageName(imageName string) string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would get rid of the nested negated conditions

  if pkgutil.IsTar(imageName) || strings.HasPrefix(imageName, "daemon://") return imageName
  // default condition
  return "remote://" + imageName

or even

if pkgutil.IsTar(imageName)
 return ...
if strings.HasPrefix(imageName, "daemon://")
 return ...

return ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@@ -54,17 +54,23 @@ func getImage(prepper Prepper, source string) (Image, error) {
}, nil
}

func (p ImagePrepper) GetImage() (Image, error) {
func (p *ImagePrepper) GetImage() (Image, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its weird that GetImage() actually mutates the fields on the struct. Calling this twice on the same imagePrepper will break, right? I would refactor this. getImage already takes in redundant information since it takes prepper and prepper.ImagePrepper.Source

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason I changed it to do this is to handle the prefixes that the image prepper respects to force a certain source (e.g. daemon://<image_path>). these need to be removed before actually trying to parse the image reference with the containers/image library. GetImage() only ever gets called one time per prepper, so there shouldn't be any risk here. getImage() doesn't need to take the source as parameter though, since it gets it from the prepper

"Cloud Registry": func(ip ImagePrepper) Prepper { return CloudPrepper{ImagePrepper: ip} },
"Tar": func(ip ImagePrepper) Prepper { return TarPrepper{ImagePrepper: ip} },
var sourceToPrepMap = map[string]func(ip *ImagePrepper) Prepper{
"Local Daemon": func(ip *ImagePrepper) Prepper { return DaemonPrepper{ImagePrepper: ip} },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do these strings come from? I would get rid of them in favor of some function like

func NewPrepperForSource(source string) Prepper
or
func NewPrepperForSource(source ImageSourceType) Prepper

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this is getting refactored out in an upcoming PR. these are just here for logging at this point

if errMsg != "" {
glog.Error(errMsg)
if err := remove(image.FSPath, true); err != nil {
glog.Error(err.Error())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be glog.Error(err)

Also, this won't exit or do anything besides print the error to stdout and the glog error file. Is that what you mean to do here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not new code, but yeah this can fail without impacting the overall run. this should probably get knocked down to a warn though

}

var err error
func remove(path string, dir bool) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove remove. os.RemoveAll tries to call remove before it deletes the directory. Just calling that directly will be fine

https://golang.org/src/os/path.go?s=1634:1667#L56

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call, done

errMsg := remove(image.FSPath, true)
if errMsg != "" {
glog.Error(errMsg)
if err := remove(image.FSPath, true); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove remove

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

cmd/diff.go Outdated
@@ -108,27 +98,34 @@ func diffImages(image1Arg, image2Arg string, diffArgs []string) error {

diffTypes, err := differs.GetAnalyzers(diffArgs)
if err != nil {
glog.Error(err.Error())
return errors.New("Could not perform image diff")
return fmt.Errorf("Could not perform image diff: %s", err.Error())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you usually want to just do something like fmt.Errorf("error msg: %s", err)
or fmt.Errorf("error msg: %v", err) if you might expect a typed error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@nkubala
Copy link
Contributor Author

nkubala commented Sep 20, 2017

closing in favor of #97

@nkubala nkubala closed this Sep 20, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants