Skip to content

Commit e98eaeb

Browse files
committed
Allow fetch daemon images
1 parent b8801f4 commit e98eaeb

File tree

3 files changed

+82
-43
lines changed

3 files changed

+82
-43
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<p align="center">
2-
<img alt="GoReleaser Logo" src="layer.png"/>
2+
<img alt="Layer Logo" src="layer.png"/>
33
<h3 align="center">Layer</h3>
44
<p align="center">A tool for inspecting container layers</p>
55
</p>
@@ -14,7 +14,7 @@ USAGE:
1414
layer [global options] command [command options] [arguments...]
1515
1616
COMMANDS:
17-
info info prints info about the layers of an image
17+
inspect print info about the layers of an image
1818
ls ls prints the files of a layer
1919
help, h Shows a list of commands or help for one command
2020
@@ -33,13 +33,13 @@ go install github.com/stackb/layer@latest
3333
Show layers in an image (tarball filename):
3434

3535
```sh
36-
layer info image.tar
36+
layer inspect image.tar
3737
```
3838

3939
Show layers in an image (ref):
4040

4141
```
42-
layer info index.docker.io/nginx:latest
42+
layer inspect index.docker.io/nginx:latest
4343
N Layer Size
4444
1 sha256:9c1b6dd6c1e6be9fdd2b1987783824670d3b0dd7ae8ad6f57dc3cea5739ac71e 31 MB
4545
2 sha256:4b7fffa0f0a4a72b2f901c584c1d4ffb67cce7f033cc7969ee7713995c4d2610 25 MB
@@ -88,5 +88,6 @@ Mode Size Name
8888

8989
---
9090

91-
<a href="https://www.flaticon.com/free-icons/hangar" style="font-size: 8px" title="hangar icons">Layer
92-
Image by Pause08</a>
91+
- See also https://github.com/wagoodman/dive.
92+
- <a href="https://www.flaticon.com/free-icons/hangar" style="font-size: 8px" title="hangar icons">Layer
93+
Image by Pause08</a>

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX
7171
github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
7272
github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
7373
github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
74+
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
7475
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
7576
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
7677
github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
@@ -178,6 +179,7 @@ github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7
178179
github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU=
179180
github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI=
180181
github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=
182+
github.com/containerd/containerd v1.5.8 h1:NmkCC1/QxyZFBny8JogwLpOy2f+VEbO/f6bV2Mqtwuw=
181183
github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s=
182184
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
183185
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
@@ -276,11 +278,13 @@ github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiew
276278
github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
277279
github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o=
278280
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
281+
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
279282
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
280283
github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
281284
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
282285
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
283286
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
287+
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
284288
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
285289
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
286290
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
@@ -345,6 +349,7 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
345349
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
346350
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
347351
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
352+
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
348353
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
349354
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
350355
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -378,6 +383,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
378383
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
379384
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
380385
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
386+
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
381387
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
382388
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
383389
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -886,6 +892,7 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy
886892
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
887893
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
888894
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
895+
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM=
889896
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
890897
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
891898
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1204,6 +1211,7 @@ google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ6
12041211
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
12051212
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
12061213
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
1214+
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0=
12071215
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
12081216
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
12091217
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -1236,6 +1244,7 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
12361244
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
12371245
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
12381246
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
1247+
google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
12391248
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
12401249
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
12411250
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@@ -1250,6 +1259,7 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
12501259
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
12511260
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
12521261
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
1262+
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
12531263
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
12541264
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
12551265
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=

main.go

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,25 @@ import (
1414
"github.com/google/go-containerregistry/pkg/crane"
1515
"github.com/google/go-containerregistry/pkg/name"
1616
v1 "github.com/google/go-containerregistry/pkg/v1"
17+
"github.com/google/go-containerregistry/pkg/v1/daemon"
1718
"github.com/google/go-containerregistry/pkg/v1/remote"
1819
"github.com/google/go-containerregistry/pkg/v1/tarball"
1920
"github.com/urfave/cli/v2"
2021
)
2122

22-
type config struct {
23-
filename string
24-
layerIDs []string
25-
sort bool
26-
}
27-
2823
func main() {
2924
app := &cli.App{
3025
Name: "layer",
3126
Usage: "inspect layers of an image",
3227
Commands: []*cli.Command{
3328
{
34-
Name: "info",
35-
Usage: "info prints info about the layers of an image",
29+
Name: "inspect",
30+
Usage: "print info about the layers of an image",
3631
Action: func(c *cli.Context) error {
3732
cfg := &config{
38-
filename: c.Args().First(),
33+
ref: c.Args().First(),
3934
}
40-
if err := info(cfg); err != nil {
35+
if err := inspect(cfg); err != nil {
4136
return cli.Exit(c.Command.Name+": "+err.Error(), 1)
4237
}
4338
return nil
@@ -48,12 +43,12 @@ func main() {
4843
Usage: "ls prints the files of a layer",
4944
Action: func(c *cli.Context) error {
5045
cfg := &config{
51-
filename: c.Args().First(),
46+
ref: c.Args().First(),
5247
layerIDs: c.Args().Tail(),
5348
sort: c.Bool("sort"),
5449
}
5550

56-
if err := files(cfg); err != nil {
51+
if err := ls(cfg); err != nil {
5752
return cli.Exit(c.Command.Name+": "+err.Error(), 1)
5853
}
5954
return nil
@@ -72,52 +67,84 @@ func main() {
7267
app.Run(os.Args)
7368
}
7469

70+
// config is the configuration for the layer commands.
71+
type config struct {
72+
// ref is the image ref to inspect, or a path to a tarball.
73+
ref string
74+
// layerIDs is the list of layer IDs to inspect.
75+
layerIDs []string
76+
// sort is true if the output should be sorted by size.
77+
sort bool
78+
}
79+
80+
// makeOptions returns the options for crane.
81+
func makeOptions(opts ...crane.Option) crane.Options {
82+
opt := crane.Options{
83+
Remote: []remote.Option{
84+
remote.WithAuthFromKeychain(authn.DefaultKeychain),
85+
},
86+
}
87+
for _, o := range opts {
88+
o(&opt)
89+
}
90+
return opt
91+
}
92+
93+
// getImage returns the image for the given ref.
7594
func getImage(r string) (v1.Image, error) {
7695
if r == "" {
7796
return nil, fmt.Errorf("no image ref provided")
7897
}
7998

80-
image, _, err := getRemoteImage(r)
99+
image, err := tarball.ImageFromPath(r, nil)
81100
if err == nil {
82101
return image, nil
83102
}
84103

85-
image, err = tarball.ImageFromPath(r, nil)
86-
if err != nil {
87-
return nil, err
104+
image, _, err = getDaemonImage(r)
105+
if err == nil {
106+
return image, nil
88107
}
89108

90-
return image, nil
109+
image, _, err = getRemoteImage(r)
110+
if err == nil {
111+
return image, nil
112+
}
113+
114+
return nil, fmt.Errorf("unable to find image %q", r)
91115
}
92116

93-
func makeOptions(opts ...crane.Option) crane.Options {
94-
opt := crane.Options{
95-
Remote: []remote.Option{
96-
remote.WithAuthFromKeychain(authn.DefaultKeychain),
97-
},
117+
// getRemoteImage returns the image for the given ref.
118+
func getRemoteImage(r string, opt ...crane.Option) (v1.Image, name.Reference, error) {
119+
o := makeOptions(opt...)
120+
ref, err := name.ParseReference(r, o.Name...)
121+
if err != nil {
122+
return nil, nil, fmt.Errorf("parsing reference %q: %w", r, err)
98123
}
99-
for _, o := range opts {
100-
o(&opt)
124+
img, err := remote.Image(ref, o.Remote...)
125+
if err != nil {
126+
return nil, nil, fmt.Errorf("reading image %q: %w", ref, err)
101127
}
102-
return opt
128+
return img, ref, nil
103129
}
104130

105-
func getRemoteImage(r string, opt ...crane.Option) (v1.Image, name.Reference, error) {
131+
// getDaemonImage returns the image for the given ref.
132+
func getDaemonImage(r string, opt ...crane.Option) (v1.Image, name.Reference, error) {
106133
o := makeOptions(opt...)
107134
ref, err := name.ParseReference(r, o.Name...)
108135
if err != nil {
109136
return nil, nil, fmt.Errorf("parsing reference %q: %w", r, err)
110137
}
111-
img, err := remote.Image(ref, o.Remote...)
138+
img, err := daemon.Image(ref)
112139
if err != nil {
113140
return nil, nil, fmt.Errorf("reading image %q: %w", ref, err)
114141
}
115142
return img, ref, nil
116143
}
117144

118-
// info returns the config file for the given filename.
119-
func info(cfg *config) error {
120-
image, err := getImage(cfg.filename)
145+
// inspect prints info about layers.
146+
func inspect(cfg *config) error {
147+
image, err := getImage(cfg.ref)
121148
if err != nil {
122149
return err
123150
}
@@ -145,9 +172,9 @@ func info(cfg *config) error {
145172
return tw.Flush()
146173
}
147174

148-
func files(cfg *config) error {
149-
150-
image, err := getImage(cfg.filename)
175+
// ls prints layers.
176+
func ls(cfg *config) error {
177+
image, err := getImage(cfg.ref)
151178
if err != nil {
152179
return err
153180
}
@@ -159,7 +186,7 @@ func files(cfg *config) error {
159186

160187
if len(cfg.layerIDs) == 0 {
161188
for _, layer := range layers {
162-
if err := layerFiles(cfg, layer); err != nil {
189+
if err := files(cfg, layer); err != nil {
163190
return err
164191
}
165192
}
@@ -171,7 +198,7 @@ func files(cfg *config) error {
171198
if n < 1 || n > len(layers) {
172199
return fmt.Errorf("layer %d does not exist", n)
173200
}
174-
if err := layerFiles(cfg, layers[n-1]); err != nil {
201+
if err := files(cfg, layers[n-1]); err != nil {
175202
return err
176203
}
177204
continue
@@ -187,15 +214,16 @@ func files(cfg *config) error {
187214
if err != nil {
188215
return fmt.Errorf("layer %s not found: %w", id, err)
189216
}
190-
if err := layerFiles(cfg, layer); err != nil {
217+
if err := files(cfg, layer); err != nil {
191218
return err
192219
}
193220
}
194221

195222
return nil
196223
}
197224

198-
func layerFiles(cfg *config, layer v1.Layer) error {
225+
// files lists the files in the given layer.
226+
func files(cfg *config, layer v1.Layer) error {
199227
hash, err := layer.DiffID()
200228
if err != nil {
201229
return fmt.Errorf("getting layer diffid: %w", err)

0 commit comments

Comments
 (0)