Skip to content

Commit

Permalink
feat: provide commit msg output for helm-update-image
Browse files Browse the repository at this point in the history
Signed-off-by: Hidde Beydals <[email protected]>
  • Loading branch information
hiddeco committed Sep 10, 2024
1 parent df87d55 commit eb34543
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 36 deletions.
60 changes: 43 additions & 17 deletions internal/directives/helm_update_image_directive.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package directives
import (
"context"
"fmt"
"strings"

securejoin "github.com/cyphar/filepath-securejoin"
"github.com/xeipuuv/gojsonschema"
Expand Down Expand Up @@ -62,26 +63,48 @@ func (d *helmUpdateImageDirective) run(
stepCtx *StepContext,
cfg HelmUpdateImageConfig,
) (Result, error) {
changes, err := d.generateImageUpdates(ctx, stepCtx, cfg)
updates, fullImageRefs, err := d.generateImageUpdates(ctx, stepCtx, cfg)
if err != nil {
return Result{Status: StatusFailure}, fmt.Errorf("failed to generate image updates: %w", err)
}

if len(changes) > 0 {
if err := d.updateValuesFile(stepCtx.WorkDir, cfg.Path, changes); err != nil {
var commitMsg strings.Builder
if len(updates) > 0 {
if err = d.updateValuesFile(stepCtx.WorkDir, cfg.Path, updates); err != nil {
return Result{Status: StatusFailure}, fmt.Errorf("values file update failed: %w", err)
}

if len(fullImageRefs) > 0 {
_, _ = commitMsg.WriteString("Updated ")
_, _ = commitMsg.WriteString(cfg.Path)
_, _ = commitMsg.WriteString(" to use new image")
if len(fullImageRefs) > 1 {
_, _ = commitMsg.WriteString("s")
}
_, _ = commitMsg.WriteString("\n")
for _, s := range fullImageRefs {
_, _ = commitMsg.WriteString("\n- ")
_, _ = commitMsg.WriteString(s)
}
}
}

return Result{Status: StatusSuccess}, nil
result := Result{Status: StatusSuccess}
if commitMsg.Len() > 0 {
result.Output = make(State, 1)
result.Output.Set("commitMessage", commitMsg.String())
}
return result, nil
}

func (d *helmUpdateImageDirective) generateImageUpdates(
ctx context.Context,
stepCtx *StepContext,
cfg HelmUpdateImageConfig,
) (map[string]string, error) {
changes := make(map[string]string, len(cfg.Images))
) (map[string]string, []string, error) {
updates := make(map[string]string, len(cfg.Images))
fullImageRefs := make([]string, 0, len(cfg.Images))

for _, image := range cfg.Images {
desiredOrigin := d.getDesiredOrigin(image.FromOrigin)

Expand All @@ -95,21 +118,22 @@ func (d *helmUpdateImageDirective) generateImageUpdates(
image.Image,
)
if err != nil {
return nil, fmt.Errorf("failed to find image %s: %w", image.Image, err)
return nil, nil, fmt.Errorf("failed to find image %s: %w", image.Image, err)
}

if targetImage == nil {
continue
}

value, err := d.getImageValue(targetImage, image.Value)
value, imageRef, err := d.getImageValues(targetImage, image.Value)
if err != nil {
return nil, err
return nil, nil, err
}

changes[image.Key] = value
updates[image.Key] = value
fullImageRefs = append(fullImageRefs, imageRef)
}
return changes, nil
return updates, fullImageRefs, nil
}

func (d *helmUpdateImageDirective) getDesiredOrigin(fromOrigin *ChartFromOrigin) *kargoapi.FreightOrigin {
Expand All @@ -122,18 +146,20 @@ func (d *helmUpdateImageDirective) getDesiredOrigin(fromOrigin *ChartFromOrigin)
}
}

func (d *helmUpdateImageDirective) getImageValue(image *kargoapi.Image, valueType Value) (string, error) {
func (d *helmUpdateImageDirective) getImageValues(image *kargoapi.Image, valueType Value) (string, string, error) {
switch valueType {
case ImageAndTag:
return fmt.Sprintf("%s:%s", image.RepoURL, image.Tag), nil
imageRef := fmt.Sprintf("%s:%s", image.RepoURL, image.Tag)
return imageRef, imageRef, nil
case Tag:
return image.Tag, nil
return image.Tag, fmt.Sprintf("%s:%s", image.RepoURL, image.Tag), nil
case ImageAndDigest:
return fmt.Sprintf("%s@%s", image.RepoURL, image.Digest), nil
imageRef := fmt.Sprintf("%s@%s", image.RepoURL, image.Digest)
return imageRef, imageRef, nil
case Digest:
return image.Digest, nil
return image.Digest, fmt.Sprintf("%s@%s", image.RepoURL, image.Digest), nil
default:
return "", fmt.Errorf("unknown image value type %q", valueType)
return "", "", fmt.Errorf("unknown image value type %q", valueType)
}
}

Expand Down
54 changes: 35 additions & 19 deletions internal/directives/helm_update_image_directive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,12 @@ func Test_helmUpdateImageDirective_run(t *testing.T) {
},
assertions: func(t *testing.T, workDir string, result Result, err error) {
assert.NoError(t, err)
assert.Equal(t, Result{Status: StatusSuccess}, result)
assert.Equal(t, Result{
Status: StatusSuccess,
Output: State{
"commitMessage": "Updated values.yaml to use new image\n\n- docker.io/library/nginx:1.19.0",
},
}, result)
content, err := os.ReadFile(path.Join(workDir, "values.yaml"))
require.NoError(t, err)
assert.Contains(t, string(content), "tag: 1.19.0")
Expand Down Expand Up @@ -223,7 +228,7 @@ func Test_helmUpdateImageDirective_generateImageUpdates(t *testing.T) {
objects []client.Object
stepCtx *StepContext
cfg HelmUpdateImageConfig
assertions func(*testing.T, map[string]string, error)
assertions func(*testing.T, map[string]string, []string, error)
}{
{
name: "finds image update",
Expand Down Expand Up @@ -267,9 +272,10 @@ func Test_helmUpdateImageDirective_generateImageUpdates(t *testing.T) {
{Key: "image.tag", Image: "docker.io/library/nginx", Value: Tag},
},
},
assertions: func(t *testing.T, changes map[string]string, err error) {
assertions: func(t *testing.T, changes map[string]string, summary []string, err error) {
assert.NoError(t, err)
assert.Equal(t, map[string]string{"image.tag": "1.19.0"}, changes)
assert.Equal(t, []string{"docker.io/library/nginx:1.19.0"}, summary)
},
},
{
Expand All @@ -284,9 +290,10 @@ func Test_helmUpdateImageDirective_generateImageUpdates(t *testing.T) {
{Key: "image.tag", Image: "docker.io/library/non-existent", Value: Tag},
},
},
assertions: func(t *testing.T, changes map[string]string, err error) {
assertions: func(t *testing.T, changes map[string]string, summary []string, err error) {
assert.NoError(t, err)
assert.Empty(t, changes)
assert.Empty(t, summary)
},
},
{
Expand Down Expand Up @@ -332,9 +339,10 @@ func Test_helmUpdateImageDirective_generateImageUpdates(t *testing.T) {
{Key: "image2.tag", Image: "docker.io/library/non-existent", Value: Tag},
},
},
assertions: func(t *testing.T, changes map[string]string, err error) {
assertions: func(t *testing.T, changes map[string]string, summary []string, err error) {
assert.NoError(t, err)
assert.Equal(t, map[string]string{"image1.tag": "1.19.0"}, changes)
assert.Equal(t, []string{"docker.io/library/nginx:1.19.0"}, summary)
},
},
{
Expand Down Expand Up @@ -384,9 +392,10 @@ func Test_helmUpdateImageDirective_generateImageUpdates(t *testing.T) {
},
},
},
assertions: func(t *testing.T, changes map[string]string, err error) {
assertions: func(t *testing.T, changes map[string]string, summary []string, err error) {
assert.NoError(t, err)
assert.Equal(t, map[string]string{"image.tag": "2.0.0"}, changes)
assert.Equal(t, []string{"docker.io/library/origin-image:2.0.0"}, summary)
},
},
}
Expand All @@ -400,8 +409,8 @@ func Test_helmUpdateImageDirective_generateImageUpdates(t *testing.T) {
stepCtx.KargoClient = fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.objects...).Build()

d := &helmUpdateImageDirective{}
changes, err := d.generateImageUpdates(context.Background(), stepCtx, tt.cfg)
tt.assertions(t, changes, err)
changes, summary, err := d.generateImageUpdates(context.Background(), stepCtx, tt.cfg)
tt.assertions(t, changes, summary, err)
})
}
}
Expand Down Expand Up @@ -442,12 +451,12 @@ func Test_helmUpdateImageDirective_getDesiredOrigin(t *testing.T) {
}
}

