Skip to content

Commit 2d24932

Browse files
Merge pull request #180 from ChristofferNissen/feat/output-artifacts-to-json
[feat] output artifacts overview to json
1 parent 1a7c982 commit 2d24932

File tree

6 files changed

+226
-5
lines changed

6 files changed

+226
-5
lines changed

example/helmper.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,7 @@ registries:
101101
insecure: true
102102
plainHTTP: true
103103
sourcePrefix: true
104+
export:
105+
artifacts:
106+
enabled: true
107+
folder: /workspace/.out/artifacts

internal/bootstrap/viper.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ type ImportConfigSection struct {
6161
AllowInsecure bool `yaml:"allowInsecure"`
6262
} `yaml:"cosign"`
6363
} `yaml:"import"`
64+
Export struct {
65+
Artifacts struct {
66+
Enabled bool `yaml:"enabled"`
67+
Folder string `yaml:"folder"`
68+
} `yaml:"artifacts"`
69+
} `yaml:"export"`
6470
}
6571

6672
type imageConfigSection struct {
@@ -139,6 +145,7 @@ func LoadViperConfiguration() (*viper.Viper, error) {
139145
viper.SetDefault("verbose", false)
140146
viper.SetDefault("update", false)
141147
viper.SetDefault("k8s_version", "1.31.1")
148+
viper.SetDefault("export.artifacts.enabled", false)
142149

143150
// Unmarshal registries config section
144151
conf := config{}
@@ -274,7 +281,15 @@ copacetic:
274281
`
275282
return nil, xerrors.Errorf("You have enabled copacetic patching but did not specify the path to the tars output folder'. Please add the value and try again\nExample:\n%s", s)
276283
}
277-
284+
if importConf.Export.Artifacts.Enabled && importConf.Export.Artifacts.Folder == "" {
285+
s := `
286+
export:
287+
artifacts:
288+
enabled: true
289+
folder: /workspace/.out/artifacts <---
290+
`
291+
return nil, xerrors.Errorf("You have enabled artifacts output but did not specify the output path. Please add the value and try again...\nExample config:\n%s", s)
292+
}
278293
}
279294

280295
viper.Set("importConfig", importConf)

internal/program.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
99
"github.com/common-nighthawk/go-figure"
10+
"github.com/spf13/afero"
1011
"github.com/spf13/viper"
1112
"go.uber.org/fx"
1213
"go.uber.org/fx/fxevent"
@@ -15,6 +16,7 @@ import (
1516
"github.com/ChristofferNissen/helmper/internal/bootstrap"
1617
"github.com/ChristofferNissen/helmper/pkg/copa"
1718
mySign "github.com/ChristofferNissen/helmper/pkg/cosign"
19+
"github.com/ChristofferNissen/helmper/pkg/exportArtifacts"
1820
"github.com/ChristofferNissen/helmper/pkg/flow"
1921
"github.com/ChristofferNissen/helmper/pkg/helm"
2022
"github.com/ChristofferNissen/helmper/pkg/image"
@@ -260,8 +262,7 @@ func program(ctx context.Context, _ []string, viper *viper.Viper, settings *cli.
260262
}
261263
vo.Report.Render()
262264
so := mySign.SignOption{
263-
Data: imgs,
264-
265+
Data: imgs,
265266
KeyRef: importConfig.Import.Cosign.KeyRef,
266267
KeyRefPass: *importConfig.Import.Cosign.KeyRefPass,
267268
AllowInsecure: importConfig.Import.Cosign.AllowInsecure,
@@ -271,6 +272,19 @@ func program(ctx context.Context, _ []string, viper *viper.Viper, settings *cli.
271272
return err
272273
}
273274
}
274-
275+
// Step 7: Export artifacts to json
276+
if importConfig.Export.Artifacts.Enabled {
277+
folder := importConfig.Export.Artifacts.Folder
278+
eo := exportArtifacts.ExportOption{
279+
Fs: afero.NewOsFs(),
280+
Image: mImgs,
281+
Chart: mCharts,
282+
}
283+
_, _, err = eo.Run(context.WithoutCancel(ctx), folder)
284+
if err != nil {
285+
slog.Error("Error generating artifacts file.")
286+
return err
287+
}
288+
}
275289
return nil
276-
}
290+
}

pkg/exportArtifacts/main.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package exportArtifacts
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"log/slog"
8+
9+
"github.com/ChristofferNissen/helmper/pkg/helm"
10+
"github.com/spf13/afero"
11+
)
12+
13+
type ExportOption struct {
14+
Fs afero.Fs
15+
Image helm.RegistryImageStatus
16+
Chart helm.RegistryChartStatus
17+
}
18+
19+
type ChartArtifact struct {
20+
ChartOverview string `json:"chart_overview"`
21+
ChartName string `json:"chart_name"`
22+
Repository string `json:"repository"`
23+
ChartVersion string `json:"chart_version"`
24+
ChartPath string `json:"chart_artifact_path"`
25+
}
26+
27+
type ImageArtifact struct {
28+
ImageOverview string `json:"image_overview"`
29+
ImageName string `json:"image_name"`
30+
ImageTag string `json:"image_tag"`
31+
}
32+
33+
func (eo *ExportOption) Run(ctx context.Context, folder string) ([]ImageArtifact, []ChartArtifact, error) {
34+
// Collect image data
35+
imageArtifacts := []ImageArtifact{}
36+
for r, i := range eo.Image {
37+
for img := range i {
38+
overview := fmt.Sprintf("Registry: %s, Image: %s, Tag: %s",
39+
r.GetName(),
40+
img.String(), img.Tag)
41+
ia := ImageArtifact{
42+
ImageOverview: overview,
43+
ImageName: img.String(),
44+
ImageTag: img.Tag,
45+
}
46+
imageArtifacts = append(imageArtifacts, ia)
47+
}
48+
}
49+
50+
// Collect chart data
51+
chartArtifacts := []ChartArtifact{}
52+
for r, c := range eo.Chart {
53+
for chart := range c {
54+
overview := fmt.Sprintf("Registry: %s, Chart: %s, Version: %s, ChartPath: %s",
55+
r.Name, chart.Name, chart.Version, fmt.Sprintf("charts/%s", chart.Name))
56+
57+
ca := ChartArtifact{
58+
ChartOverview: overview,
59+
ChartName: chart.Name,
60+
ChartVersion: chart.Version,
61+
ChartPath: fmt.Sprintf("charts/%s", chart.Name),
62+
}
63+
chartArtifacts = append(chartArtifacts, ca)
64+
}
65+
}
66+
67+
exportData := struct {
68+
Images []ImageArtifact `json:"images"`
69+
Charts []ChartArtifact `json:"charts"`
70+
}{
71+
Images: imageArtifacts,
72+
Charts: chartArtifacts,
73+
}
74+
75+
jsonData, err := json.MarshalIndent(exportData, "", " ")
76+
if err != nil {
77+
slog.Error("Failed to export data to JSON", slog.String("error", err.Error()))
78+
return nil, nil, fmt.Errorf("failed to export data to JSON: %w", err)
79+
}
80+
81+
destPath := "artifacts.json"
82+
if folder != "" {
83+
err = eo.Fs.MkdirAll(folder, 0755)
84+
if err != nil {
85+
slog.Error("Failed to create directory", slog.String("folder", folder), slog.String("error", err.Error()))
86+
return nil, nil, fmt.Errorf("failed to save file in the specified location %s: %w", folder, err)
87+
}
88+
destPath = fmt.Sprintf("%s/%s", folder, destPath)
89+
} else {
90+
destPath = "./" + destPath
91+
slog.Info("No folder specified, saving in the root directory")
92+
}
93+
94+
err = afero.WriteFile(eo.Fs, destPath, jsonData, 0644)
95+
if err != nil {
96+
slog.Error("Failed to write artifacts to", destPath, slog.String("error", err.Error()))
97+
return nil, nil, fmt.Errorf("failed to write artifacts to %s: %w", destPath, err)
98+
}
99+
100+
slog.Info("Exported artifacts", slog.String("path", destPath))
101+
return imageArtifacts, chartArtifacts, nil
102+
}

pkg/exportArtifacts/main_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package exportArtifacts
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"testing"
7+
8+
"github.com/ChristofferNissen/helmper/pkg/helm"
9+
"github.com/ChristofferNissen/helmper/pkg/image"
10+
"github.com/ChristofferNissen/helmper/pkg/registry"
11+
"github.com/spf13/afero"
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
func TestExportOptionRun(t *testing.T) {
16+
// Arrange
17+
mockRegistry := &registry.Registry{Name: "azure.registry.io"}
18+
mockImage := &image.Image{Repository: "argocd", Tag: "v2.0.5"}
19+
mockChart := &helm.Chart{Name: "prometheus", Version: "1.0.0"}
20+
21+
mockData := helm.RegistryImageStatus{
22+
mockRegistry: {
23+
mockImage: true,
24+
},
25+
}
26+
27+
mockData2 := helm.RegistryChartStatus {
28+
mockRegistry: {
29+
mockChart: true,
30+
},
31+
}
32+
33+
mockFs := afero.NewMemMapFs()
34+
eo := &ExportOption{
35+
Fs: mockFs,
36+
Image: mockData,
37+
Chart: mockData2,
38+
}
39+
40+
// Act
41+
imgOverview, chartOverview, err := eo.Run(context.Background(), "")
42+
43+
// Assert
44+
45+
assert.NoError(t, err)
46+
47+
content, err := afero.ReadFile(mockFs, "artifacts.json")
48+
assert.NoError(t, err)
49+
50+
51+
var artifact struct {
52+
Images []ImageArtifact `json:"images"`
53+
Charts []ChartArtifact `json:"charts"`
54+
}
55+
err = json.Unmarshal(content, &artifact)
56+
assert.NoError(t, err)
57+
58+
assert.EqualValues(t, imgOverview, artifact.Images)
59+
assert.EqualValues(t, chartOverview, artifact.Charts)
60+
}
61+
62+
func TestExportOptionRun_NoData(t *testing.T) {
63+
// Arrange
64+
mockFs := afero.NewMemMapFs()
65+
66+
eo := &ExportOption{
67+
Fs: mockFs,
68+
Image: helm.RegistryImageStatus{},
69+
Chart: helm.RegistryChartStatus{},
70+
}
71+
72+
// Act
73+
imgOverview, chartOverview, err := eo.Run(context.Background(), "")
74+
75+
// Assert
76+
assert.NoError(t, err)
77+
assert.Empty(t, imgOverview, "expected no image artifacts")
78+
assert.Empty(t, chartOverview, "expected no chart artifacts")
79+
}

website/docs/config.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ registries:
113113
url: 0.0.0.0:5000
114114
insecure: true
115115
plainHTTP: true
116+
export:
117+
artifacts:
118+
enabled: true
119+
folder: /workspace/.out/artifacts
116120
```
117121
118122
## Configuration options
@@ -186,6 +190,9 @@ registries:
186190
| `mirrors` | list(object) | [] | false | Enable use of registry mirrors |
187191
| `mirrors.registry` | string | "" | true | Registry to configure mirror for fx docker.io |
188192
| `mirrors.mirror` | string | "" | true | Registry Mirror URL |
193+
| `export` | object | nil | false | Configuration for exporting artifacts |
194+
| `export.artifacts.enabled` | bool | false | false | Enable exporting of charts and images overview to json |
195+
| `export.artifacts.folder` | string | "" | true | Path to the folder where artifacts will be exported |
189196

190197
## Charts
191198

0 commit comments

Comments
 (0)