diff --git a/utils/record/record.go b/utils/record/record.go index f255b02c24..c41dbd34c7 100644 --- a/utils/record/record.go +++ b/utils/record/record.go @@ -232,7 +232,7 @@ func NewAPIFactorySettings() api.Settings { ConfigMapName: NotificationConfigMap, InitGetVars: func(cfg *api.Config, configMap *corev1.ConfigMap, secret *corev1.Secret) (api.GetVars, error) { return func(obj map[string]interface{}, dest services.Destination) map[string]interface{} { - return map[string]interface{}{"rollout": obj} + return map[string]interface{}{"rollout": obj, "time": timeExprs} }, nil }, } @@ -368,3 +368,20 @@ func translateReasonToTrigger(reason string) string { trigger = matchAllCap.ReplaceAllString(trigger, "${1}-${2}") return "on-" + strings.ToLower(trigger) } + +var timeExprs = map[string]interface{}{ + "Parse": parse, + "Now": now, +} + +func parse(timestamp string) time.Time { + res, err := time.Parse(time.RFC3339, timestamp) + if err != nil { + panic(err) + } + return res +} + +func now() time.Time { + return time.Now() +} diff --git a/utils/record/record_test.go b/utils/record/record_test.go index c99f55985b..fb8794bcf5 100644 --- a/utils/record/record_test.go +++ b/utils/record/record_test.go @@ -6,8 +6,12 @@ import ( "fmt" "strings" "testing" + "time" + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + "github.com/argoproj/argo-rollouts/utils/defaults" "github.com/argoproj/notifications-engine/pkg/api" + notificationapi "github.com/argoproj/notifications-engine/pkg/api" "github.com/argoproj/notifications-engine/pkg/mocks" "github.com/argoproj/notifications-engine/pkg/services" "github.com/argoproj/notifications-engine/pkg/triggers" @@ -20,8 +24,8 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - - "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" ) func TestRecordLog(t *testing.T) { @@ -139,6 +143,101 @@ func TestSendNotificationsWhenCondition(t *testing.T) { assert.NoError(t, err) } +func TestSendNotificationsWhenConditionTime(t *testing.T) { + tNow := metav1.NewTime(time.Now().Add(-time.Minute * 5)) + r := v1alpha1.Rollout{ + ObjectMeta: metav1.ObjectMeta{ + Name: "guestbook", + Namespace: "default", + Annotations: map[string]string{"notifications.argoproj.io/subscribe.on-foo-reason.console": "console"}, + }, + Spec: v1alpha1.RolloutSpec{ + RestartAt: &tNow, + }, + } + + t.Run("Test when condition is true", func(t *testing.T) { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argo-rollouts-notification-secret", + Namespace: "argo-rollouts", + }, + Data: nil, + } + + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argo-rollouts-notification-configmap", + Namespace: "argo-rollouts", + }, + Data: map[string]string{ + "trigger.on-foo-reason": "- send: [on-foo-reason]\n when: \"time.Now().Sub(time.Parse(rollout.spec.restartAt)).Minutes() > 4\"\n", + "template.on-foo-reason": "message: Rollout {{.rollout.metadata.name}}'s time check", + }, + } + + k8sClient := fake.NewSimpleClientset() + sharedInformers := informers.NewSharedInformerFactory(k8sClient, 0) + cmInformer := sharedInformers.Core().V1().ConfigMaps().Informer() + secretInformer := sharedInformers.Core().V1().Secrets().Informer() + + secretInformer.GetIndexer().Add(secret) + cmInformer.GetIndexer().Add(cm) + + apiFactory := notificationapi.NewFactory(NewAPIFactorySettings(), defaults.Namespace(), secretInformer, cmInformer) + api, err := apiFactory.GetAPI() + assert.NoError(t, err) + + objMap, err := toObjectMap(r) + assert.NoError(t, err) + + cr, err := api.RunTrigger("on-foo-reason", objMap) + assert.NoError(t, err) + assert.Equal(t, 1, len(cr)) + assert.True(t, cr[0].Triggered) + }) + + t.Run("Test when condition parse panics", func(t *testing.T) { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argo-rollouts-notification-secret", + Namespace: "argo-rollouts", + }, + Data: nil, + } + + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argo-rollouts-notification-configmap", + Namespace: "argo-rollouts", + }, + Data: map[string]string{ + "trigger.on-foo-reason": "- send: [on-foo-reason]\n when: \"time.Now().Sub(time.Parse(rollout.metadata.name)).Minutes() > 6\"\n", + "template.on-foo-reason": "message: Rollout {{.rollout.metadata.name}}'s time check", + }, + } + + k8sClient := fake.NewSimpleClientset() + sharedInformers := informers.NewSharedInformerFactory(k8sClient, 0) + cmInformer := sharedInformers.Core().V1().ConfigMaps().Informer() + secretInformer := sharedInformers.Core().V1().Secrets().Informer() + + secretInformer.GetIndexer().Add(secret) + cmInformer.GetIndexer().Add(cm) + + apiFactory := notificationapi.NewFactory(NewAPIFactorySettings(), defaults.Namespace(), secretInformer, cmInformer) + api, err := apiFactory.GetAPI() + assert.NoError(t, err) + + objMap, err := toObjectMap(r) + assert.NoError(t, err) + + cr, err := api.RunTrigger("on-foo-reason", objMap) + assert.NoError(t, err) + assert.False(t, cr[0].Triggered) + }) +} + func TestNotificationFailedCounter(t *testing.T) { r := v1alpha1.Rollout{ ObjectMeta: metav1.ObjectMeta{ @@ -333,7 +432,7 @@ func TestNewAPIFactorySettings(t *testing.T) { rollout := map[string]interface{}{"name": "hello"} vars := getVars(rollout, services.Destination{}) - assert.Equal(t, map[string]interface{}{"rollout": rollout}, vars) + assert.Equal(t, map[string]interface{}{"rollout": rollout, "time": timeExprs}, vars) } func TestWorkloadRefObjectMap(t *testing.T) {