Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: golangci-lint
on:
push:
branches:
- main
pull_request:

jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.60
18 changes: 18 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Test
on:
push:
branches:
- main
pull_request:

jobs:
test:
name: tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable
- name: Test
run: go test -v ./...
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.vscode
.vscode
imager
53 changes: 53 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Options for analysis running.
run:
timeout: 3m
concurrency: 8

issues:
exclude-dirs:
- pkg/mycarehub/presentation/graph/
- ./tests/

linters:
disable-all: true
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- typecheck
- asciicheck
- dogsled
- goheader
- unused
- misspell
- rowserrcheck
- sqlclosecheck
- revive
- funlen
- gofmt
- unparam
- errorlint
- bodyclose
- gocritic
- nilerr
- ireturn
- importas
- wsl
- copyloopvar
- nilerr
- makezero
- reassign

linters-settings:
staticcheck:
checks: ["all"]
funlen:
lines: -1
statements: -1
gosec:
excludes:
- G601
- G304
- G101
49 changes: 45 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,46 @@
It is a simple utility that uses a link to a helm chart to search for container images defined in the chart, downloads the respective Docker images from their repositories, and prints info about the images i.e their sizes and number of layers.
# Imager

```bash
go run cmd/main.go list https://github.com/openfga/helm-charts/releases/download/openfga-0.2.19/openfga-0.2.19.tgz
```
A simple utility that uses a link to a Helm chart to search for container images defined in the chart, downloads the respective images and outputs info about the images i.e., their sizes and number of layers.

## Features

- Extracts container images from Helm charts.
- Downloads the specified images.
- Displays image size.
- Displays the number of image layers.

## Installation

### Prerequisites

- Go 1.23 or higher

### Building and Running

1. Build the `imager` binary

```bash
go build -o imager ./cmd/main.go
```
This command will create an executable file named imager in your current directory.

2. Run imager with the desired option
```bash
./imager <subcommand> <arguments>
```
Available subcommands:
- `list`: A CLI to show image information.
Example
```bash
./imager list https://github.com/openfga/helm-charts/releases/download/openfga-0.2.19/openfga-0.2.19.tgz
```
- `http-server`: Starts a HTTP server that exposes an API to show image information.
```bash
./imager http-server
```
Example request:
```bash
curl --location 'http://localhost:8080' --header 'Content-Type: application/json' --data '{"chart_url": "oci://registry-1.docker.io/bitnamicharts/airflow"}'
```


4 changes: 2 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ func main() {
return cli.Exit("Expected a link to a helm chart", 1)
}

chartUrl := cCtx.Args().First()
chartURL := cCtx.Args().First()

images, err := i.GetChartImagesDetails(cCtx.Context, chartUrl)
images, err := i.GetChartImagesDetails(cCtx.Context, chartURL)
if err != nil {
return cli.Exit(err.Error(), 1)
}
Expand Down
22 changes: 10 additions & 12 deletions pkg/imager/imager.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func NewImager() *Imager {
func (i Imager) extractImages(ctx context.Context, chart *chart.Chart) ([]string, error) {
release, err := i.client.RunWithContext(ctx, chart, map[string]interface{}{})
if err != nil {
return nil, fmt.Errorf("Error rendering templates: %v", err)
return nil, fmt.Errorf("Error rendering templates: %w", err)
}

reader := strings.NewReader(release.Manifest)
Expand All @@ -76,7 +76,7 @@ func (i Imager) extractImages(ctx context.Context, chart *chart.Chart) ([]string
break
}

return nil, fmt.Errorf("Failed to decode manifest: %v", err)
return nil, fmt.Errorf("Failed to decode manifest: %w", err)
}

obj, _, err := scheme.Codecs.UniversalDeserializer().Decode(rawObj.Raw, nil, nil)
Expand Down Expand Up @@ -105,23 +105,22 @@ func (i Imager) extractImages(ctx context.Context, chart *chart.Chart) ([]string
func (i Imager) getImageDetails(_ context.Context, containerImage string) (*ImageDetails, error) {
ref, err := name.ParseReference(containerImage)
if err != nil {
return nil, fmt.Errorf("failed to parse image reference: %v", err)

return nil, fmt.Errorf("failed to parse image reference: %w", err)
}

image, err := remote.Image(ref)
if err != nil {
return nil, fmt.Errorf("failed to pull image: %v", err)
return nil, fmt.Errorf("failed to pull image: %w", err)
}

layers, err := image.Layers()
if err != nil {
return nil, fmt.Errorf("failed to get image layers: %v", err)
return nil, fmt.Errorf("failed to get image layers: %w", err)
}

size, err := image.Size()
if err != nil {
return nil, fmt.Errorf("failed to get image size: %v", err)
return nil, fmt.Errorf("failed to get image size: %w", err)
}

details := ImageDetails{
Expand All @@ -137,18 +136,17 @@ func (i Imager) getImageDetails(_ context.Context, containerImage string) (*Imag
func (i Imager) GetChartImagesDetails(ctx context.Context, chartURL string) ([]*ImageDetails, error) {
chartPath, err := i.client.ChartPathOptions.LocateChart(chartURL, settings)
if err != nil {
return nil, fmt.Errorf("failed to locate chart from provided path: %v", err)
return nil, fmt.Errorf("failed to locate chart from provided path: %w", err)
}

chart, err := loader.Load(chartPath)
if err != nil {
return nil, fmt.Errorf("failed to load chart: %v", err)

return nil, fmt.Errorf("failed to load chart: %w", err)
}

containerImages, err := i.extractImages(ctx, chart)
if err != nil {
return nil, fmt.Errorf("failed to get images from chart: %v", err)
return nil, fmt.Errorf("failed to get images from chart: %w", err)
}

if len(containerImages) == 0 {
Expand All @@ -164,7 +162,7 @@ func (i Imager) GetChartImagesDetails(ctx context.Context, chartURL string) ([]*
for _, containerImage := range containerImages {
imageInfo, err := i.getImageDetails(ctx, containerImage)
if err != nil {
return nil, fmt.Errorf("failed to get images from main chart: %v", err)
return nil, fmt.Errorf("failed to get images from main chart: %w", err)
}

images = append(images, imageInfo)
Expand Down
4 changes: 3 additions & 1 deletion pkg/imager/imager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import (
)

func TestImager_GetChartImagesDetails(t *testing.T) {

type args struct {
ctx context.Context
chartURL string
}

tests := []struct {
name string
args args
Expand Down Expand Up @@ -63,11 +63,13 @@ func TestImager_GetChartImagesDetails(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
i := imager.NewImager()

got, err := i.GetChartImagesDetails(tt.args.ctx, tt.args.chartURL)
if (err != nil) != tt.wantErr {
t.Errorf("Imager.GetChartImagesDetails() error = %v, wantErr %v", err, tt.wantErr)
return
}

if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Imager.GetChartImagesDetails() = %v, want %v", got, tt.want)
}
Expand Down
Loading