Skip to content

Commit

Permalink
Initial development to be compatible with multiple application sources
Browse files Browse the repository at this point in the history
  • Loading branch information
Casey Morton committed Oct 20, 2023
1 parent 117ca31 commit dc7bf29
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 254 deletions.
119 changes: 76 additions & 43 deletions pkg/argocd/argocd.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,6 @@ func FilterApplicationsForUpdate(apps []v1alpha1.Application, patterns []string,
continue
}

// Check for valid application type
if !IsValidApplicationType(&app) {
logCtx.Warnf("skipping app '%s' of type '%s' because it's not of supported source type", app.GetName(), app.Status.SourceType)
continue
}

// Check if application name matches requested patterns
if !nameMatchesPattern(app.GetName(), patterns) {
logCtx.Debugf("Skipping app '%s' because it does not match requested patterns", app.GetName())
Expand All @@ -201,13 +195,20 @@ func FilterApplicationsForUpdate(apps []v1alpha1.Application, patterns []string,
logCtx.Debugf("Skipping app '%s' because it does not carry requested label", app.GetName())
continue
}
for sourceIndex, _ := range getApplicationTypes(&app) {
// Check for valid application type
if !IsValidApplicationTypeForSource(&app, sourceIndex) {
logCtx.Infof("skipping app '%s' of type '%s' because it's not of supported source type", app.GetName(), GetSourceTypes(app.Status)[sourceIndex])
continue
}

logCtx.Tracef("processing app '%s' of type '%v'", app.GetName(), app.Status.SourceType)
imageList := parseImageList(annotations)
appImages := ApplicationImages{}
appImages.Application = app
appImages.Images = *imageList
appsForUpdate[app.GetName()] = appImages
logCtx.Tracef("processing app '%s' of type '%v'", app.GetName(), app.Status.SourceType)
imageList := parseImageList(annotations)
appImages := ApplicationImages{}
appImages.Application = app
appImages.Images = *imageList
appsForUpdate[app.GetName()] = appImages
}
}

return appsForUpdate, nil
Expand Down Expand Up @@ -378,10 +379,10 @@ func mergeHelmParams(src []v1alpha1.HelmParameter, merge []v1alpha1.HelmParamete
return retParams
}