func Test_helmUpdateImageDirective_getImageValue(t *testing.T) {
func Test_helmUpdateImageDirective_getImageValues(t *testing.T) {
tests := []struct {
name string
image *kargoapi.Image
valueType Value
assertions func(*testing.T, string, error)
assertions func(*testing.T, string, string, error)
}{
{
name: "image and tag",
Expand All @@ -456,20 +465,23 @@ func Test_helmUpdateImageDirective_getImageValue(t *testing.T) {
Tag: "1.19",
},
valueType: ImageAndTag,
assertions: func(t *testing.T, value string, err error) {
assertions: func(t *testing.T, value, ref string, err error) {
assert.NoError(t, err)
assert.Equal(t, "docker.io/library/nginx:1.19", value)
assert.Equal(t, "docker.io/library/nginx:1.19", ref)
},
},
{
name: "tag only",
image: &kargoapi.Image{
Tag: "1.19",
RepoURL: "docker.io/library/nginx",
Tag: "1.19",
},
valueType: Tag,
assertions: func(t *testing.T, value string, err error) {
assertions: func(t *testing.T, value, ref string, err error) {
assert.NoError(t, err)
assert.Equal(t, "1.19", value)
assert.Equal(t, "docker.io/library/nginx:1.19", ref)
},
},
{
Expand All @@ -479,38 +491,42 @@ func Test_helmUpdateImageDirective_getImageValue(t *testing.T) {
Digest: "sha256:abcdef1234567890",
},
valueType: ImageAndDigest,
assertions: func(t *testing.T, value string, err error) {
assertions: func(t *testing.T, value, ref string, err error) {
assert.NoError(t, err)
assert.Equal(t, "docker.io/library/nginx@sha256:abcdef1234567890", value)
assert.Equal(t, "docker.io/library/nginx@sha256:abcdef1234567890", ref)
},
},
{
name: "digest only",
image: &kargoapi.Image{
Digest: "sha256:abcdef1234567890",
RepoURL: "docker.io/library/nginx",
Digest: "sha256:abcdef1234567890",
},
valueType: Digest,
assertions: func(t *testing.T, value string, err error) {
assertions: func(t *testing.T, value, ref string, err error) {
assert.NoError(t, err)
assert.Equal(t, "sha256:abcdef1234567890", value)
assert.Equal(t, "docker.io/library/nginx@sha256:abcdef1234567890", ref)
},
},
{
name: "unknown value type",
image: &kargoapi.Image{},
valueType: "unknown",
assertions: func(t *testing.T, value string, err error) {
assertions: func(t *testing.T, value, ref string, err error) {
assert.Error(t, err)
assert.Empty(t, value)
assert.Empty(t, ref)
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := &helmUpdateImageDirective{}
value, err := d.getImageValue(tt.image, tt.valueType)
tt.assertions(t, value, err)
value, ref, err := d.getImageValues(tt.image, tt.valueType)
tt.assertions(t, value, ref, err)
})
}
}
Expand Down

0 comments on commit eb34543

Please sign in to comment.