mirror of
https://github.com/wagoodman/dive
synced 2024-05-19 21:36:32 +02:00
Compare commits
7 commits
95d0fa05f7
...
92f682e6ca
Author | SHA1 | Date | |
---|---|---|---|
92f682e6ca | |||
925cdd8648 | |||
fd526464b2 | |||
9f08f7e6cc | |||
7556be352a | |||
2d86aa7b4c | |||
5d6a406df1 |
BIN
.data/test-oci-docker-image.tar
Normal file
BIN
.data/test-oci-docker-image.tar
Normal file
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
FROM alpine:3.12
|
||||
FROM alpine:3.18
|
||||
|
||||
ARG DOCKER_CLI_VERSION=${DOCKER_CLI_VERSION}
|
||||
RUN wget -O- https://download.docker.com/linux/static/stable/$(uname -m)/docker-${DOCKER_CLI_VERSION}.tgz | \
|
||||
|
|
14
README.md
14
README.md
|
@ -94,15 +94,25 @@ With valid `source` options as such:
|
|||
## Installation
|
||||
|
||||
**Ubuntu/Debian**
|
||||
|
||||
Using debs:
|
||||
```bash
|
||||
export DIVE_VERSION=$(curl -sL "https://api.github.com/repos/wagoodman/dive/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
||||
DIVE_VERSION=$(curl -sL "https://api.github.com/repos/wagoodman/dive/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
||||
curl -OL https://github.com/wagoodman/dive/releases/download/v${DIVE_VERSION}/dive_${DIVE_VERSION}_linux_amd64.deb
|
||||
sudo apt install ./dive_${DIVE_VERSION}_linux_amd64.deb
|
||||
```
|
||||
|
||||
Using snap:
|
||||
```bash
|
||||
sudo snap install docker
|
||||
sudo snap install dive
|
||||
sudo snap connect dive:docker-executables docker:docker-executables
|
||||
sudo snap connect dive:docker-daemon docker:docker-daemon
|
||||
```
|
||||
|
||||
**RHEL/Centos**
|
||||
```bash
|
||||
export DIVE_VERSION=$(curl -sL "https://api.github.com/repos/wagoodman/dive/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
||||
DIVE_VERSION=$(curl -sL "https://api.github.com/repos/wagoodman/dive/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
||||
curl -OL https://github.com/wagoodman/dive/releases/download/v${DIVE_VERSION}/dive_${DIVE_VERSION}_linux_amd64.rpm
|
||||
rpm -i dive_${DIVE_VERSION}_linux_amd64.rpm
|
||||
```
|
||||
|
|
|
@ -2,7 +2,9 @@ package docker
|
|||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -46,6 +48,7 @@ func NewImageArchive(tarFile io.ReadCloser) (*ImageArchive, error) {
|
|||
|
||||
// some layer tars can be relative layer symlinks to other layer tars
|
||||
if header.Typeflag == tar.TypeSymlink || header.Typeflag == tar.TypeReg {
|
||||
// For the Docker image format, use file name conventions
|
||||
if strings.HasSuffix(name, ".tar") {
|
||||
currentLayer++
|
||||
layerReader := tar.NewReader(tarReader)
|
||||
|
@ -82,6 +85,55 @@ func NewImageArchive(tarFile io.ReadCloser) (*ImageArchive, error) {
|
|||
return img, err
|
||||
}
|
||||
jsonFiles[name] = fileBuffer
|
||||
} else if strings.HasPrefix(name, "blobs/") {
|
||||
// For the OCI-compatible image format (used since Docker 25), use mime sniffing
|
||||
// but limit this to only the blobs/ (containing the config, and the layers)
|
||||
|
||||
// The idea here is that we try various formats in turn, and those tries should
|
||||
// never consume more bytes than this buffer contains so we can start again.
|
||||
|
||||
// 512 bytes ought to be enough (as that's the size of a TAR entry header),
|
||||
// but play it safe with 1024 bytes. This should also include very small layers
|
||||
// (unless they've also been gzipped, but Docker does not appear to do it)
|
||||
buffer := make([]byte, 1024)
|
||||
n, err := io.ReadFull(tarReader, buffer)
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
return img, err
|
||||
}
|
||||
|
||||
// Only try reading a TAR if file is "big enough"
|
||||
if n == cap(buffer) {
|
||||
var unwrappedReader io.Reader
|
||||
unwrappedReader, err = gzip.NewReader(io.MultiReader(bytes.NewReader(buffer[:n]), tarReader))
|
||||
if err != nil {
|
||||
// Not a gzipped entry
|
||||
unwrappedReader = io.MultiReader(bytes.NewReader(buffer[:n]), tarReader)
|
||||
}
|
||||
|
||||
// Try reading a TAR
|
||||
layerReader := tar.NewReader(unwrappedReader)
|
||||
tree, err := processLayerTar(name, layerReader)
|
||||
if err == nil {
|
||||
currentLayer++
|
||||
// add the layer to the image
|
||||
img.layerMap[tree.Name] = tree
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Not a TAR (or smaller than our buffer), might be a JSON file
|
||||
decoder := json.NewDecoder(bytes.NewReader(buffer[:n]))
|
||||
token, err := decoder.Token()
|
||||
if _, ok := token.(json.Delim); err == nil && ok {
|
||||
// Looks like a JSON object (or array)
|
||||
// XXX: should we add a header.Size check too?
|
||||
fileBuffer, err := io.ReadAll(io.MultiReader(bytes.NewReader(buffer[:n]), tarReader))
|
||||
if err != nil {
|
||||
return img, err
|
||||
}
|
||||
jsonFiles[name] = fileBuffer
|
||||
}
|
||||
// Ignore every other unknown file type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
10
go.mod
10
go.mod
|
@ -7,7 +7,7 @@ require (
|
|||
github.com/awesome-gocui/keybinding v1.0.1-0.20190805183143-864552bd36b7
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/docker/cli v0.0.0-20190906153656-016a3232168d
|
||||
github.com/docker/docker v24.0.2+incompatible
|
||||
github.com/docker/docker v24.0.7+incompatible
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/fatih/color v1.7.0
|
||||
github.com/google/uuid v1.1.1
|
||||
|
@ -20,7 +20,7 @@ require (
|
|||
github.com/spf13/afero v1.2.2
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/viper v1.4.0
|
||||
golang.org/x/net v0.11.0
|
||||
golang.org/x/net v0.17.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -52,9 +52,9 @@ require (
|
|||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/testify v1.4.0 // indirect
|
||||
golang.org/x/sys v0.9.0 // indirect
|
||||
golang.org/x/term v0.9.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/term v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||
gotest.tools v2.2.0+incompatible // indirect
|
||||
gotest.tools/v3 v3.5.0 // indirect
|
||||
|
|
20
go.sum
20
go.sum
|
@ -36,8 +36,8 @@ github.com/docker/cli v0.0.0-20190906153656-016a3232168d h1:gwX/88xJZfxZV1yjhhuQ
|
|||
github.com/docker/cli v0.0.0-20190906153656-016a3232168d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg=
|
||||
github.com/docker/docker v24.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
|
||||
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
|
@ -209,8 +209,8 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR
|
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -231,15 +231,15 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
|
||||
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
|
Loading…
Reference in a new issue