// SetHelmImage sets image parameters for a Helm application
func SetHelmImage(app *v1alpha1.Application, newImage *image.ContainerImage) error {
if appType := getApplicationType(app); appType != ApplicationTypeHelm {
return fmt.Errorf("cannot set Helm params on non-Helm application")
// SetHelmImageWithIndex sets image parameters for a Helm application and a specific sourceIndex
func SetHelmImageWithIndex(app *v1alpha1.Application, sourceIndex int, newImage *image.ContainerImage) bool {
if appType := getApplicationTypes(app)[sourceIndex]; appType != ApplicationTypeHelm {
return false
}

appName := app.GetName()
Expand Down Expand Up @@ -425,23 +426,24 @@ func SetHelmImage(app *v1alpha1.Application, newImage *image.ContainerImage) err
}
}

if app.Spec.GetSources()[0].Helm == nil {
app.Spec.GetSources()[0].Helm = &v1alpha1.ApplicationSourceHelm{}
if app.Spec.GetSources()[sourceIndex].Helm == nil {
app.Spec.GetSources()[sourceIndex].Helm = &v1alpha1.ApplicationSourceHelm{}
}

if app.Spec.GetSources()[0].Helm.Parameters == nil {
app.Spec.GetSources()[0].Helm.Parameters = make([]v1alpha1.HelmParameter, 0)
if app.Spec.GetSources()[sourceIndex].Helm.Parameters == nil {
app.Spec.GetSources()[sourceIndex].Helm.Parameters = make([]v1alpha1.HelmParameter, 0)
}

app.Spec.GetSources()[0].Helm.Parameters = mergeHelmParams(app.Spec.GetSources()[0].Helm.Parameters, mergeParams)
app.Spec.GetSources()[sourceIndex].Helm.Parameters = mergeHelmParams(app.Spec.GetSources()[sourceIndex].Helm.Parameters, mergeParams)

return nil
return true
}

// SetKustomizeImage sets a Kustomize image for given application
func SetKustomizeImage(app *v1alpha1.Application, newImage *image.ContainerImage) error {
if appType := getApplicationType(app); appType != ApplicationTypeKustomize {
return fmt.Errorf("cannot set Kustomize image on non-Kustomize application")
// SetKustomizeImageWithIndex sets a Kustomize image for given application
// returns whether that specific source was updated
func SetKustomizeImageWithIndex(app *v1alpha1.Application, sourceIndex int, newImage *image.ContainerImage) bool {
if appType := getApplicationTypes(app)[sourceIndex]; appType != ApplicationTypeKustomize {
return false
}

var ksImageParam string
Expand All @@ -454,7 +456,7 @@ func SetKustomizeImage(app *v1alpha1.Application, newImage *image.ContainerImage

log.WithContext().AddField("application", app.GetName()).Tracef("Setting Kustomize parameter %s", ksImageParam)

var source = app.Spec.GetSources()[0]
var source = app.Spec.GetSources()[sourceIndex]
if source.Kustomize == nil {
source.Kustomize = &v1alpha1.ApplicationSourceKustomize{}
}
Expand All @@ -473,12 +475,12 @@ func SetKustomizeImage(app *v1alpha1.Application, newImage *image.ContainerImage
source.Kustomize.MergeImage(v1alpha1.KustomizeImage(ksImageParam))

if app.Spec.HasMultipleSources() {
app.Spec.Sources[0] = source
app.Spec.Sources[sourceIndex] = source
} else {
app.Spec.Source = &source
}

return nil
return true
}

// GetImagesFromApplication returns the list of known images for the given application
Expand All @@ -503,33 +505,50 @@ func GetImagesFromApplication(app *v1alpha1.Application) image.ContainerImageLis
return images
}

// GetApplicationTypeByName first retrieves application with given appName and
// returns its application type
func GetApplicationTypeByName(client ArgoCD, appName string) (ApplicationType, error) {
// GetApplicationTypesByName first retrieves application with given appName and
// returns its application types
func GetApplicationTypesByName(client ArgoCD, appName string) ([]ApplicationType, error) {
app, err := client.GetApplication(context.TODO(), appName)
if err != nil {
return ApplicationTypeUnsupported, err
retval := []ApplicationType{ApplicationTypeUnsupported}
return retval, err
}
return getApplicationType(app), nil
return getApplicationTypes(app), nil
}

// GetApplicationType returns the type of the ArgoCD application
func GetApplicationType(app *v1alpha1.Application) ApplicationType {
return getApplicationType(app)
// GetApplicationTypeForSource returns the type of the ArgoCD application source
func GetApplicationTypeForSource(app *v1alpha1.Application, sourceIndex int) ApplicationType {
return getApplicationTypes(app)[sourceIndex]
}

// IsValidApplicationType returns true if we can update the application
func IsValidApplicationType(app *v1alpha1.Application) bool {
return getApplicationType(app) != ApplicationTypeUnsupported
// IsValidApplicationTypeForSource returns true if we can update the application source
func IsValidApplicationTypeForSource(app *v1alpha1.Application, sourceIndex int) bool {
return getApplicationTypes(app)[sourceIndex] != ApplicationTypeUnsupported
}

// getApplicationType returns the type of the application
func getApplicationType(app *v1alpha1.Application) ApplicationType {
sourceType := app.Status.SourceType
// writebacktargetannotation with the kustomization prefix forces all sources to be handled as kustomization
func getApplicationTypes(app *v1alpha1.Application) []ApplicationType {
kustomizationWriteBack := false
retval := make([]ApplicationType, 0)

if st, set := app.Annotations[common.WriteBackTargetAnnotation]; set &&
strings.HasPrefix(st, common.KustomizationPrefix) {
sourceType = v1alpha1.ApplicationSourceTypeKustomize
kustomizationWriteBack = true
}

for _, sourceType := range GetSourceTypes(app.Status) {
if kustomizationWriteBack {
retval = append(retval, ApplicationTypeKustomize)
} else {
retval = append(retval, getApplicationTypeForSourceType(sourceType))
}
}

return retval
}

func getApplicationTypeForSourceType(sourceType v1alpha1.ApplicationSourceType) ApplicationType {
if sourceType == v1alpha1.ApplicationSourceTypeKustomize {
return ApplicationTypeKustomize
} else if sourceType == v1alpha1.ApplicationSourceTypeHelm {
Expand All @@ -552,3 +571,17 @@ func (a ApplicationType) String() string {
return "Unknown"
}
}

func HasMultipleSourceTypes(status v1alpha1.ApplicationStatus) bool {
return status.SourceTypes != nil && len(status.SourceTypes) > 0
}

func GetSourceTypes(status v1alpha1.ApplicationStatus) []v1alpha1.ApplicationSourceType {
if HasMultipleSourceTypes(status) {
return status.SourceTypes
}
if &status.SourceType != nil {
return []v1alpha1.ApplicationSourceType{status.SourceType}
}
return []v1alpha1.ApplicationSourceType{}
}
97 changes: 21 additions & 76 deletions pkg/argocd/argocd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func Test_GetImagesFromApplication(t *testing.T) {
})
}

func Test_GetApplicationType(t *testing.T) {
func Test_GetApplicationTypeForSource(t *testing.T) {
t.Run("Get application of type Helm", func(t *testing.T) {
application := &v1alpha1.Application{
ObjectMeta: v1.ObjectMeta{
Expand All @@ -94,7 +94,7 @@ func Test_GetApplicationType(t *testing.T) {
},
},
}
appType := GetApplicationType(application)
appType := GetApplicationTypeForSource(application, 0)
assert.Equal(t, ApplicationTypeHelm, appType)
assert.Equal(t, "Helm", appType.String())
})
Expand All @@ -113,7 +113,7 @@ func Test_GetApplicationType(t *testing.T) {
},
},
}
appType := GetApplicationType(application)
appType := GetApplicationTypeForSource(application, 0)
assert.Equal(t, ApplicationTypeKustomize, appType)
assert.Equal(t, "Kustomize", appType.String())
})
Expand All @@ -132,7 +132,7 @@ func Test_GetApplicationType(t *testing.T) {
},
},
}
appType := GetApplicationType(application)
appType := GetApplicationTypeForSource(application, 0)
assert.Equal(t, ApplicationTypeUnsupported, appType)
assert.Equal(t, "Unsupported", appType.String())
})
Expand All @@ -154,7 +154,7 @@ func Test_GetApplicationType(t *testing.T) {
},
},
}
appType := GetApplicationType(application)
appType := GetApplicationTypeForSource(application, 0)
assert.Equal(t, ApplicationTypeKustomize, appType)
})

