Skip to content

Commit

Permalink
add analysisRun to the rollout notification
Browse files Browse the repository at this point in the history
Signed-off-by: asingh51 <[email protected]>

add analysisRun to the rollout notification

Signed-off-by: asingh51 <[email protected]>

deploy rollout ext in wave4 and 5
  • Loading branch information
asingh51 committed Jan 12, 2024
1 parent 985aaea commit a303280
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 50 deletions.
79 changes: 47 additions & 32 deletions utils/record/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"

Check failure on line 8 in utils/record/record.go

View workflow job for this annotation

GitHub Actions / Lint Go code

File is not `goimports`-ed (goimports)
informers "github.com/argoproj/argo-rollouts/pkg/client/informers/externalversions/rollouts/v1alpha1"
argoinformers "github.com/argoproj/argo-rollouts/pkg/client/informers/externalversions/rollouts/v1alpha1"
timeutil "github.com/argoproj/argo-rollouts/utils/time"
"github.com/argoproj/notifications-engine/pkg/api"
"github.com/argoproj/notifications-engine/pkg/services"
Expand Down Expand Up @@ -43,9 +43,10 @@ func init() {
}

const (
controllerAgentName = "rollouts-controller"
NotificationConfigMap = "argo-rollouts-notification-configmap"
NotificationSecret = "argo-rollouts-notification-secret"
controllerAgentName = "rollouts-controller"
NotificationConfigMap = "argo-rollouts-notification-configmap"
NotificationSecret = "argo-rollouts-notification-secret"
RolloutsPodTemplateHash = "rollouts-pod-template-hash"
)

