Files
argo-cd/test/e2e/applicationset_test.go
2026-01-12 16:35:49 +02:00

2252 lines
75 KiB
Go

package e2e
import (
"fmt"
"io"
"net"
"net/http"
"net/http/httptest"
"testing"
"github.com/google/uuid"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"github.com/argoproj/argo-cd/v3/common"
"github.com/argoproj/argo-cd/v3/pkg/apiclient/applicationset"
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/test/e2e/fixture"
utilio "github.com/argoproj/argo-cd/v3/util/io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
. "github.com/argoproj/argo-cd/v3/test/e2e/fixture/applicationsets"
"github.com/argoproj/argo-cd/v3/test/e2e/fixture/applicationsets/utils"
"github.com/argoproj/argo-cd/v3/pkg/apis/application"
)
var ExpectedConditions = []v1alpha1.ApplicationSetCondition{
{
Type: v1alpha1.ApplicationSetConditionErrorOccurred,
Status: v1alpha1.ApplicationSetConditionStatusFalse,
Message: "All applications have been generated successfully",
Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate,
},
{
Type: v1alpha1.ApplicationSetConditionParametersGenerated,
Status: v1alpha1.ApplicationSetConditionStatusTrue,
Message: "Successfully generated parameters for all Applications",
Reason: v1alpha1.ApplicationSetReasonParametersGenerated,
},
{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Status: v1alpha1.ApplicationSetConditionStatusTrue,
Message: "All applications have been generated successfully",
Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate,
},
}
func TestSimpleListGeneratorExternalNamespace(t *testing.T) {
externalNamespace := string(utils.ArgoCDExternalNamespace)
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: externalNamespace,
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
var expectedAppNewNamespace *v1alpha1.Application
var expectedAppNewMetadata *v1alpha1.Application
Given(t).
// Create a ListGenerator-based ApplicationSet
When().
SwitchToExternalNamespace(utils.ArgoCDExternalNamespace).
Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
// Update the ApplicationSet template namespace, and verify it updates the Applications
When().
And(func() {
expectedAppNewNamespace = expectedApp.DeepCopy()
expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2"
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Spec.Destination.Namespace = "guestbook2"
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})).
// Update the metadata fields in the appset template, and make sure it propagates to the apps
When().
And(func() {
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
"label-key": "label-value",
}
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
appset.Spec.Template.Labels = map[string]string{
"label-key": "label-value",
}
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})).
// verify the ApplicationSet status conditions were set correctly
Expect(ApplicationSetHasConditions(ExpectedConditions)).
// Delete the ApplicationSet, and verify it deletes the Applications
When().
Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewMetadata}))
}
func TestSimpleListGeneratorExternalNamespaceNoConflict(t *testing.T) {
externalNamespace := string(utils.ArgoCDExternalNamespace)
externalNamespace2 := string(utils.ArgoCDExternalNamespace2)
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: externalNamespace,
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
expectedAppExternalNamespace2 := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: externalNamespace2,
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
var expectedAppNewNamespace *v1alpha1.Application
var expectedAppNewMetadata *v1alpha1.Application
Given(t).
// Create a ListGenerator-based ApplicationSet
When().
SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2).
Create(v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Namespace: externalNamespace2,
},
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedAppExternalNamespace2})).
When().
SwitchToExternalNamespace(utils.ArgoCDExternalNamespace).
Create(v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Namespace: externalNamespace,
},
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
When().
SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2).
Then().
Expect(ApplicationsExist([]v1alpha1.Application{expectedAppExternalNamespace2})).
When().
SwitchToExternalNamespace(utils.ArgoCDExternalNamespace).
Then().
// Update the ApplicationSet template namespace, and verify it updates the Applications
When().
And(func() {
expectedAppNewNamespace = expectedApp.DeepCopy()
expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2"
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Spec.Destination.Namespace = "guestbook2"
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})).
When().
SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2).
Then().
Expect(ApplicationsExist([]v1alpha1.Application{expectedAppExternalNamespace2})).
When().
SwitchToExternalNamespace(utils.ArgoCDExternalNamespace).
Then().
// Update the metadata fields in the appset template, and make sure it propagates to the apps
When().
And(func() {
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
"label-key": "label-value",
}
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
appset.Spec.Template.Labels = map[string]string{
"label-key": "label-value",
}
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})).
// verify the ApplicationSet status conditions were set correctly
Expect(ApplicationSetHasConditions(ExpectedConditions)).
When().
SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2).
Then().
Expect(ApplicationsExist([]v1alpha1.Application{expectedAppExternalNamespace2})).
When().
SwitchToExternalNamespace(utils.ArgoCDExternalNamespace).
Then().
// Delete the ApplicationSet, and verify it deletes the Applications
When().
Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewMetadata})).
When().
SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2).
Then().
Expect(ApplicationsExist([]v1alpha1.Application{expectedAppExternalNamespace2})).
When().
Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedAppExternalNamespace2}))
}
func TestSimpleListGenerator(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
var expectedAppNewNamespace *v1alpha1.Application
var expectedAppNewMetadata *v1alpha1.Application
Given(t).
// Create a ListGenerator-based ApplicationSet
When().
Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{cluster}}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
// Update the ApplicationSet template namespace, and verify it updates the Applications
When().
And(func() {
expectedAppNewNamespace = expectedApp.DeepCopy()
expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2"
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Spec.Destination.Namespace = "guestbook2"
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})).
// Update the metadata fields in the appset template, and make sure it propagates to the apps
When().
And(func() {
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"}
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})).
// verify the ApplicationSet status conditions were set correctly
Expect(ApplicationSetHasConditions(ExpectedConditions)).
// Delete the ApplicationSet, and verify it deletes the Applications
When().
Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewMetadata}))
}
func TestSimpleListGeneratorGoTemplate(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
var expectedAppNewNamespace *v1alpha1.Application
var expectedAppNewMetadata *v1alpha1.Application
Given(t).
// Create a ListGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
// Update the ApplicationSet template namespace, and verify it updates the Applications
When().
And(func() {
expectedAppNewNamespace = expectedApp.DeepCopy()
expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2"
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Spec.Destination.Namespace = "guestbook2"
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})).
// Update the metadata fields in the appset template, and make sure it propagates to the apps
When().
And(func() {
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"}
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})).
// verify the ApplicationSet status conditions were set correctly
Expect(ApplicationSetHasConditions(ExpectedConditions)).
// Delete the ApplicationSet, and verify it deletes the Applications
When().
Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewMetadata}))
}
func TestRenderHelmValuesObject(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "helm-guestbook",
Helm: &v1alpha1.ApplicationSourceHelm{
ValuesObject: &runtime.RawExtension{
// This will always be converted as yaml
Raw: []byte(`{"some":{"string":"Hello world"}}`),
},
},
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
Given(t).
// Create a ListGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "helm-guestbook",
Helm: &v1alpha1.ApplicationSourceHelm{
ValuesObject: &runtime.RawExtension{
Raw: []byte(`{"some":{"string":"{{.test}}"}}`),
},
},
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "test": "Hello world"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
// Delete the ApplicationSet, and verify it deletes the Applications
When().
Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp}))
}
func TestTemplatePatch(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
Annotations: map[string]string{
"annotation-some-key": "annotation-some-value",
},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
SyncPolicy: &v1alpha1.SyncPolicy{
SyncOptions: v1alpha1.SyncOptions{"CreateNamespace=true"},
},
},
}
templatePatch := `{
"metadata": {
"annotations": {
{{- range $k, $v := .annotations }}
"{{ $k }}": "{{ $v }}"
{{- end }}
}
},
{{- if .createNamespace }}
"spec": {
"syncPolicy": {
"syncOptions": [
"CreateNamespace=true"
]
}
}
{{- end }}
}
`
var expectedAppNewNamespace *v1alpha1.Application
var expectedAppNewMetadata *v1alpha1.Application
Given(t).
// Create a ListGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
TemplatePatch: &templatePatch,
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{
"cluster": "my-cluster",
"url": "https://kubernetes.default.svc",
"createNamespace": true,
"annotations": {
"annotation-some-key": "annotation-some-value"
}
}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
// Update the ApplicationSet template namespace, and verify it updates the Applications
When().
And(func() {
expectedAppNewNamespace = expectedApp.DeepCopy()
expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2"
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Spec.Destination.Namespace = "guestbook2"
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})).
// Update the metadata fields in the appset template, and make sure it propagates to the apps
When().
And(func() {
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
"label-key": "label-value",
}
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"}
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})).
// verify the ApplicationSet status conditions were set correctly
Expect(ApplicationSetHasConditions(ExpectedConditions)).
// Delete the ApplicationSet, and verify it deletes the Applications
When().
Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewMetadata}))
}
func TestUpdateHelmValuesObject(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "helm-guestbook",
Helm: &v1alpha1.ApplicationSourceHelm{
ValuesObject: &runtime.RawExtension{
// This will always be converted as yaml
Raw: []byte(`{"some":{"foo":"bar"}}`),
},
},
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
Given(t).
// Create a ListGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "helm-guestbook",
Helm: &v1alpha1.ApplicationSourceHelm{
ValuesObject: &runtime.RawExtension{
Raw: []byte(`{"some":{"string":"{{.test}}"}}`),
},
},
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "test": "Hello world"}`),
}},
},
},
},
},
}).Then().
Expect(ApplicationSetHasConditions(ExpectedConditions)).
When().
// Update the app spec with some knew ValuesObject to force a merge
Update(func(as *v1alpha1.ApplicationSet) {
as.Spec.Template.Spec.Source.Helm.ValuesObject = &runtime.RawExtension{
Raw: []byte(`{"some":{"foo":"bar"}}`),
}
}).
Then().
Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
When().
// Delete the ApplicationSet, and verify it deletes the Applications
Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp}))
}
func TestSyncPolicyCreateUpdate(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook-sync-policy-create-update",
Namespace: utils.ArgoCDNamespace,
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
var expectedAppNewNamespace *v1alpha1.Application
var expectedAppNewMetadata *v1alpha1.Application
Given(t).
// Create a ListGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
Name: "{{.cluster}}-guestbook-sync-policy-create-update",
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
// Update the ApplicationSet template namespace, and verify it updates the Applications
When().
And(func() {
expectedAppNewNamespace = expectedApp.DeepCopy()
expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2"
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Spec.Destination.Namespace = "guestbook2"
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})).
// Update the metadata fields in the appset template
// Update as well the policy
// As policy is create-update, updates must reflected
When().
And(func() {
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
"label-key": "label-value",
}
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
appset.Spec.Template.Labels = map[string]string{
"label-key": "label-value",
}
applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateUpdate
appset.Spec.SyncPolicy = &v1alpha1.ApplicationSetSyncPolicy{
ApplicationsSync: &applicationsSyncPolicy,
}
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})).
// Update the list and remove element
// As policy is create-update, app deletion must not be reflected
When().
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Generators = []v1alpha1.ApplicationSetGenerator{}
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})).
// verify the ApplicationSet status conditions were set correctly
Expect(ApplicationSetHasConditions(ExpectedConditions)).
// Delete the ApplicationSet, and verify it not deletes the Applications
// As policy is create-update, AppSet controller will remove all generated applications's ownerReferences on delete AppSet
// So AppSet deletion will be reflected, but all the applications it generates will still exist
When().
Delete().Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata}))
}
func TestSyncPolicyCreateDelete(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook-sync-policy-create-delete",
Namespace: utils.ArgoCDNamespace,
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
var expectedAppNewNamespace *v1alpha1.Application
Given(t).
// Create a ListGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook-sync-policy-create-delete"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
// Update the ApplicationSet template namespace, and verify it updates the Applications
When().
And(func() {
expectedAppNewNamespace = expectedApp.DeepCopy()
expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2"
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Spec.Destination.Namespace = "guestbook2"
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})).
// Update the metadata fields in the appset template
// Update as well the policy
// As policy is create-delete, updates must not be reflected
When().
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"}
applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateDelete
appset.Spec.SyncPolicy = &v1alpha1.ApplicationSetSyncPolicy{
ApplicationsSync: &applicationsSyncPolicy,
}
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})).
// Update the list and remove element
// As policy is create-delete, app deletion must be reflected
When().
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Generators = []v1alpha1.ApplicationSetGenerator{}
}).Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewNamespace})).
// verify the ApplicationSet status conditions were set correctly
Expect(ApplicationSetHasConditions(ExpectedConditions)).
// Delete the ApplicationSet
When().
Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewNamespace}))
}
func TestSyncPolicyCreateOnly(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook-sync-policy-create-only",
Namespace: utils.ArgoCDNamespace,
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
var expectedAppNewNamespace *v1alpha1.Application
Given(t).
// Create a ListGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
Name: "{{.cluster}}-guestbook-sync-policy-create-only",
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
// Update the ApplicationSet template namespace, and verify it updates the Applications
When().
And(func() {
expectedAppNewNamespace = expectedApp.DeepCopy()
expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2"
}).
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Spec.Destination.Namespace = "guestbook2"
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})).
// Update the metadata fields in the appset template
// Update as well the policy
// As policy is create-only, updates must not be reflected
When().
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"}
applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateOnly
appset.Spec.SyncPolicy = &v1alpha1.ApplicationSetSyncPolicy{
ApplicationsSync: &applicationsSyncPolicy,
}
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})).
// Update the list and remove element
// As policy is create-only, app deletion must not be reflected
When().
Update(func(appset *v1alpha1.ApplicationSet) {
appset.Spec.Generators = []v1alpha1.ApplicationSetGenerator{}
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})).
// verify the ApplicationSet status conditions were set correctly
Expect(ApplicationSetHasConditions(ExpectedConditions)).
// Delete the ApplicationSet, and verify it not deletes the Applications
// As policy is create-update, AppSet controller will remove all generated applications's ownerReferences on delete AppSet
// So AppSet deletion will be reflected, but all the applications it generates will still exist
When().
Delete().Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace}))
}
func githubSCMMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
t.Helper()
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.RequestURI {
case "/api/v3/orgs/argoproj/repos?per_page=100":
_, err := io.WriteString(w, `[
{
"id": 1296269,
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
"name": "argo-cd",
"full_name": "argoproj/argo-cd",
"owner": {
"login": "argoproj",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/argoproj_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/argoproj",
"html_url": "https://github.com/argoproj",
"followers_url": "https://api.github.com/users/argoproj/followers",
"following_url": "https://api.github.com/users/argoproj/following{/other_user}",
"gists_url": "https://api.github.com/users/argoproj/gists{/gist_id}",
"starred_url": "https://api.github.com/users/argoproj/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/argoproj/subscriptions",
"organizations_url": "https://api.github.com/users/argoproj/orgs",
"repos_url": "https://api.github.com/users/argoproj/repos",
"events_url": "https://api.github.com/users/argoproj/events{/privacy}",
"received_events_url": "https://api.github.com/users/argoproj/received_events",
"type": "User",
"site_admin": false
},
"private": false,
"html_url": "https://github.com/argoproj/argo-cd",
"description": "This your first repo!",
"fork": false,
"url": "https://api.github.com/repos/argoproj/argo-cd",
"archive_url": "https://api.github.com/repos/argoproj/argo-cd/{archive_format}{/ref}",
"assignees_url": "https://api.github.com/repos/argoproj/argo-cd/assignees{/user}",
"blobs_url": "https://api.github.com/repos/argoproj/argo-cd/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/argoproj/argo-cd/branches{/branch}",
"collaborators_url": "https://api.github.com/repos/argoproj/argo-cd/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/argoproj/argo-cd/comments{/number}",
"commits_url": "https://api.github.com/repos/argoproj/argo-cd/commits{/sha}",
"compare_url": "https://api.github.com/repos/argoproj/argo-cd/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/argoproj/argo-cd/contents/{path}",
"contributors_url": "https://api.github.com/repos/argoproj/argo-cd/contributors",
"deployments_url": "https://api.github.com/repos/argoproj/argo-cd/deployments",
"downloads_url": "https://api.github.com/repos/argoproj/argo-cd/downloads",
"events_url": "https://api.github.com/repos/argoproj/argo-cd/events",
"forks_url": "https://api.github.com/repos/argoproj/argo-cd/forks",
"git_commits_url": "https://api.github.com/repos/argoproj/argo-cd/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/argoproj/argo-cd/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/argoproj/argo-cd/git/tags{/sha}",
"git_url": "git:github.com/argoproj/argo-cd.git",
"issue_comment_url": "https://api.github.com/repos/argoproj/argo-cd/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/argoproj/argo-cd/issues/events{/number}",
"issues_url": "https://api.github.com/repos/argoproj/argo-cd/issues{/number}",
"keys_url": "https://api.github.com/repos/argoproj/argo-cd/keys{/key_id}",
"labels_url": "https://api.github.com/repos/argoproj/argo-cd/labels{/name}",
"languages_url": "https://api.github.com/repos/argoproj/argo-cd/languages",
"merges_url": "https://api.github.com/repos/argoproj/argo-cd/merges",
"milestones_url": "https://api.github.com/repos/argoproj/argo-cd/milestones{/number}",
"notifications_url": "https://api.github.com/repos/argoproj/argo-cd/notifications{?since,all,participating}",
"pulls_url": "https://api.github.com/repos/argoproj/argo-cd/pulls{/number}",
"releases_url": "https://api.github.com/repos/argoproj/argo-cd/releases{/id}",
"ssh_url": "git@github.com:argoproj/argo-cd.git",
"stargazers_url": "https://api.github.com/repos/argoproj/argo-cd/stargazers",
"statuses_url": "https://api.github.com/repos/argoproj/argo-cd/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/argoproj/argo-cd/subscribers",
"subscription_url": "https://api.github.com/repos/argoproj/argo-cd/subscription",
"tags_url": "https://api.github.com/repos/argoproj/argo-cd/tags",
"teams_url": "https://api.github.com/repos/argoproj/argo-cd/teams",
"trees_url": "https://api.github.com/repos/argoproj/argo-cd/git/trees{/sha}",
"clone_url": "https://github.com/argoproj/argo-cd.git",
"mirror_url": "git:git.example.com/argoproj/argo-cd",
"hooks_url": "https://api.github.com/repos/argoproj/argo-cd/hooks",
"svn_url": "https://svn.github.com/argoproj/argo-cd",
"homepage": "https://github.com",
"language": null,
"forks_count": 9,
"stargazers_count": 80,
"watchers_count": 80,
"size": 108,
"default_branch": "master",
"open_issues_count": 0,
"is_template": false,
"topics": [
"argoproj",
"atom",
"electron",
"api"
],
"has_issues": true,
"has_projects": true,
"has_wiki": true,
"has_pages": false,
"has_downloads": true,
"archived": false,
"disabled": false,
"visibility": "public",
"pushed_at": "2011-01-26T19:06:43Z",
"created_at": "2011-01-26T19:01:12Z",
"updated_at": "2011-01-26T19:14:43Z",
"permissions": {
"admin": false,
"push": false,
"pull": true
},
"template_repository": null
}
]`)
if err != nil {
t.Fail()
}
case "/api/v3/repos/argoproj/argo-cd/branches?per_page=100":
_, err := io.WriteString(w, `[
{
"name": "master",
"commit": {
"sha": "c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc",
"url": "https://api.github.com/repos/argoproj/argo-cd/commits/c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc"
},
"protected": true,
"protection": {
"required_status_checks": {
"enforcement_level": "non_admins",
"contexts": [
"ci-test",
"linter"
]
}
},
"protection_url": "https://api.github.com/repos/argoproj/hello-world/branches/master/protection"
}
]
`)
if err != nil {
t.Fail()
}
case "/api/v3/repos/argoproj/argo-cd/contents/pkg?ref=master":
_, err := io.WriteString(w, `{
"type": "file",
"encoding": "base64",
"size": 5362,
"name": "pkg/",
"path": "pkg/",
"content": "encoded content ...",
"sha": "3d21ec53a331a6f037a91c368710b99387d012c1",
"url": "https://api.github.com/repos/octokit/octokit.rb/contents/README.md",
"git_url": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
"html_url": "https://github.com/octokit/octokit.rb/blob/master/README.md",
"download_url": "https://raw.githubusercontent.com/octokit/octokit.rb/master/README.md",
"_links": {
"git": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
"self": "https://api.github.com/repos/octokit/octokit.rb/contents/README.md",
"html": "https://github.com/octokit/octokit.rb/blob/master/README.md"
}
}`)
if err != nil {
t.Fail()
}
case "/api/v3/repos/argoproj/argo-cd/branches/master":
_, err := io.WriteString(w, `{
"name": "master",
"commit": {
"sha": "c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc",
"url": "https://api.github.com/repos/octocat/Hello-World/commits/c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc"
},
"protected": true,
"protection": {
"required_status_checks": {
"enforcement_level": "non_admins",
"contexts": [
"ci-test",
"linter"
]
}
},
"protection_url": "https://api.github.com/repos/octocat/hello-world/branches/master/protection"
}`)
if err != nil {
t.Fail()
}
default:
w.WriteHeader(http.StatusNotFound)
}
}
}
func testServerWithPort(t *testing.T, port int, handler http.Handler) *httptest.Server {
t.Helper()
// Use mocked API response to avoid rate-limiting.
lc := net.ListenConfig{}
l, err := lc.Listen(t.Context(), "tcp", fmt.Sprintf("127.0.0.1:%d", port))
require.NoError(t, err, "Unable to start server")
ts := httptest.NewUnstartedServer(handler)
ts.Listener.Close()
ts.Listener = l
return ts
}
func TestSimpleSCMProviderGenerator(t *testing.T) {
ts := testServerWithPort(t, 8341, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
githubSCMMockHandler(t)(w, r)
}))
ts.Start()
defer ts.Close()
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "argo-cd-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "git@github.com:argoproj/argo-cd.git",
TargetRevision: "master",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
// Because you can't &"".
repoMatch := "argo-cd"
Given(t).
// Create an SCMProviderGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ repository }}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "{{ url }}",
TargetRevision: "{{ branch }}",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
SCMProvider: &v1alpha1.SCMProviderGenerator{
Github: &v1alpha1.SCMProviderGeneratorGithub{
Organization: "argoproj",
API: ts.URL,
},
Filters: []v1alpha1.SCMProviderGeneratorFilter{
{
RepositoryMatch: &repoMatch,
},
},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp}))
}
func TestSimpleSCMProviderGeneratorGoTemplate(t *testing.T) {
ts := testServerWithPort(t, 8342, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
githubSCMMockHandler(t)(w, r)
}))
ts.Start()
defer ts.Close()
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "argo-cd-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "git@github.com:argoproj/argo-cd.git",
TargetRevision: "master",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
// Because you can't &"".
repoMatch := "argo-cd"
Given(t).
// Create an SCMProviderGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ .repository }}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "{{ .url }}",
TargetRevision: "{{ .branch }}",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
SCMProvider: &v1alpha1.SCMProviderGenerator{
Github: &v1alpha1.SCMProviderGeneratorGithub{
Organization: "argoproj",
API: ts.URL,
},
Filters: []v1alpha1.SCMProviderGeneratorFilter{
{
RepositoryMatch: &repoMatch,
},
},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp}))
}
func TestSCMProviderGeneratorSCMProviderNotAllowed(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "argo-cd-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "git@github.com:argoproj/argo-cd.git",
TargetRevision: "master",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
// Because you can't &"".
repoMatch := "argo-cd"
ctx := Given(t)
// Create an SCMProviderGenerator-based ApplicationSet
ctx.When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ .repository }}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "{{ .url }}",
TargetRevision: "{{ .branch }}",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
SCMProvider: &v1alpha1.SCMProviderGenerator{
Github: &v1alpha1.SCMProviderGeneratorGithub{
Organization: "argoproj",
API: "http://myservice.mynamespace.svc.cluster.local",
},
Filters: []v1alpha1.SCMProviderGeneratorFilter{
{
RepositoryMatch: &repoMatch,
},
},
},
},
},
},
}).Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp})).
And(func() {
// app should be listed
output, err := fixture.RunCli("appset", "get", ctx.GetName())
require.NoError(t, err)
assert.Contains(t, output, "scm provider not allowed")
})
}
func TestCustomApplicationFinalizers(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.BackgroundPropagationPolicyFinalizer},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
Given(t).
// Create a ListGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
Name: "{{cluster}}-guestbook",
Finalizers: []string{v1alpha1.BackgroundPropagationPolicyFinalizer},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
// Delete the ApplicationSet, and verify it deletes the Applications
When().
Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp}))
}
func TestCustomApplicationFinalizersGoTemplate(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.BackgroundPropagationPolicyFinalizer},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
Given(t).
// Create a ListGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
Name: "{{.cluster}}-guestbook",
Finalizers: []string{v1alpha1.BackgroundPropagationPolicyFinalizer},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
// Delete the ApplicationSet, and verify it deletes the Applications
When().
Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp}))
}
func githubPullMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
t.Helper()
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.RequestURI {
case "/api/v3/repos/applicationset-test-org/argocd-example-apps/pulls?per_page=100":
_, err := io.WriteString(w, `[
{
"number": 1,
"title": "title1",
"labels": [
{
"name": "preview"
}
],
"base": {
"ref": "master",
"sha": "7a4a5c987fdfb2b0629e9dbf5f31636c69ba4775"
},
"head": {
"ref": "pull-request",
"sha": "824a5c987fdfb2b0629e9dbf5f31636c69ba4772"
},
"user": {
"login": "testName"
}
}
]`)
if err != nil {
t.Fail()
}
default:
w.WriteHeader(http.StatusNotFound)
}
}
}
func TestSimpleSCMProviderGeneratorTokenRefStrictOk(t *testing.T) {
secretName := uuid.New().String()
ts := testServerWithPort(t, 8341, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
githubSCMMockHandler(t)(w, r)
}))
ts.Start()
defer ts.Close()
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "argo-cd-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "git@github.com:argoproj/argo-cd.git",
TargetRevision: "master",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
// Because you can't &"".
repoMatch := "argo-cd"
Given(t).
And(func() {
_, err := utils.GetE2EFixtureK8sClient(t).KubeClientset.CoreV1().Secrets(fixture.TestNamespace()).Create(t.Context(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: fixture.TestNamespace(),
Name: secretName,
Labels: map[string]string{
common.LabelKeySecretType: common.LabelValueSecretTypeSCMCreds,
},
},
Data: map[string][]byte{
"hello": []byte("world"),
},
}, metav1.CreateOptions{})
assert.NoError(t, err)
}).
// Create an SCMProviderGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ repository }}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "{{ url }}",
TargetRevision: "{{ branch }}",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
SCMProvider: &v1alpha1.SCMProviderGenerator{
Github: &v1alpha1.SCMProviderGeneratorGithub{
Organization: "argoproj",
API: ts.URL,
TokenRef: &v1alpha1.SecretRef{
SecretName: secretName,
Key: "hello",
},
},
Filters: []v1alpha1.SCMProviderGeneratorFilter{
{
RepositoryMatch: &repoMatch,
},
},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})).
When().And(func() {
err := utils.GetE2EFixtureK8sClient(t).KubeClientset.CoreV1().Secrets(fixture.TestNamespace()).Delete(t.Context(), secretName, metav1.DeleteOptions{})
assert.NoError(t, err)
})
}
func TestSimpleSCMProviderGeneratorTokenRefStrictKo(t *testing.T) {
secretName := uuid.New().String()
ts := testServerWithPort(t, 8341, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
githubSCMMockHandler(t)(w, r)
}))
ts.Start()
defer ts.Close()
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "argo-cd-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
Labels: map[string]string{
common.LabelKeyAppInstance: "simple-scm-provider-generator-strict-ko",
},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "git@github.com:argoproj/argo-cd.git",
TargetRevision: "master",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
// Because you can't &"".
repoMatch := "argo-cd"
ctx := Given(t)
ctx.And(func() {
_, err := utils.GetE2EFixtureK8sClient(t).KubeClientset.CoreV1().Secrets(fixture.TestNamespace()).Create(t.Context(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: fixture.TestNamespace(),
Name: secretName,
Labels: map[string]string{
// Try to exfiltrate cluster secret
common.LabelKeySecretType: common.LabelValueSecretTypeCluster,
},
},
Data: map[string][]byte{
"hello": []byte("world"),
},
}, metav1.CreateOptions{})
assert.NoError(t, err)
}).
// Create an SCMProviderGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ repository }}-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "{{ url }}",
TargetRevision: "{{ branch }}",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
SCMProvider: &v1alpha1.SCMProviderGenerator{
Github: &v1alpha1.SCMProviderGeneratorGithub{
Organization: "argoproj",
API: ts.URL,
TokenRef: &v1alpha1.SecretRef{
SecretName: secretName,
Key: "hello",
},
},
Filters: []v1alpha1.SCMProviderGeneratorFilter{
{
RepositoryMatch: &repoMatch,
},
},
},
},
},
},
}).Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp})).
When().
And(func() {
// app should be listed
output, err := fixture.RunCli("appset", "get", ctx.GetName())
require.NoError(t, err)
assert.Contains(t, output, fmt.Sprintf("scm provider: error fetching Github token: secret %s/%s is not a valid SCM creds secret", fixture.TestNamespace(), secretName))
err2 := utils.GetE2EFixtureK8sClient(t).KubeClientset.CoreV1().Secrets(fixture.TestNamespace()).Delete(t.Context(), secretName, metav1.DeleteOptions{})
assert.NoError(t, err2)
})
}
func TestSimplePullRequestGenerator(t *testing.T) {
ts := testServerWithPort(t, 8343, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
githubPullMockHandler(t)(w, r)
}))
ts.Start()
defer ts.Close()
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "guestbook-1",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git",
TargetRevision: "824a5c987fdfb2b0629e9dbf5f31636c69ba4772",
Path: "kustomize-guestbook",
Kustomize: &v1alpha1.ApplicationSourceKustomize{
NamePrefix: "guestbook-1",
},
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook-pull-request",
},
},
}
Given(t).
// Create an PullRequestGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "guestbook-{{ number }}"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git",
TargetRevision: "{{ head_sha }}",
Path: "kustomize-guestbook",
Kustomize: &v1alpha1.ApplicationSourceKustomize{
NamePrefix: "guestbook-{{ number }}",
},
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook-{{ branch }}",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
PullRequest: &v1alpha1.PullRequestGenerator{
Github: &v1alpha1.PullRequestGeneratorGithub{
API: ts.URL,
Owner: "applicationset-test-org",
Repo: "argocd-example-apps",
Labels: []string{
"preview",
},
},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp}))
}
func TestSimplePullRequestGeneratorGoTemplate(t *testing.T) {
ts := testServerWithPort(t, 8344, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
githubPullMockHandler(t)(w, r)
}))
ts.Start()
defer ts.Close()
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "guestbook-1",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
Labels: map[string]string{"app": "preview"},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git",
TargetRevision: "824a5c987fdfb2b0629e9dbf5f31636c69ba4772",
Path: "kustomize-guestbook",
Kustomize: &v1alpha1.ApplicationSourceKustomize{
NamePrefix: "guestbook-1",
},
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook-pull-request",
},
},
}
Given(t).
// Create an PullRequestGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
Name: "guestbook-{{ .number }}",
Labels: map[string]string{"app": "{{index .labels 0}}"},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git",
TargetRevision: "{{ .head_sha }}",
Path: "kustomize-guestbook",
Kustomize: &v1alpha1.ApplicationSourceKustomize{
NamePrefix: "guestbook-{{ .number }}",
},
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook-{{ .branch }}",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
PullRequest: &v1alpha1.PullRequestGenerator{
Github: &v1alpha1.PullRequestGeneratorGithub{
API: ts.URL,
Owner: "applicationset-test-org",
Repo: "argocd-example-apps",
Labels: []string{
"preview",
},
},
},
},
},
},
}).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp}))
}
func TestPullRequestGeneratorNotAllowedSCMProvider(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "guestbook-1",
Namespace: fixture.TestNamespace(),
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
Labels: map[string]string{
"app": "preview",
},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git",
TargetRevision: "824a5c987fdfb2b0629e9dbf5f31636c69ba4772",
Path: "kustomize-guestbook",
Kustomize: &v1alpha1.ApplicationSourceKustomize{
NamePrefix: "guestbook-1",
},
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook-pull-request",
},
},
}
ctx := Given(t)
// Create an PullRequestGenerator-based ApplicationSet
ctx.When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
Name: "guestbook-{{ .number }}",
Labels: map[string]string{"app": "{{index .labels 0}}"},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git",
TargetRevision: "{{ .head_sha }}",
Path: "kustomize-guestbook",
Kustomize: &v1alpha1.ApplicationSourceKustomize{
NamePrefix: "guestbook-{{ .number }}",
},
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook-{{ .branch }}",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
PullRequest: &v1alpha1.PullRequestGenerator{
Github: &v1alpha1.PullRequestGeneratorGithub{
API: "http://myservice.mynamespace.svc.cluster.local",
Owner: "applicationset-test-org",
Repo: "argocd-example-apps",
Labels: []string{
"preview",
},
},
},
},
},
},
}).Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp})).
And(func() {
// app should be listed
output, err := fixture.RunCli("appset", "get", ctx.GetName())
require.NoError(t, err)
assert.Contains(t, output, "scm provider not allowed")
})
}
// TestApplicationSetAPIListResourceEvents tests the ApplicationSet ListResourceEvents API
func TestApplicationSetAPIListResourceEvents(t *testing.T) {
ctx := Given(t)
ctx.When().Create(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-api-events-test"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().
And(func() {
// Test the ListResourceEvents API
closer, appSetClient := fixture.ArgoCDClientset.NewApplicationSetClientOrDie()
defer utilio.Close(closer)
events, err := appSetClient.ListResourceEvents(t.Context(), &applicationset.ApplicationSetGetQuery{
Name: ctx.GetName(),
})
require.NoError(t, err)
// Events list should be returned (may be empty if no events have been generated yet)
assert.NotNil(t, events)
}).
When().Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{}))
}
// TestApplicationSetHealthStatusCLI tests that the CLI commands display the health status field for an ApplicationSet.
func TestApplicationSetHealthStatusCLI(t *testing.T) {
expectedApp := v1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "health-cli-guestbook",
Namespace: utils.ArgoCDNamespace,
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
Given(t).
When().Create(v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "health-status-cli-test",
Namespace: utils.ArgoCDNamespace,
},
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "health-cli-guestbook"},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster"}`),
}},
},
},
},
},
}).Then().
// Wait for the ApplicationSet to be ready
Expect(ApplicationSetHasConditions("health-status-cli-test", ExpectedConditions)).
// Test 'argocd appset get' shows Health Status field
When().AppSetGet("health-status-cli-test").
Then().Expect(OutputContains("Health Status:")).
// Test 'argocd appset list' shows HEALTH column header
When().AppSetList().
Then().Expect(OutputContains("HEALTH")).
// Cleanup
When().Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp}))
}