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

Commit 8ba560d

Browse files
committed
add layer caching
1 parent a2c4d96 commit 8ba560d

File tree

7 files changed

+154
-5
lines changed

7 files changed

+154
-5
lines changed

cmd/root.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@ import (
2121
goflag "flag"
2222
"fmt"
2323
"os"
24+
"os/user"
25+
"path/filepath"
2426
"sort"
2527
"strings"
2628

2729
"github.com/GoogleCloudPlatform/container-diff/differs"
30+
"github.com/GoogleCloudPlatform/container-diff/pkg/cache"
2831
pkgutil "github.com/GoogleCloudPlatform/container-diff/pkg/util"
2932
"github.com/GoogleCloudPlatform/container-diff/util"
3033
"github.com/docker/docker/client"
@@ -35,6 +38,7 @@ import (
3538

3639
var json bool
3740
var save bool
41+
var noCache bool
3842
var types string
3943

4044
var LogLevel string
@@ -122,25 +126,50 @@ func getPrepperForImage(image string) (pkgutil.Prepper, error) {
122126
if err != nil {
123127
return nil, err
124128
}
129+
130+
cacheDir, err := cacheDir()
131+
if err != nil {
132+
return nil, err
133+
}
134+
var fsCache cache.Cache
135+
if !noCache {
136+
fsCache, err = cache.NewFileCache(cacheDir)
137+
if err != nil {
138+
return nil, err
139+
}
140+
}
141+
125142
if pkgutil.IsTar(image) {
126143
return pkgutil.TarPrepper{
127144
Source: image,
128145
Client: cli,
146+
Cache: fsCache,
129147
}, nil
130148

131149
} else if strings.HasPrefix(image, DaemonPrefix) {
132150
return pkgutil.DaemonPrepper{
133151
Source: strings.Replace(image, DaemonPrefix, "", -1),
134152
Client: cli,
153+
Cache: fsCache,
135154
}, nil
136155
}
137156
// either has remote prefix or has no prefix, in which case we force remote
138157
return pkgutil.CloudPrepper{
139158
Source: strings.Replace(image, RemotePrefix, "", -1),
140159
Client: cli,
160+
Cache: fsCache,
141161
}, nil
142162
}
143163

164+
func cacheDir() (string, error) {
165+
user, err := user.Current()
166+
if err != nil {
167+
return "", err
168+
}
169+
rootDir := filepath.Join(user.HomeDir, ".container-diff")
170+
return filepath.Join(rootDir, "cache"), nil
171+
}
172+
144173
func init() {
145174
RootCmd.PersistentFlags().StringVarP(&LogLevel, "verbosity", "v", "warning", "This flag controls the verbosity of container-diff.")
146175
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
@@ -151,4 +180,5 @@ func addSharedFlags(cmd *cobra.Command) {
151180
cmd.Flags().StringVarP(&types, "types", "t", "apt", "This flag sets the list of analyzer types to use. It expects a comma separated list of supported analyzers.")
152181
cmd.Flags().BoolVarP(&save, "save", "s", false, "Set this flag to save rather than remove the final image filesystems on exit.")
153182
cmd.Flags().BoolVarP(&util.SortSize, "order", "o", false, "Set this flag to sort any file/package results by descending size. Otherwise, they will be sorted by name.")
183+
cmd.Flags().BoolVarP(&noCache, "no-cache", "n", false, "Set this to force retrieval of layers on each run.")
154184
}

pkg/cache/cache.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Copyright 2017 Google, Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cache
18+
19+
import "io"
20+
21+
type Cache interface {
22+
HasLayer(string) bool
23+
GetLayer(string) (io.ReadCloser, error)
24+
SetLayer(string, io.Reader) (io.ReadCloser, error)
25+
Invalidate(string) error
26+
}

pkg/cache/file_cache.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
Copyright 2017 Google, Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cache
18+
19+
import (
20+
"io"
21+
"os"
22+
"path/filepath"
23+
24+
"github.com/sirupsen/logrus"
25+
)
26+
27+
type FileCache struct {
28+
RootDir string
29+
}
30+
31+
func NewFileCache(dir string) (*FileCache, error) {
32+
if err := os.MkdirAll(dir, 0700); err != nil {
33+
return nil, err
34+
}
35+
return &FileCache{RootDir: dir}, nil
36+
}
37+
38+
func (c *FileCache) HasLayer(layerId string) bool {
39+
_, err := os.Stat(filepath.Join(c.RootDir, layerId))
40+
return !os.IsNotExist(err)
41+
}
42+
43+
func (c *FileCache) SetLayer(layerId string, r io.Reader) (io.ReadCloser, error) {
44+
fullpath := filepath.Join(c.RootDir, layerId)
45+
entry, err := os.Create(fullpath)
46+
if err != nil {
47+
return nil, err
48+
}
49+
if _, err := io.Copy(entry, r); err != nil {
50+
return nil, err
51+
}
52+
return c.GetLayer(layerId)
53+
}
54+
55+
func (c *FileCache) GetLayer(layerId string) (io.ReadCloser, error) {
56+
logrus.Infof("retrieving layer %s from cache", layerId)
57+
return os.Open(filepath.Join(c.RootDir, layerId))
58+
}
59+
60+
func (c *FileCache) Invalidate(layerId string) error {
61+
return os.RemoveAll(filepath.Join(c.RootDir, layerId))
62+
}

pkg/util/cloud_prepper.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package util
1818

1919
import (
20+
"github.com/GoogleCloudPlatform/container-diff/pkg/cache"
2021
"github.com/containers/image/docker"
2122
"github.com/docker/docker/client"
2223
)
@@ -25,6 +26,7 @@ import (
2526
type CloudPrepper struct {
2627
Source string
2728
Client *client.Client
29+
Cache cache.Cache
2830
}
2931

3032
func (p CloudPrepper) Name() string {
@@ -45,7 +47,7 @@ func (p CloudPrepper) GetFileSystem() (string, error) {
4547
return "", err
4648
}
4749

48-
return getFileSystemFromReference(ref, p.Source)
50+
return getFileSystemFromReference(ref, p.Source, p.Cache)
4951
}
5052

5153
func (p CloudPrepper) GetConfig() (ConfigSchema, error) {

pkg/util/daemon_prepper.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package util
1919
import (
2020
"context"
2121

22+
"github.com/GoogleCloudPlatform/container-diff/pkg/cache"
2223
"github.com/containers/image/docker/daemon"
2324

2425
"github.com/docker/docker/client"
@@ -28,6 +29,7 @@ import (
2829
type DaemonPrepper struct {
2930
Source string
3031
Client *client.Client
32+
Cache cache.Cache
3133
}
3234

3335
func (p DaemonPrepper) Name() string {
@@ -47,7 +49,7 @@ func (p DaemonPrepper) GetFileSystem() (string, error) {
4749
if err != nil {
4850
return "", err
4951
}
50-
return getFileSystemFromReference(ref, p.Source)
52+
return getFileSystemFromReference(ref, p.Source, p.Cache)
5153
}
5254

5355
func (p DaemonPrepper) GetConfig() (ConfigSchema, error) {

pkg/util/image_prep_utils.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ import (
2121
"encoding/json"
2222
"errors"
2323
"fmt"
24+
"io"
2425
"io/ioutil"
2526
"os"
2627
"strings"
2728

29+
"github.com/GoogleCloudPlatform/container-diff/pkg/cache"
2830
"github.com/containers/image/pkg/compression"
2931
"github.com/containers/image/types"
3032
"github.com/sirupsen/logrus"
@@ -91,7 +93,7 @@ func getImageFromTar(tarPath string) (string, error) {
9193
return tempPath, unpackDockerSave(tarPath, tempPath)
9294
}
9395

94-
func getFileSystemFromReference(ref types.ImageReference, imageName string) (string, error) {
96+
func getFileSystemFromReference(ref types.ImageReference, imageName string, cache cache.Cache) (string, error) {
9597
sanitizedName := strings.Replace(imageName, ":", "", -1)
9698
sanitizedName = strings.Replace(sanitizedName, "/", "", -1)
9799

@@ -113,10 +115,33 @@ func getFileSystemFromReference(ref types.ImageReference, imageName string) (str
113115
return "", err
114116
}
115117

118+
var bi io.ReadCloser
116119
for _, b := range img.LayerInfos() {
117-
bi, _, err := imgSrc.GetBlob(b)
120+
layerId := b.Digest.String()
121+
if cache == nil {
122+
bi, _, err = imgSrc.GetBlob(b)
123+
if err != nil {
124+
logrus.Errorf("Failed to pull image layer: %s", err)
125+
return "", err
126+
}
127+
} else if cache.HasLayer(layerId) {
128+
logrus.Infof("cache hit for layer %s", layerId)
129+
bi, err = cache.GetLayer(layerId)
130+
} else {
131+
logrus.Infof("cache miss for layer %s", layerId)
132+
bi, _, err = imgSrc.GetBlob(b)
133+
if err != nil {
134+
logrus.Errorf("Failed to pull image layer: %s", err)
135+
return "", err
136+
}
137+
bi, err = cache.SetLayer(layerId, bi)
138+
if err != nil {
139+
logrus.Errorf("error when caching layer %s: %s", layerId, err)
140+
cache.Invalidate(layerId)
141+
}
142+
}
118143
if err != nil {
119-
logrus.Errorf("Failed to pull image layer: %s", err)
144+
logrus.Errorf("Failed to retrieve image layer: %s", err)
120145
return "", err
121146
}
122147
// try and detect layer compression

pkg/util/tar_prepper.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"os"
2424
"path/filepath"
2525

26+
"github.com/GoogleCloudPlatform/container-diff/pkg/cache"
2627
"github.com/containers/image/docker/tarfile"
2728
"github.com/docker/docker/client"
2829
"github.com/sirupsen/logrus"
@@ -31,6 +32,7 @@ import (
3132
type TarPrepper struct {
3233
Source string
3334
Client *client.Client
35+
Cache cache.Cache
3436
}
3537

3638
func (p TarPrepper) Name() string {

0 commit comments

Comments
 (0)