-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b4e450a
Showing
9 changed files
with
1,643 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
docker-inspector-* | ||
cmd/docker-inspector/internal-inspector |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2024 Hans Raaf | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
.PHONY: all clean | ||
|
||
BINARY_NAME=docker-inspector | ||
INTERNAL_BINARY=internal-inspector | ||
|
||
all: clean $(BINARY_NAME) | ||
|
||
clean: | ||
rm -f $(BINARY_NAME) cmd/docker-inspector/$(INTERNAL_BINARY) | ||
|
||
# Build the internal Linux inspector first | ||
cmd/docker-inspector/$(INTERNAL_BINARY): cmd/internal-inspector/main.go | ||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o cmd/docker-inspector/$(INTERNAL_BINARY) ./cmd/internal-inspector | ||
|
||
# Build the main wrapper for the current platform | ||
$(BINARY_NAME): cmd/docker-inspector/$(INTERNAL_BINARY) | ||
go build -o $(BINARY_NAME) ./cmd/docker-inspector | ||
|
||
# Build for specific platforms | ||
.PHONY: darwin linux windows | ||
darwin: clean cmd/docker-inspector/$(INTERNAL_BINARY) | ||
GOOS=darwin GOARCH=amd64 go build -o $(BINARY_NAME)-darwin ./cmd/docker-inspector | ||
|
||
linux: clean cmd/docker-inspector/$(INTERNAL_BINARY) | ||
GOOS=linux GOARCH=amd64 go build -o $(BINARY_NAME)-linux ./cmd/docker-inspector | ||
|
||
windows: clean cmd/docker-inspector/$(INTERNAL_BINARY) | ||
GOOS=windows GOARCH=amd64 go build -o $(BINARY_NAME).exe ./cmd/docker-inspector |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
# Docker Image Inspector | ||
|
||
A command-line tool to inspect the contents of Docker images without having to manually create containers or extract tar files. The tool creates a temporary container, inspects its filesystem, and cleans up automatically. | ||
|
||
## Features | ||
|
||
- Cross-platform: Runs on macOS, Linux, and Windows (with Linux containers) | ||
- Inspects any Docker image without modifying it | ||
- Recursive directory listing | ||
- Glob pattern support (including `**/`) for finding specific files | ||
- MD5 checksum calculation for files | ||
- JSON output option for automated processing | ||
- Detailed summaries of files, directories, and sizes | ||
- Clean handling of special filesystems (/proc, /sys, etc.) | ||
- Modification time handling for reliable diffs | ||
|
||
## Installation | ||
|
||
1. Clone the repository | ||
2. Build for your platform: | ||
```bash | ||
# For macOS | ||
make darwin | ||
|
||
# For Linux | ||
make linux | ||
|
||
# For Windows | ||
make windows | ||
``` | ||
|
||
## Usage | ||
|
||
Basic usage: | ||
```bash | ||
./docker-inspector nginx:latest | ||
``` | ||
|
||
With options: | ||
```bash | ||
# Find specific files | ||
./docker-inspector nginx:latest --glob "**/*.conf" | ||
|
||
# Calculate MD5 checksums | ||
./docker-inspector nginx:latest --md5 | ||
|
||
# Output as JSON | ||
./docker-inspector nginx:latest --json > nginx-files.json | ||
|
||
# Inspect specific path | ||
./docker-inspector nginx:latest --path /etc/nginx | ||
|
||
# Keep container for further inspection | ||
./docker-inspector nginx:latest --keep | ||
``` | ||
|
||
### Comparing Images | ||
|
||
To compare the contents of two images or two runs of the same image: | ||
|
||
```bash | ||
# With modification times (might show differences due to container startup) | ||
./docker-inspector nginx:latest --json --md5 > run1.txt | ||
./docker-inspector nginx:latest --json --md5 > run2.txt | ||
diff run1.txt run2.txt | ||
|
||
# Without modification times (more reliable for structural comparisons) | ||
./docker-inspector nginx:latest --json --md5 --no-times > run1.txt | ||
./docker-inspector nginx:latest --json --md5 --no-times > run2.txt | ||
diff run1.txt run2.txt | ||
``` | ||
|
||
Note: Files like /etc/resolv.conf typically show modification time differences between runs due to container startup configuration. Using --md5 helps identify actual content changes regardless of timestamps. | ||
|
||
### Options | ||
|
||
``` | ||
--path Path inside the container to inspect (default: "/") | ||
--json Output in JSON format | ||
--summary Show summary statistics | ||
--glob Glob pattern for matching files (supports **/) | ||
--md5 Calculate MD5 checksums for files | ||
--keep Keep the temporary container after inspection | ||
--no-times Exclude modification times from output (useful for diffs) | ||
``` | ||
|
||
## How It Works | ||
|
||
The tool: | ||
1. Creates a temporary container from the specified image | ||
2. Copies a specialized Linux inspector binary into the container | ||
3. Executes the inspector inside the container | ||
4. Collects and formats the results | ||
5. Automatically cleans up the container (unless --keep is specified) | ||
|
||
## Building from Source | ||
|
||
Requires: | ||
- Go 1.21 or later | ||
- Docker running with Linux containers | ||
- make | ||
|
||
```bash | ||
# Build for current platform | ||
make | ||
|
||
# Or for specific platform | ||
make darwin # For macOS | ||
make linux # For Linux | ||
make windows # For Windows | ||
``` | ||
|
||
## Credits | ||
|
||
Most of the implementation work was done by Claude (Anthropic) in a conversation about Docker image inspection requirements and cross-platform Go development. The original concept and requirements were provided by the repository owner. | ||
|
||
## License | ||
|
||
[MIT-License](LICENSE.txt) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package main | ||
|
||
import ( | ||
_ "embed" | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
"time" | ||
|
||
"github.com/alexflint/go-arg" | ||
) | ||
|
||
//go:embed internal-inspector | ||
var internalInspector []byte | ||
|
||
type Args struct { | ||
Image string `arg:"positional,required" help:"docker image to inspect"` | ||
Path string `arg:"--path" default:"/" help:"path inside the container to inspect"` | ||
JSON bool `arg:"--json" help:"output in JSON format"` | ||
Summary bool `arg:"--summary" help:"show summary statistics"` | ||
Pattern string `arg:"--glob" help:"glob pattern for matching files (supports **/)"` | ||
MD5 bool `arg:"--md5" help:"calculate MD5 checksums for files"` | ||
Keep bool `arg:"--keep" help:"keep the temporary container after inspection"` | ||
NoTimes bool `arg:"--no-times" help:"exclude modification times from output"` | ||
} | ||
|
||
func (Args) Version() string { | ||
return "docker-inspector 1.0.0" | ||
} | ||
|
||
func (Args) Description() string { | ||
return "Docker image content inspector - examines files and directories inside a container image" | ||
} | ||
|
||
func runInspector(containerID string, args Args) error { | ||
// Create a temporary directory for the inspector | ||
tempDir, err := os.MkdirTemp("", "docker-inspector-*") | ||
if err != nil { | ||
return fmt.Errorf("failed to create temp dir: %v", err) | ||
} | ||
defer os.RemoveAll(tempDir) | ||
|
||
// Write the embedded Linux inspector to the temp directory | ||
inspectorPath := filepath.Join(tempDir, "internal-inspector") | ||
if err := os.WriteFile(inspectorPath, internalInspector, 0755); err != nil { | ||
return fmt.Errorf("failed to write inspector: %v", err) | ||
} | ||
|
||
// Copy the inspector to the container | ||
copyCmd := exec.Command("docker", "cp", inspectorPath, fmt.Sprintf("%s:/inspect", containerID)) | ||
if output, err := copyCmd.CombinedOutput(); err != nil { | ||
return fmt.Errorf("failed to copy inspector to container: %v\n%s", err, output) | ||
} | ||
|
||
// Build the command arguments | ||
dockerArgs := []string{"exec", containerID, "/inspect"} | ||
if args.JSON { | ||
dockerArgs = append(dockerArgs, "--json") | ||
} | ||
if args.Summary { | ||
dockerArgs = append(dockerArgs, "--summary") | ||
} | ||
if args.Pattern != "" { | ||
dockerArgs = append(dockerArgs, "--glob", args.Pattern) | ||
} | ||
if args.MD5 { | ||
dockerArgs = append(dockerArgs, "--md5") | ||
} | ||
if args.NoTimes { | ||
dockerArgs = append(dockerArgs, "--no-times") | ||
} | ||
if args.Path != "/" { | ||
dockerArgs = append(dockerArgs, "--path", args.Path) | ||
} | ||
|
||
cmd := exec.Command("docker", dockerArgs...) | ||
cmd.Stdout = os.Stdout | ||
cmd.Stderr = os.Stderr | ||
return cmd.Run() | ||
} | ||
|
||
func main() { | ||
var args Args | ||
// Set defaults | ||
args.Summary = false | ||
args.Path = "/" | ||
|
||
arg.MustParse(&args) | ||
|
||
// First, ensure the image exists or can be pulled | ||
pullCmd := exec.Command("docker", "pull", args.Image) | ||
pullCmd.Stderr = os.Stderr | ||
if err := pullCmd.Run(); err != nil { | ||
fmt.Fprintf(os.Stderr, "Failed to pull image %s: %v\n", args.Image, err) | ||
os.Exit(1) | ||
} | ||
|
||
// Start a temporary container | ||
startCmd := exec.Command("docker", "run", "-d", "--entrypoint", "sleep", args.Image, "3600") | ||
output, err := startCmd.CombinedOutput() | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Failed to start container: %v\n%s\n", err, output) | ||
os.Exit(1) | ||
} | ||
|
||
containerID := strings.TrimSpace(string(output)) | ||
if containerID == "" { | ||
fmt.Fprintf(os.Stderr, "Failed to get container ID\n") | ||
os.Exit(1) | ||
} | ||
|
||
// Give the container a moment to start | ||
time.Sleep(1 * time.Second) | ||
|
||
// Ensure container cleanup unless --keep is specified | ||
if !args.Keep { | ||
defer func() { | ||
stopCmd := exec.Command("docker", "rm", "-f", containerID) | ||
stopCmd.Run() | ||
}() | ||
} | ||
|
||
// Run the inspection | ||
if err := runInspector(containerID, args); err != nil { | ||
fmt.Fprintf(os.Stderr, "Inspection failed: %v\n", err) | ||
os.Exit(1) | ||
} | ||
|
||
if args.Keep { | ||
fmt.Printf("\nContainer ID: %s\n", containerID) | ||
} | ||
} |
Oops, something went wrong.