Skip to content

Commit 55ec556

Browse files
committed
fix: resolve argocdService initialization issue in notifications CLI
The notifications CLI commands were failing with 'argocdService is not initialized' error in v3.1.0 due to incorrect initialization order. This fix introduces GetFactorySettingsDeferred that uses a closure to defer service access until it's actually needed, ensuring the service is properly initialized when accessed. Changes: - Add GetFactorySettingsDeferred function with simplified naming - Add unit test to verify error handling for uninitialized service - Update notifications command to use deferred initialization pattern Fixes #24196 Signed-off-by: puretension <rlrlfhtm5@gmail.com>
1 parent 4e42f00 commit 55ec556

File tree

3 files changed

+36
-81
lines changed

3 files changed

+36
-81
lines changed

cmd/argocd/commands/admin/notifications.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ func NewNotificationsCommand() *cobra.Command {
3030
)
3131

3232
var argocdService service.Service
33+
3334
toolsCommand := cmd.NewToolsCommand(
3435
"notifications",
3536
"argocd admin notifications",
3637
applications,
37-
settings.GetFactorySettingsForCLI(argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false),
38+
settings.GetFactorySettingsDeferred(func() service.Service { return argocdService }, "argocd-notifications-secret", "argocd-notifications-cm", false),
3839
func(clientConfig clientcmd.ClientConfig) {
3940
k8sCfg, err := clientConfig.ClientConfig()
4041
if err != nil {

util/notification/settings/settings.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,25 @@ func GetFactorySettingsForCLI(argocdService service.Service, secretName, configM
4545
}
4646
}
4747

48+
// GetFactorySettingsDeferred allows deferred service initialization for CLI commands.
49+
func GetFactorySettingsDeferred(serviceGetter func() service.Service, secretName, configMapName string, selfServiceNotificationEnabled bool) api.Settings {
50+
return api.Settings{
51+
SecretName: secretName,
52+
ConfigMapName: configMapName,
53+
InitGetVars: func(cfg *api.Config, configMap *corev1.ConfigMap, secret *corev1.Secret) (api.GetVars, error) {
54+
argocdService := serviceGetter()
55+
if argocdService == nil {
56+
return nil, errors.New("argocdService is not initialized")
57+
}
58+
59+
if selfServiceNotificationEnabled {
60+
return initGetVarsWithoutSecret(argocdService, cfg, configMap, secret)
61+
}
62+
return initGetVars(argocdService, cfg, configMap, secret)
63+
},
64+
}
65+
}
66+
4867
func getContext(cfg *api.Config, configMap *corev1.ConfigMap, secret *corev1.Secret) (map[string]string, error) {
4968
context := map[string]string{}
5069
if contextYaml, ok := configMap.Data["context"]; ok {
Lines changed: 15 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,30 @@
11
package settings
22

33
import (
4-
"fmt"
54
"testing"
65

7-
"github.com/argoproj/notifications-engine/pkg/api"
8-
"github.com/argoproj/notifications-engine/pkg/services"
96
"github.com/stretchr/testify/assert"
10-
"github.com/stretchr/testify/require"
117
corev1 "k8s.io/api/core/v1"
12-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13-
"k8s.io/client-go/kubernetes/fake"
148

15-
"github.com/argoproj/argo-cd/v3/reposerver/apiclient/mocks"
9+
"github.com/argoproj/notifications-engine/pkg/api"
1610
service "github.com/argoproj/argo-cd/v3/util/notification/argocd"
1711
)
1812

19-
const (
20-
testNamespace = "default"
21-
testContextKey = "test-context-key"
22-
testContextKeyValue = "test-context-key-value"
23-
)
13+
func TestGetFactorySettingsDeferred_ServiceNotInitialized(t *testing.T) {
14+
var argocdService service.Service // nil service
2415

25-
func TestInitGetVars(t *testing.T) {
26-
notificationsCm := corev1.ConfigMap{
27-
ObjectMeta: metav1.ObjectMeta{
28-
Namespace: testNamespace,
29-
Name: "argocd-notifications-cm",
30-
},
31-
Data: map[string]string{
32-
"context": fmt.Sprintf("%s: %s", testContextKey, testContextKeyValue),
33-
"service.webhook.test": "url: https://test.example.com",
34-
"template.app-created": "email:\n subject: Application {{.app.metadata.name}} has been created.\nmessage: Application {{.app.metadata.name}} has been created.\nteams:\n title: Application {{.app.metadata.name}} has been created.\n",
35-
"trigger.on-created": "- description: Application is created.\n oncePer: app.metadata.name\n send:\n - app-created\n when: \"true\"\n",
36-
},
37-
}
38-
notificationsSecret := corev1.Secret{
39-
ObjectMeta: metav1.ObjectMeta{
40-
Name: "argocd-notifications-secret",
41-
Namespace: testNamespace,
42-
},
43-
Data: map[string][]byte{
44-
"notification-secret": []byte("secret-value"),
45-
},
46-
}
47-
kubeclientset := fake.NewClientset(&corev1.ConfigMap{
48-
ObjectMeta: metav1.ObjectMeta{
49-
Namespace: testNamespace,
50-
Name: "argocd-notifications-cm",
51-
},
52-
Data: notificationsCm.Data,
53-
},
54-
&corev1.Secret{
55-
ObjectMeta: metav1.ObjectMeta{
56-
Name: "argocd-notifications-secret",
57-
Namespace: testNamespace,
58-
},
59-
Data: notificationsSecret.Data,
60-
})
61-
mockRepoClient := &mocks.Clientset{RepoServerServiceClient: &mocks.RepoServerServiceClient{}}
62-
argocdService, err := service.NewArgoCDService(kubeclientset, testNamespace, mockRepoClient)
63-
require.NoError(t, err)
64-
defer argocdService.Close()
65-
config := api.Config{}
66-
testDestination := services.Destination{
67-
Service: "webhook",
68-
}
69-
emptyAppData := map[string]any{}
16+
settings := GetFactorySettingsDeferred(
17+
func() service.Service { return argocdService },
18+
"test-secret",
19+
"test-configmap",
20+
false,
21+
)
7022

71-
varsProvider, _ := initGetVars(argocdService, &config, &notificationsCm, &notificationsSecret)
23+
cfg := &api.Config{}
24+
configMap := &corev1.ConfigMap{}
25+
secret := &corev1.Secret{}
7226

73-
t.Run("Vars provider serves Application data on app key", func(t *testing.T) {
74-
appData := map[string]any{
75-
"name": "app-name",
76-
}
77-
result := varsProvider(appData, testDestination)
78-
assert.NotNil(t, t, result["app"])
79-
assert.Equal(t, result["app"], appData)
80-
})
81-
t.Run("Vars provider serves notification context data on context key", func(t *testing.T) {
82-
expectedContext := map[string]string{
83-
testContextKey: testContextKeyValue,
84-
"notificationType": testDestination.Service,
85-
}
86-
result := varsProvider(emptyAppData, testDestination)
87-
assert.NotNil(t, result["context"])
88-
assert.Equal(t, expectedContext, result["context"])
89-
})
90-
t.Run("Vars provider serves notification secrets on secrets key", func(t *testing.T) {
91-
result := varsProvider(emptyAppData, testDestination)
92-
assert.NotNil(t, result["secrets"])
93-
assert.Equal(t, result["secrets"], notificationsSecret.Data)
94-
})
27+
_, err := settings.InitGetVars(cfg, configMap, secret)
28+
assert.Error(t, err)
29+
assert.Contains(t, err.Error(), "argocdService is not initialized")
9530
}

0 commit comments

Comments
 (0)