github.com/argoproj/argo-cd@v1.8.7/cmd/argocd-util/commands/settings_test.go (about) 1 package commands 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "testing" 11 12 "github.com/argoproj/argo-cd/common" 13 utils "github.com/argoproj/argo-cd/util/io" 14 "github.com/argoproj/argo-cd/util/settings" 15 16 "github.com/stretchr/testify/assert" 17 v1 "k8s.io/api/core/v1" 18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 "k8s.io/client-go/kubernetes/fake" 20 ) 21 22 func captureStdout(callback func()) (string, error) { 23 oldStdout := os.Stdout 24 oldStderr := os.Stderr 25 r, w, err := os.Pipe() 26 if err != nil { 27 return "", err 28 } 29 os.Stdout = w 30 defer func() { 31 os.Stdout = oldStdout 32 os.Stderr = oldStderr 33 }() 34 35 callback() 36 utils.Close(w) 37 38 data, err := ioutil.ReadAll(r) 39 40 if err != nil { 41 return "", err 42 } 43 return string(data), err 44 } 45 46 func newSettingsManager(data map[string]string) *settings.SettingsManager { 47 clientset := fake.NewSimpleClientset(&v1.ConfigMap{ 48 ObjectMeta: metav1.ObjectMeta{ 49 Namespace: "default", 50 Name: common.ArgoCDConfigMapName, 51 Labels: map[string]string{ 52 "app.kubernetes.io/part-of": "argocd", 53 }, 54 }, 55 Data: data, 56 }, &v1.Secret{ 57 ObjectMeta: metav1.ObjectMeta{ 58 Namespace: "default", 59 Name: common.ArgoCDSecretName, 60 }, 61 Data: map[string][]byte{ 62 "admin.password": []byte("test"), 63 "server.secretkey": []byte("test"), 64 }, 65 }) 66 return settings.NewSettingsManager(context.Background(), clientset, "default") 67 } 68 69 type fakeCmdContext struct { 70 mgr *settings.SettingsManager 71 // nolint:unused,structcheck 72 out bytes.Buffer 73 } 74 75 func newCmdContext(data map[string]string) *fakeCmdContext { 76 return &fakeCmdContext{mgr: newSettingsManager(data)} 77 } 78 79 func (ctx *fakeCmdContext) createSettingsManager() (*settings.SettingsManager, error) { 80 return ctx.mgr, nil 81 } 82 83 type validatorTestCase struct { 84 validator string 85 data map[string]string 86 containsSummary string 87 containsError string 88 } 89 90 func TestCreateSettingsManager(t *testing.T) { 91 f, closer, err := tempFile(`apiVersion: v1 92 kind: ConfigMap 93 metadata: 94 name: argocd-cm 95 data: 96 url: https://myargocd.com`) 97 if !assert.NoError(t, err) { 98 return 99 } 100 defer utils.Close(closer) 101 102 opts := settingsOpts{argocdCMPath: f} 103 settingsManager, err := opts.createSettingsManager() 104 105 if !assert.NoError(t, err) { 106 return 107 } 108 109 argoCDSettings, err := settingsManager.GetSettings() 110 if !assert.NoError(t, err) { 111 return 112 } 113 114 assert.Equal(t, "https://myargocd.com", argoCDSettings.URL) 115 } 116 117 func TestValidator(t *testing.T) { 118 testCases := map[string]validatorTestCase{ 119 "General_SSOIsNotConfigured": { 120 validator: "general", containsSummary: "SSO is not configured", 121 }, 122 "General_DexInvalidConfig": { 123 validator: "general", 124 data: map[string]string{"dex.config": "abcdefg"}, 125 containsError: "invalid dex.config", 126 }, 127 "General_OIDCConfigured": { 128 validator: "general", 129 data: map[string]string{ 130 "url": "https://myargocd.com", 131 "oidc.config": ` 132 name: Okta 133 issuer: https://dev-123456.oktapreview.com 134 clientID: aaaabbbbccccddddeee 135 clientSecret: aaaabbbbccccddddeee`, 136 }, 137 containsSummary: "OIDC is configured", 138 }, 139 "General_DexConfiguredMissingURL": { 140 validator: "general", 141 data: map[string]string{ 142 "dex.config": `connectors: 143 - type: github 144 name: GitHub 145 config: 146 clientID: aabbccddeeff00112233 147 clientSecret: aabbccddeeff00112233`, 148 }, 149 containsSummary: "Dex is configured ('url' field is missing)", 150 }, 151 "Plugins_ValidConfig": { 152 validator: "plugins", 153 data: map[string]string{ 154 "configManagementPlugins": `[{"name": "test1"}, {"name": "test2"}]`, 155 }, 156 containsSummary: "2 plugins", 157 }, 158 "Kustomize_ModifiedOptions": { 159 validator: "kustomize", 160 containsSummary: "default options", 161 }, 162 "Kustomize_DefaultOptions": { 163 validator: "kustomize", 164 data: map[string]string{ 165 "kustomize.buildOptions": "updated-options (2 versions)", 166 "kustomize.versions.v123": "binary-123", 167 "kustomize.versions.v321": "binary-321", 168 }, 169 containsSummary: "updated-options", 170 }, 171 "Repositories": { 172 validator: "repositories", 173 data: map[string]string{ 174 "repositories": ` 175 - url: https://github.com/argoproj/my-private-repository1 176 - url: https://github.com/argoproj/my-private-repository2`, 177 }, 178 containsSummary: "2 repositories", 179 }, 180 "Accounts": { 181 validator: "accounts", 182 data: map[string]string{ 183 "accounts.user1": "apiKey, login", 184 "accounts.user2": "login", 185 "accounts.user3": "apiKey", 186 }, 187 containsSummary: "4 accounts", 188 }, 189 "ResourceOverrides": { 190 validator: "resource-overrides", 191 data: map[string]string{ 192 "resource.customizations": ` 193 admissionregistration.k8s.io/MutatingWebhookConfiguration: 194 ignoreDifferences: | 195 jsonPointers: 196 - /webhooks/0/clientConfig/caBundle`, 197 }, 198 containsSummary: "2 resource overrides", 199 }, 200 } 201 for name := range testCases { 202 tc := testCases[name] 203 t.Run(name, func(t *testing.T) { 204 validator, ok := validatorsByGroup[tc.validator] 205 if !assert.True(t, ok) { 206 return 207 } 208 summary, err := validator(newSettingsManager(tc.data)) 209 if tc.containsSummary != "" { 210 assert.NoError(t, err) 211 assert.Contains(t, summary, tc.containsSummary) 212 } else if tc.containsError != "" { 213 if assert.Error(t, err) { 214 assert.Contains(t, err.Error(), tc.containsError) 215 } 216 } 217 }) 218 } 219 } 220 221 const ( 222 testDeploymentYAML = `apiVersion: v1 223 apiVersion: apps/v1 224 kind: Deployment 225 metadata: 226 name: nginx-deployment 227 labels: 228 app: nginx 229 spec: 230 replicas: 0` 231 ) 232 233 func tempFile(content string) (string, io.Closer, error) { 234 f, err := ioutil.TempFile("", "*.yaml") 235 if err != nil { 236 return "", nil, err 237 } 238 _, err = f.Write([]byte(content)) 239 if err != nil { 240 _ = os.Remove(f.Name()) 241 return "", nil, err 242 } 243 return f.Name(), utils.NewCloser(func() error { 244 return os.Remove(f.Name()) 245 }), nil 246 } 247 248 func TestValidateSettingsCommand_NoErrors(t *testing.T) { 249 cmd := NewValidateSettingsCommand(newCmdContext(map[string]string{})) 250 out, err := captureStdout(func() { 251 err := cmd.Execute() 252 assert.NoError(t, err) 253 }) 254 255 assert.NoError(t, err) 256 for k := range validatorsByGroup { 257 assert.Contains(t, out, fmt.Sprintf("✅ %s", k)) 258 } 259 } 260 261 func TestResourceOverrideIgnoreDifferences(t *testing.T) { 262 f, closer, err := tempFile(testDeploymentYAML) 263 if !assert.NoError(t, err) { 264 return 265 } 266 defer utils.Close(closer) 267 268 t.Run("NoOverridesConfigured", func(t *testing.T) { 269 cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{})) 270 out, err := captureStdout(func() { 271 cmd.SetArgs([]string{"ignore-differences", f}) 272 err := cmd.Execute() 273 assert.NoError(t, err) 274 }) 275 assert.NoError(t, err) 276 assert.Contains(t, out, "No overrides configured") 277 }) 278 279 t.Run("DataIgnored", func(t *testing.T) { 280 cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ 281 "resource.customizations": `apps/Deployment: 282 ignoreDifferences: | 283 jsonPointers: 284 - /spec`})) 285 out, err := captureStdout(func() { 286 cmd.SetArgs([]string{"ignore-differences", f}) 287 err := cmd.Execute() 288 assert.NoError(t, err) 289 }) 290 assert.NoError(t, err) 291 assert.Contains(t, out, "< spec:") 292 }) 293 } 294 295 func TestResourceOverrideHealth(t *testing.T) { 296 f, closer, err := tempFile(testDeploymentYAML) 297 if !assert.NoError(t, err) { 298 return 299 } 300 defer utils.Close(closer) 301 302 t.Run("NoHealthAssessment", func(t *testing.T) { 303 cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ 304 "resource.customizations": `apps/Deployment: {}`})) 305 out, err := captureStdout(func() { 306 cmd.SetArgs([]string{"health", f}) 307 err := cmd.Execute() 308 assert.NoError(t, err) 309 }) 310 assert.NoError(t, err) 311 assert.Contains(t, out, "Health script is not configured") 312 }) 313 314 t.Run("HealthAssessmentConfigured", func(t *testing.T) { 315 cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ 316 "resource.customizations": `apps/Deployment: 317 health.lua: | 318 return { status = "Progressing" } 319 `})) 320 out, err := captureStdout(func() { 321 cmd.SetArgs([]string{"health", f}) 322 err := cmd.Execute() 323 assert.NoError(t, err) 324 }) 325 assert.NoError(t, err) 326 assert.Contains(t, out, "Progressing") 327 }) 328 } 329 330 func TestResourceOverrideAction(t *testing.T) { 331 f, closer, err := tempFile(testDeploymentYAML) 332 if !assert.NoError(t, err) { 333 return 334 } 335 defer utils.Close(closer) 336 337 t.Run("NoActions", func(t *testing.T) { 338 cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ 339 "resource.customizations": `apps/Deployment: {}`})) 340 out, err := captureStdout(func() { 341 cmd.SetArgs([]string{"run-action", f, "test"}) 342 err := cmd.Execute() 343 assert.NoError(t, err) 344 }) 345 assert.NoError(t, err) 346 assert.Contains(t, out, "Actions are not configured") 347 }) 348 349 t.Run("ActionConfigured", func(t *testing.T) { 350 cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ 351 "resource.customizations": `apps/Deployment: 352 actions: | 353 discovery.lua: | 354 actions = {} 355 actions["resume"] = {["disabled"] = false} 356 actions["restart"] = {["disabled"] = false} 357 return actions 358 definitions: 359 - name: test 360 action.lua: | 361 obj.metadata.labels["test"] = 'updated' 362 return obj 363 `})) 364 out, err := captureStdout(func() { 365 cmd.SetArgs([]string{"run-action", f, "test"}) 366 err := cmd.Execute() 367 assert.NoError(t, err) 368 }) 369 assert.NoError(t, err) 370 assert.Contains(t, out, "test: updated") 371 372 out, err = captureStdout(func() { 373 cmd.SetArgs([]string{"list-actions", f}) 374 err := cmd.Execute() 375 assert.NoError(t, err) 376 }) 377 assert.NoError(t, err) 378 assert.Contains(t, out, `NAME ENABLED 379 restart false 380 resume false 381 `) 382 }) 383 }