type EventOptions struct {
Expand Down Expand Up @@ -228,47 +229,61 @@ func (e *EventRecorderAdapter) K8sRecorder() record.EventRecorder {
return e.Recorder
}

func NewAPIFactorySettings(arInformer informers.AnalysisRunInformer) api.Settings {
func getAnalysisRunsMatchLabels(ro v1alpha1.Rollout, arInformer argoinformers.AnalysisRunInformer) (interface{}, error) {

set := labels.Set(map[string]string{
RolloutsPodTemplateHash: ro.Status.CurrentPodHash,
})

ars, err := arInformer.Lister().AnalysisRuns(ro.Namespace).List(labels.SelectorFromSet(set))

if err != nil || len(ars) == 0 {
return nil, err
}

sort.Slice(ars[:], func(i, j int) bool {
ts1 := ars[i].ObjectMeta.CreationTimestamp.Time
ts2 := ars[j].ObjectMeta.CreationTimestamp.Time
return ts1.After(ts2)
})

var arsObj interface{}
arBytes, _ := json.Marshal(ars)
err = json.Unmarshal(arBytes, &arsObj)

if err != nil {
return nil, err
}

Check warning on line 256 in utils/record/record.go

View check run for this annotation

Codecov / codecov/patch

utils/record/record.go#L255-L256

Added lines #L255 - L256 were not covered by tests
return arsObj, nil
}

func NewAPIFactorySettings(arInformer argoinformers.AnalysisRunInformer) api.Settings {
return api.Settings{
SecretName: NotificationSecret,
ConfigMapName: NotificationConfigMap,
InitGetVars: func(cfg *api.Config, configMap *corev1.ConfigMap, secret *corev1.Secret) (getVars api.GetVars, err error) {

getVars = func(obj map[string]interface{}, dest services.Destination) (result map[string]any) {
result = map[string]any{"rollout": obj, "time": timeExprs}
InitGetVars: func(cfg *api.Config, configMap *corev1.ConfigMap, secret *corev1.Secret) (api.GetVars, error) {
return func(obj map[string]any, dest services.Destination) map[string]any {
var vars = map[string]any{"rollout": obj, "time": timeExprs}

if arInformer == nil {
return
return vars
}

un := unstructured.Unstructured{Object: obj}
var ro v1alpha1.Rollout
err = runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &ro)
if err != nil {
log.Errorf("failed to convert obj to rollout: %v", err)
result = map[string]any{"rollout": nil, "time": timeExprs}
return
err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj, &ro)

if err != nil || ro.Namespace == "" {
return vars
}

Check warning on line 277 in utils/record/record.go

View check run for this annotation

Codecov / codecov/patch

utils/record/record.go#L276-L277

Added lines #L276 - L277 were not covered by tests
arsObj, err := getAnalysisRunsMatchLabels(ro, arInformer)

ars, err := arInformer.Lister().AnalysisRuns(ro.Namespace).List(labels.Everything())
if err != nil {
log.Errorf("failed to fetch analysisRuns in the given namespace: %v", err)
result = map[string]any{"rollout": obj, "time": timeExprs}
return
return vars
}

Check warning on line 282 in utils/record/record.go

View check run for this annotation

Codecov / codecov/patch

utils/record/record.go#L281-L282

Added lines #L281 - L282 were not covered by tests

if len(ars) != 0 {
sort.Slice(ars[:], func(i, j int) bool {
ts1 := ars[i].ObjectMeta.CreationTimestamp.Time
ts2 := ars[j].ObjectMeta.CreationTimestamp.Time
return ts1.After(ts2)
})
result = map[string]any{"rollout": obj, "analysisRun": ars[0], "time": timeExprs}
}
return
}

return getVars, nil
vars = map[string]any{"rollout": obj, "analysisRuns": arsObj, "time": timeExprs}
return vars
}, nil
},
}
}
Expand Down
167 changes: 149 additions & 18 deletions utils/record/record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@ package record

import (
"bytes"
"encoding/json"
"errors"
"fmt"

Check failure on line 7 in utils/record/record_test.go

View workflow job for this annotation

GitHub Actions / Lint Go code

File is not `goimports`-ed (goimports)
"strings"
"testing"
"time"

"github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
argofake "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/fake"
argoinformers "github.com/argoproj/argo-rollouts/pkg/client/informers/externalversions"
argoinformersfactory "github.com/argoproj/argo-rollouts/pkg/client/informers/externalversions"
argoinformers "github.com/argoproj/argo-rollouts/pkg/client/informers/externalversions/rollouts/v1alpha1"
"github.com/argoproj/argo-rollouts/utils/defaults"

timeutil "github.com/argoproj/argo-rollouts/utils/time"
"github.com/argoproj/notifications-engine/pkg/api"
notificationapi "github.com/argoproj/notifications-engine/pkg/api"
"github.com/argoproj/notifications-engine/pkg/mocks"
Expand All @@ -24,11 +22,17 @@ import (
dto "github.com/prometheus/client_model/go"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"

"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"strings"
"testing"
"time"
)

var (
Expand Down Expand Up @@ -187,7 +191,7 @@ func TestSendNotificationsWhenConditionTime(t *testing.T) {
sharedInformers := informers.NewSharedInformerFactory(k8sClient, 0)

f := argofake.NewSimpleClientset()
rolloutsI := argoinformers.NewSharedInformerFactory(f, noResyncPeriodFunc())
rolloutsI := argoinformersfactory.NewSharedInformerFactory(f, noResyncPeriodFunc())
arInformer := rolloutsI.Argoproj().V1alpha1().AnalysisRuns()

cmInformer := sharedInformers.Core().V1().ConfigMaps().Informer()
Expand Down Expand Up @@ -237,7 +241,7 @@ func TestSendNotificationsWhenConditionTime(t *testing.T) {
secretInformer.GetIndexer().Add(secret)
cmInformer.GetIndexer().Add(cm)
f := argofake.NewSimpleClientset()
rolloutsI := argoinformers.NewSharedInformerFactory(f, noResyncPeriodFunc())
rolloutsI := argoinformersfactory.NewSharedInformerFactory(f, noResyncPeriodFunc())
arInformer := rolloutsI.Argoproj().V1alpha1().AnalysisRuns()

apiFactory := notificationapi.NewFactory(NewAPIFactorySettings(arInformer), defaults.Namespace(), secretInformer, cmInformer)
Expand Down Expand Up @@ -438,20 +442,147 @@ func TestSendNotificationsNoTrigger(t *testing.T) {
}

func TestNewAPIFactorySettings(t *testing.T) {

ars := []*v1alpha1.AnalysisRun{
{
ObjectMeta: metav1.ObjectMeta{
Name: "analysis-run-1",
CreationTimestamp: metav1.NewTime(timeutil.Now().Add(-1 * time.Hour)),
Namespace: "default",
Labels: map[string]string{"rollouts-pod-template-hash": "85659df978"},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "analysis-run-2",
CreationTimestamp: metav1.NewTime(timeutil.Now().Add(-2 * time.Hour)),
Namespace: "default",
Labels: map[string]string{"rollouts-pod-template-hash": "85659df978"},
},
},
}

f := argofake.NewSimpleClientset()
rolloutsI := argoinformers.NewSharedInformerFactory(f, noResyncPeriodFunc())
rolloutsI := argoinformersfactory.NewSharedInformerFactory(f, noResyncPeriodFunc())
arInformer := rolloutsI.Argoproj().V1alpha1().AnalysisRuns()
ro := v1alpha1.Rollout{
ObjectMeta: metav1.ObjectMeta{
Name: "rollout",
Namespace: "default",
},
Status: v1alpha1.RolloutStatus{
CurrentPodHash: "85659df978",
},
}

settings := NewAPIFactorySettings(arInformer)
assert.Equal(t, NotificationConfigMap, settings.ConfigMapName)
assert.Equal(t, NotificationSecret, settings.SecretName)
getVars, err := settings.InitGetVars(nil, nil, nil)
assert.NoError(t, err)

rollout := map[string]any{"name": "hello"}
vars := getVars(rollout, services.Destination{})
type expectedFunc func(obj map[string]interface{}, ar interface{}) map[string]interface{}
testcase := []struct {
name string
arInformer argoinformers.AnalysisRunInformer
rollout v1alpha1.Rollout
ars []*v1alpha1.AnalysisRun
expected expectedFunc
}{
{
name: "Send notification for rollout and analysisRun matches label",
arInformer: arInformer,
rollout: ro,
ars: ars,
expected: func(obj map[string]interface{}, ar interface{}) map[string]interface{} {
return map[string]interface{}{
"rollout": obj,
"analysisRuns": ar,
"time": timeExprs,
}
},
},
{
name: "Send notification only rollout when pod hash doesn't match",
arInformer: arInformer,
rollout: ro,
ars: append(ars, &v1alpha1.AnalysisRun{
ObjectMeta: metav1.ObjectMeta{
Name: "analysis-run-2",
CreationTimestamp: metav1.Now(),
Namespace: "default-1",
Labels: map[string]string{"rollouts-pod-template-hash": "123456"},
},
}),
expected: func(obj map[string]interface{}, ar interface{}) map[string]interface{} {
return map[string]interface{}{
"rollout": obj,
"analysisRuns": ar,
"time": timeExprs,
}
},
},
{
name: "arInformer is nil",
arInformer: nil,
rollout: ro,
ars: ars,
expected: func(obj map[string]interface{}, ar interface{}) map[string]interface{} {
return map[string]interface{}{
"rollout": obj,
"time": timeExprs,
}
},
},
{
name: "rollout object namespace and pod-hash not matched",
rollout: v1alpha1.Rollout{
ObjectMeta: metav1.ObjectMeta{
Name: "rollout",
},
},
ars: ars,
expected: func(obj map[string]interface{}, ar interface{}) map[string]interface{} {
ro.Namespace = ""
ro.Status.CurrentPodHash = "1234"
return map[string]interface{}{
"rollout": obj,
"time": timeExprs,
}
},
},
{
name: "no analysisRuns",
arInformer: nil,
rollout: ro,
ars: []*v1alpha1.AnalysisRun{},
expected: func(obj map[string]interface{}, ar interface{}) map[string]interface{} {
return map[string]interface{}{
"rollout": obj,
"time": timeExprs,
}
},
},
}

assert.Equal(t, map[string]any{"rollout": rollout, "time": timeExprs}, vars)
for _, test := range testcase {
t.Run(test.name, func(t *testing.T) {
for _, ar := range test.ars {
_ = arInformer.Informer().GetStore().Add(ar)
}
settings := NewAPIFactorySettings(test.arInformer)
getVars, err := settings.InitGetVars(nil, nil, nil)
require.NoError(t, err)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

obj, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&test.rollout)

arBytes, err := json.Marshal(ars)
var arsObj interface{}
_ = json.Unmarshal(arBytes, &arsObj)
vars := getVars(obj, services.Destination{})
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
assert.Equal(t, test.expected(obj, arsObj), vars)
})
}
}

func TestWorkloadRefObjectMap(t *testing.T) {
Expand Down

0 comments on commit a303280

Please sign in to comment.