Expand Down Expand Up @@ -466,8 +466,8 @@ func Test_SetKustomizeImage(t *testing.T) {
},
}
img := image.NewFromIdentifier("jannfis/foobar:1.0.1")
err := SetKustomizeImage(app, img)
require.NoError(t, err)
updated := SetKustomizeImageWithIndex(app, 0, img)
assert.True(t, updated)
require.NotNil(t, app.Spec.GetSources()[0].Kustomize)
assert.Len(t, app.Spec.GetSources()[0].Kustomize.Images, 1)
assert.Equal(t, v1alpha1.KustomizeImage("jannfis/foobar:1.0.1"), app.Spec.GetSources()[0].Kustomize.Images[0])
Expand All @@ -492,8 +492,8 @@ func Test_SetKustomizeImage(t *testing.T) {
},
}
img := image.NewFromIdentifier("jannfis/foobar:1.0.1")
err := SetKustomizeImage(app, img)
require.NoError(t, err)
updated := SetKustomizeImageWithIndex(app, 0, img)
assert.True(t, updated)
require.NotNil(t, app.Spec.GetSources()[0].Kustomize)
assert.Len(t, app.Spec.GetSources()[0].Kustomize.Images, 1)
assert.Equal(t, v1alpha1.KustomizeImage("jannfis/foobar:1.0.1"), app.Spec.GetSources()[0].Kustomize.Images[0])
Expand Down Expand Up @@ -524,8 +524,8 @@ func Test_SetKustomizeImage(t *testing.T) {
},
}
img := image.NewFromIdentifier("jannfis/foobar:1.0.1")
err := SetKustomizeImage(app, img)
require.Error(t, err)
updated := SetKustomizeImageWithIndex(app, 0, img)
assert.False(t, updated)
})

t.Run("Test set Kustomize image parameters with alias name on Kustomize app with param already set", func(t *testing.T) {
Expand Down Expand Up @@ -556,8 +556,8 @@ func Test_SetKustomizeImage(t *testing.T) {
},
}
img := image.NewFromIdentifier("foobar=jannfis/foobar:1.0.1")
err := SetKustomizeImage(app, img)
require.NoError(t, err)
updated := SetKustomizeImageWithIndex(app, 0, img)
assert.True(t, updated)
require.NotNil(t, app.Spec.GetSources()[0].Kustomize)
assert.Len(t, app.Spec.GetSources()[0].Kustomize.Images, 1)
assert.Equal(t, v1alpha1.KustomizeImage("foobar=jannfis/foobar:1.0.1"), app.Spec.GetSources()[0].Kustomize.Images[0])
Expand Down Expand Up @@ -604,8 +604,8 @@ func Test_SetHelmImage(t *testing.T) {

img := image.NewFromIdentifier("foobar=jannfis/foobar:1.0.1")

err := SetHelmImage(app, img)
require.NoError(t, err)
updated := SetHelmImageWithIndex(app, 0, img)
assert.True(t, updated)
require.NotNil(t, app.Spec.GetSources()[0].Helm)
assert.Len(t, app.Spec.GetSources()[0].Helm.Parameters, 2)

Expand Down Expand Up @@ -647,63 +647,8 @@ func Test_SetHelmImage(t *testing.T) {

img := image.NewFromIdentifier("foobar=jannfis/foobar:1.0.1")

err := SetHelmImage(app, img)
require.NoError(t, err)
require.NotNil(t, app.Spec.GetSources()[0].Helm)
assert.Len(t, app.Spec.GetSources()[0].Helm.Parameters, 2)

// Find correct parameter
var tagParam v1alpha1.HelmParameter
for _, p := range app.Spec.GetSources()[0].Helm.Parameters {
if p.Name == "image.tag" {
tagParam = p
break
}
}
assert.Equal(t, "1.0.1", tagParam.Value)
})

t.Run("Test set Helm image parameters on Helm app with multiple sources but existing parameters", func(t *testing.T) {
app := &v1alpha1.Application{
ObjectMeta: v1.ObjectMeta{
Name: "test-app",
Namespace: "testns",
Annotations: map[string]string{
fmt.Sprintf(common.HelmParamImageNameAnnotation, "foobar"): "image.name",
fmt.Sprintf(common.HelmParamImageTagAnnotation, "foobar"): "image.tag",
fmt.Sprintf(common.HelmParamImageNameAnnotation, "baz"): "image.name",
fmt.Sprintf(common.HelmParamImageTagAnnotation, "baz"): "image.tag",
},
},
Spec: v1alpha1.ApplicationSpec{
Sources: v1alpha1.ApplicationSources{
v1alpha1.ApplicationSource{
Helm: &v1alpha1.ApplicationSourceHelm{},
},
v1alpha1.ApplicationSource{
Helm: &v1alpha1.ApplicationSourceHelm{},
},
},
},
Status: v1alpha1.ApplicationStatus{
SourceTypes: []v1alpha1.ApplicationSourceType{
v1alpha1.ApplicationSourceTypeHelm,
v1alpha1.ApplicationSourceTypeHelm,
},
SourceType: v1alpha1.ApplicationSourceTypeHelm,
Summary: v1alpha1.ApplicationSummary{
Images: []string{
"jannfis/foobar:1.0.0",
"cjm/baz:2.0.0",
},
},
},
}

img := image.NewFromIdentifier("foobar=jannfis/foobar:1.0.1")

err := SetHelmImage(app, img)
require.NoError(t, err)
updated := SetHelmImageWithIndex(app, 0, img)
assert.True(t, updated)
require.NotNil(t, app.Spec.GetSources()[0].Helm)
assert.Len(t, app.Spec.GetSources()[0].Helm.Parameters, 2)

Expand Down Expand Up @@ -756,8 +701,8 @@ func Test_SetHelmImage(t *testing.T) {

img := image.NewFromIdentifier("foobar=jannfis/foobar:1.0.1")

err := SetHelmImage(app, img)
require.NoError(t, err)
updated := SetHelmImageWithIndex(app, 0, img)
assert.True(t, updated)
require.NotNil(t, app.Spec.GetSources()[0].Helm)
assert.Len(t, app.Spec.GetSources()[0].Helm.Parameters, 4)

Expand Down Expand Up @@ -797,8 +742,8 @@ func Test_SetHelmImage(t *testing.T) {

img := image.NewFromIdentifier("foobar=jannfis/foobar:1.0.1")

err := SetHelmImage(app, img)
require.Error(t, err)
updated := SetHelmImageWithIndex(app, 0, img)
assert.False(t, updated)
})

}
Expand Down
Loading

0 comments on commit dc7bf29

Please sign in to comment.