github.com/verrazzano/verrazzano@v1.7.0/pkg/helm/helmcli_test.go (about) 1 // Copyright (c) 2020, 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package helm 5 6 import ( 7 "testing" 8 9 "helm.sh/helm/v3/pkg/action" 10 "helm.sh/helm/v3/pkg/chart" 11 "helm.sh/helm/v3/pkg/cli" 12 "helm.sh/helm/v3/pkg/release" 13 "helm.sh/helm/v3/pkg/time" 14 15 "github.com/stretchr/testify/assert" 16 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 17 ) 18 19 const ns = "my-namespace" 20 const chartdir = "my_charts" 21 const helmRelease = "my-release" 22 const missingRelease = "no-release" 23 24 func testActionConfigWithReleaseUnknownStatus(log vzlog.VerrazzanoLogger, settings *cli.EnvSettings, namespace string) (*action.Configuration, error) { 25 return createActionConfigWithStatus(log, settings, namespace, release.StatusUnknown) 26 } 27 28 func testActionConfigWithReleaseUninstallingStatus(log vzlog.VerrazzanoLogger, settings *cli.EnvSettings, namespace string) (*action.Configuration, error) { 29 return createActionConfigWithStatus(log, settings, namespace, release.StatusUninstalling) 30 } 31 32 func testActionConfigWithRelease(log vzlog.VerrazzanoLogger, settings *cli.EnvSettings, namespace string) (*action.Configuration, error) { 33 return createActionConfigWithStatus(log, settings, namespace, release.StatusDeployed) 34 } 35 36 func createActionConfigWithStatus(log vzlog.VerrazzanoLogger, settings *cli.EnvSettings, namespace string, status release.Status) (*action.Configuration, error) { 37 return CreateActionConfig(true, helmRelease, status, log, createRelease) 38 } 39 40 func testActionConfigWithFailedRelease(log vzlog.VerrazzanoLogger, settings *cli.EnvSettings, namespace string) (*action.Configuration, error) { 41 return CreateActionConfig(true, helmRelease, release.StatusFailed, log, createRelease) 42 } 43 44 func testActionConfigWithPendingRelease(log vzlog.VerrazzanoLogger, settings *cli.EnvSettings, namespace string) (*action.Configuration, error) { 45 return CreateActionConfig(true, helmRelease, release.StatusPendingInstall, log, createRelease) 46 } 47 48 func testActionConfig(log vzlog.VerrazzanoLogger, settings *cli.EnvSettings, namespace string) (*action.Configuration, error) { 49 return CreateActionConfig(false, helmRelease, release.StatusFailed, log, createRelease) 50 } 51 52 func getChart() *chart.Chart { 53 return &chart.Chart{ 54 Metadata: &chart.Metadata{ 55 APIVersion: "v1", 56 Name: "hello", 57 Version: "0.1.0", 58 AppVersion: "1.0", 59 }, 60 Templates: []*chart.File{ 61 {Name: "templates/hello", Data: []byte("hello: world")}, 62 }, 63 } 64 } 65 66 func createRelease(name string, status release.Status) *release.Release { 67 now := time.Now() 68 return &release.Release{ 69 Name: name, 70 Namespace: ns, 71 Info: &release.Info{ 72 FirstDeployed: now, 73 LastDeployed: now, 74 Status: status, 75 Description: "Named Release Stub", 76 }, 77 Chart: getChart(), 78 Config: map[string]interface{}{ 79 "name1": "value1", 80 "name2": "value2", 81 "nestedKey": map[string]interface{}{ 82 "simpleKey": "simpleValue", 83 "anotherNestedKey": map[string]interface{}{ 84 "yetAnotherNestedKey": map[string]interface{}{ 85 "youReadyForAnotherNestedKey": "No", 86 }, 87 }, 88 }, 89 }, 90 Version: 1, 91 } 92 } 93 94 // TestGetValues tests the Helm get values command 95 // GIVEN a set of upgrade parameters 96 // 97 // WHEN I call Upgrade 98 // THEN the Helm upgrade returns success and the cmd object has correct values 99 func TestGetValues(t *testing.T) { 100 assertion := assert.New(t) 101 SetActionConfigFunction(testActionConfigWithRelease) 102 defer SetDefaultActionConfigFunction() 103 104 vals, err := GetValues(vzlog.DefaultLogger(), helmRelease, ns) 105 assertion.NoError(err, "GetValues returned an error") 106 assertion.NotZero(vals, "GetValues stdout should not be empty") 107 } 108 109 // TestGetValuesMap tests the Helm get values command 110 // GIVEN a set of upgrade parameters 111 // 112 // WHEN I call Upgrade 113 // THEN the Helm upgrade returns success and the cmd object has correct values 114 func TestGetValuesMap(t *testing.T) { 115 assertion := assert.New(t) 116 SetActionConfigFunction(testActionConfigWithRelease) 117 defer SetDefaultActionConfigFunction() 118 119 vals, err := GetValuesMap(vzlog.DefaultLogger(), helmRelease, ns) 120 assertion.NoError(err, "GetValues returned an error") 121 assertion.Len(vals, 3) 122 } 123 124 // TestUpgrade tests the Helm upgrade command 125 // GIVEN a set of upgrade parameters 126 // 127 // WHEN I call Upgrade 128 // THEN the Helm upgrade returns success and the cmd object has correct values 129 func TestUpgrade(t *testing.T) { 130 var overrides []HelmOverrides 131 overrides = append(overrides, HelmOverrides{SetOverrides: "name1=modifiedValue1"}) 132 assertion := assert.New(t) 133 SetActionConfigFunction(testActionConfigWithRelease) 134 defer SetDefaultActionConfigFunction() 135 SetLoadChartFunction(func(chartDir string) (*chart.Chart, error) { 136 return getChart(), nil 137 }) 138 defer SetDefaultLoadChartFunction() 139 140 _, err := Upgrade(vzlog.DefaultLogger(), helmRelease, ns, chartdir, false, false, overrides) 141 assertion.NoError(err, "Upgrade returned an error") 142 } 143 144 // TestUpgradeFail tests the Helm upgrade command failure condition 145 // GIVEN a set of upgrade parameters and a fake runner that fails 146 // 147 // WHEN I call Upgrade 148 // THEN the Helm upgrade returns an error 149 func TestUpgradeFail(t *testing.T) { 150 var overrides []HelmOverrides 151 overrides = append(overrides, HelmOverrides{SetOverrides: "name1=modifiedValue1"}) 152 assertion := assert.New(t) 153 SetActionConfigFunction(testActionConfigWithRelease) 154 defer SetDefaultActionConfigFunction() 155 // no chart load function should generate an error 156 157 _, err := Upgrade(vzlog.DefaultLogger(), helmRelease, ns, "", false, false, overrides) 158 assertion.Error(err, "Upgrade should have returned an error") 159 } 160 161 // TestUninstall tests the Helm Uninstall fn 162 // GIVEN a call to Uninstall 163 // 164 // WHEN the command executes successfully 165 // THEN the function returns no error 166 func TestUninstall(t *testing.T) { 167 assertion := assert.New(t) 168 SetActionConfigFunction(testActionConfigWithRelease) 169 defer SetDefaultActionConfigFunction() 170 171 err := Uninstall(vzlog.DefaultLogger(), helmRelease, ns, false) 172 assertion.NoError(err) 173 } 174 175 // TestUninstallError tests the Helm Uninstall fn 176 // GIVEN a call to Uninstall 177 // 178 // WHEN the command executes and returns an error 179 // THEN the function returns an error 180 func TestUninstallError(t *testing.T) { 181 assertion := assert.New(t) 182 SetActionConfigFunction(testActionConfig) 183 defer SetDefaultActionConfigFunction() 184 185 err := Uninstall(vzlog.DefaultLogger(), helmRelease, ns, false) 186 assertion.Error(err) 187 } 188 189 // TestIsReleaseInstalled tests checking if a Helm helmRelease is installed 190 // GIVEN a helmRelease name and namespace 191 // 192 // WHEN I call IsReleaseInstalled 193 // THEN the function returns success and found equal true 194 func TestIsReleaseInstalled(t *testing.T) { 195 assertion := assert.New(t) 196 SetActionConfigFunction(testActionConfigWithRelease) 197 defer SetDefaultActionConfigFunction() 198 199 found, err := IsReleaseInstalled(helmRelease, ns) 200 assertion.NoError(err, "IsReleaseInstalled returned an error") 201 assertion.True(found, "Release not found") 202 } 203 204 // TestIsReleaseNotInstalled tests checking if a Helm helmRelease is not installed 205 // GIVEN a helmRelease name and namespace 206 // 207 // WHEN I call IsReleaseInstalled 208 // THEN the function returns success and the correct found status 209 func TestIsReleaseNotInstalled(t *testing.T) { 210 assertion := assert.New(t) 211 SetActionConfigFunction(testActionConfigWithRelease) 212 defer SetDefaultActionConfigFunction() 213 214 found, err := IsReleaseInstalled(missingRelease, ns) 215 assertion.NoError(err, "IsReleaseInstalled returned an error") 216 assertion.False(found, "Release should not be found") 217 } 218 219 // TestIsReleaseInstalledFailed tests failure when checking if a Helm helmRelease is installed 220 // GIVEN a bad helmRelease name and namespace 221 // 222 // WHEN I call IsReleaseInstalled 223 // THEN the function returns a failure 224 func TestIsReleaseInstalledFailed(t *testing.T) { 225 assertion := assert.New(t) 226 SetActionConfigFunction(testActionConfigWithRelease) 227 defer SetDefaultActionConfigFunction() 228 229 found, err := IsReleaseInstalled("", ns) 230 assertion.Error(err, "IsReleaseInstalled should have returned an error") 231 assertion.False(found, "Release should not be found") 232 } 233 234 // TestReleaseExists tests checking if a Helm release is exists 235 // GIVEN a call to ReleaseExists 236 // 237 // WHEN for a Helm release in the Deployed state 238 // THEN the function returns true and no error 239 func TestReleaseExists(t *testing.T) { 240 assertion := assert.New(t) 241 SetActionConfigFunction(testActionConfigWithRelease) 242 defer SetDefaultActionConfigFunction() 243 244 exists, err := ReleaseExists(helmRelease, ns) 245 assertion.NoError(err, "ReleaseExists returned an error") 246 assertion.True(exists, "Release not found") 247 } 248 249 // TestReleaseExistsUninstallingStatus tests checking if a Helm release is exists 250 // GIVEN a call to ReleaseExists 251 // 252 // WHEN for a Helm release in the Uninstalling state 253 // THEN the function returns true and no error 254 func TestReleaseExistsUninstallingStatus(t *testing.T) { 255 assertion := assert.New(t) 256 SetActionConfigFunction(testActionConfigWithReleaseUninstallingStatus) 257 defer SetDefaultActionConfigFunction() 258 259 exists, err := ReleaseExists(helmRelease, ns) 260 assertion.NoError(err, "ReleaseExists returned an error") 261 assertion.True(exists, "Release not found") 262 } 263 264 // TestReleaseExistsUnknownStatus tests checking if a Helm release is exists 265 // GIVEN a call to ReleaseExists 266 // 267 // WHEN for a Helm release in the Unknown state 268 // THEN the function returns true and no error 269 func TestReleaseExistsUnknownStatus(t *testing.T) { 270 assertion := assert.New(t) 271 SetActionConfigFunction(testActionConfigWithReleaseUnknownStatus) 272 defer SetDefaultActionConfigFunction() 273 274 exists, err := ReleaseExists(helmRelease, ns) 275 assertion.NoError(err, "ReleaseExists returned an error") 276 assertion.True(exists, "Release not found") 277 } 278 279 // TestReleaseExistsNotInstalled tests the ReleaseExists func 280 // GIVEN a call to ReleaseExists 281 // 282 // WHEN for a Helm release that does not exist 283 // THEN the function returns false and no error 284 func TestReleaseExistsNotInstalled(t *testing.T) { 285 assertion := assert.New(t) 286 SetActionConfigFunction(testActionConfigWithRelease) 287 defer SetDefaultActionConfigFunction() 288 289 exists, err := ReleaseExists(missingRelease, ns) 290 assertion.NoError(err, "IsReleaseInstalled returned an error") 291 assertion.False(exists, "Release should not be exists") 292 } 293 294 // TestReleaseExistsError tests the ReleaseExists func 295 // GIVEN a bad helmRelease name and namespace 296 // 297 // WHEN I call ReleaseExists 298 // THEN the function returns false and an error 299 func TestReleaseExistsError(t *testing.T) { 300 assertion := assert.New(t) 301 SetActionConfigFunction(testActionConfigWithRelease) 302 defer SetDefaultActionConfigFunction() 303 304 found, err := ReleaseExists("", ns) 305 assertion.Error(err, "IsReleaseInstalled should have returned an error") 306 assertion.False(found, "Release should not be found") 307 } 308 309 // TestIsReleaseDeployed tests checking if a Helm helmRelease is installed 310 // GIVEN a helmRelease name and namespace 311 // 312 // WHEN I call IsReleaseDeployed 313 // THEN the function returns success and found equal true 314 func TestIsReleaseDeployed(t *testing.T) { 315 assertion := assert.New(t) 316 SetActionConfigFunction(testActionConfigWithRelease) 317 defer SetDefaultActionConfigFunction() 318 319 found, err := IsReleaseDeployed(helmRelease, ns) 320 assertion.NoError(err, "IsReleaseDeployed returned an error") 321 assertion.True(found, "Release not found") 322 } 323 324 // TestIsReleaseNotDeployed tests checking if a Helm helmRelease is not installed 325 // GIVEN a helmRelease name and namespace 326 // 327 // WHEN I call IsReleaseDeployed 328 // THEN the function returns success and the correct found status 329 func TestIsReleaseNotDeployed(t *testing.T) { 330 assertion := assert.New(t) 331 SetActionConfigFunction(testActionConfigWithRelease) 332 defer SetDefaultActionConfigFunction() 333 334 found, err := IsReleaseDeployed(missingRelease, ns) 335 assertion.NoError(err, "IsReleaseDeployed returned an error") 336 assertion.False(found, "Release should not be found") 337 } 338 339 // TestIsReleaseFailedChartNotFound tests checking if a Helm helmRelease is in a failed state 340 // GIVEN a helmRelease name and namespace 341 // 342 // WHEN I call IsReleaseFailed and the status is ChartNotFound 343 // THEN the function returns false and no error 344 func TestIsReleaseFailedChartNotFound(t *testing.T) { 345 assertion := assert.New(t) 346 SetActionConfigFunction(testActionConfigWithRelease) 347 defer SetDefaultActionConfigFunction() 348 349 failed, err := IsReleaseFailed("foo", "bar") 350 assertion.NoError(err, "IsReleaseFailed returned an error") 351 assertion.False(failed, "ReleaseFailed should be false") 352 } 353 354 // TestIsReleaseFailedChartDeployed tests checking if a Helm helmRelease is in a failed state 355 // GIVEN a helmRelease name and namespace 356 // 357 // WHEN I call IsReleaseFailed and the status is deployed 358 // THEN the function returns false and no error 359 func TestIsReleaseFailedChartDeployed(t *testing.T) { 360 assertion := assert.New(t) 361 SetActionConfigFunction(testActionConfigWithFailedRelease) 362 defer SetDefaultActionConfigFunction() 363 364 failed, err := IsReleaseFailed(helmRelease, ns) 365 assertion.NoError(err, "IsReleaseFailed returned an error") 366 assertion.True(failed, "ReleaseFailed should be true") 367 } 368 369 // Test_getReleaseStateDeployed tests the getReleaseState fn 370 // GIVEN a call to getReleaseState 371 // 372 // WHEN the chart state is deployed 373 // THEN the function returns ChartStatusDeployed and no error 374 func Test_getReleaseStateDeployed(t *testing.T) { 375 assertion := assert.New(t) 376 SetActionConfigFunction(testActionConfigWithRelease) 377 defer SetDefaultActionConfigFunction() 378 379 state, err := getReleaseState(helmRelease, ns) 380 assertion.NoError(err) 381 assertion.Equalf(ChartStatusDeployed, state, "unpexected state: %s", state) 382 } 383 384 // Test_getReleaseStateDeployed tests the getReleaseState fn 385 // GIVEN a call to getReleaseState 386 // 387 // WHEN the chart state is pending-install 388 // THEN the function returns ChartStatusPendingInstall and no error 389 func Test_getReleaseStatePendingInstall(t *testing.T) { 390 assertion := assert.New(t) 391 SetActionConfigFunction(testActionConfigWithPendingRelease) 392 defer SetDefaultActionConfigFunction() 393 394 state, err := getReleaseState(helmRelease, ns) 395 assertion.NoError(err) 396 assertion.Equalf(ChartStatusPendingInstall, state, "unpexected state: %s", state) 397 } 398 399 // Test_getReleaseStateChartNotFound tests the getReleaseState fn 400 // GIVEN a call to getReleaseState 401 // 402 // WHEN the chart/helmRelease can not be found 403 // THEN the function returns "" and no error 404 func Test_getReleaseStateChartNotFound(t *testing.T) { 405 assertion := assert.New(t) 406 SetActionConfigFunction(testActionConfigWithRelease) 407 defer SetDefaultActionConfigFunction() 408 409 state, err := getReleaseState("weblogic-operator", "verrazzano-system") 410 assertion.NoError(err) 411 assertion.Equalf("", state, "unpexected state: %s", state) 412 } 413 414 // Test_getChartStatusDeployed tests the getChartStatus fn 415 // GIVEN a call to getChartStatus 416 // 417 // WHEN Helm returns a deployed state 418 // THEN the function returns "deployed" and no error 419 func Test_getChartStatusDeployed(t *testing.T) { 420 assertion := assert.New(t) 421 SetActionConfigFunction(testActionConfigWithRelease) 422 defer SetDefaultActionConfigFunction() 423 424 state, err := getChartStatus(helmRelease, ns) 425 assertion.NoError(err) 426 assertion.Equalf(ChartStatusDeployed, state, "unpexected state: %s", state) 427 } 428 429 // Test_getChartStatusChartNotFound tests the getChartStatus fn 430 // GIVEN a call to getChartStatus 431 // 432 // WHEN the Chart is not found 433 // THEN the function returns chart not found and no error 434 func Test_getChartStatusChartNotFound(t *testing.T) { 435 assertion := assert.New(t) 436 SetActionConfigFunction(testActionConfigWithRelease) 437 defer SetDefaultActionConfigFunction() 438 439 state, err := getChartStatus("weblogic-operator", "verrazzano-system") 440 assertion.NoError(err) 441 assertion.Equalf(ChartNotFound, state, "unpexected state: %s", state) 442 } 443 444 // TestGetReleaseValue tests the GetReleaseValues fn 445 // GIVEN a call to GetReleaseValues 446 // 447 // WHEN a valid helm helmRelease and namespace are deployed 448 // THEN the function returns the value/true/nil if the helm key exists, or ""/false/nil if it doesn't 449 func TestGetReleaseValues(t *testing.T) { 450 assertion := assert.New(t) 451 SetActionConfigFunction(testActionConfigWithRelease) 452 defer SetDefaultActionConfigFunction() 453 454 keys := []string{"name1", "name2", "simpleKey", "foo"} 455 value, err := GetReleaseValues(vzlog.DefaultLogger(), keys, helmRelease, ns) 456 457 expectedMap := map[string]interface{}{ 458 "name1": "value1", 459 "name2": "value2", 460 } 461 assertion.NoError(err) 462 assertion.Equal(expectedMap, value, "Map did not contain expected values") 463 } 464 465 // TestGetReleaseStringValue tests the GetReleaseStringValues fn 466 // GIVEN a call to GetReleaseStringValues 467 // 468 // WHEN a valid helm helmRelease and namespace are deployed 469 // THEN the function returns the value/true/nil if the helm key exists, or ""/false/nil if it doesn't 470 func TestGetReleaseStringValue(t *testing.T) { 471 assertion := assert.New(t) 472 SetActionConfigFunction(testActionConfigWithRelease) 473 defer SetDefaultActionConfigFunction() 474 475 keys := []string{"nestedKey", "foo"} 476 value, err := GetReleaseStringValues(vzlog.DefaultLogger(), keys, helmRelease, ns) 477 478 expectedMap := map[string]string{ 479 "nestedKey": "map[anotherNestedKey:map[yetAnotherNestedKey:map[youReadyForAnotherNestedKey:No]] simpleKey:simpleValue]", 480 } 481 assertion.NoError(err) 482 assertion.Equal(expectedMap, value, "Map did not contain expected values") 483 } 484 485 // TestGetReleaseValueReleaseNotFound tests the GetReleaseValues fn 486 // GIVEN a call to GetReleaseValues 487 // 488 // WHEN a the helm helmRelease is NOT deployed 489 // THEN the function returns the value/true/nil if the helm key exists, or ""/false/nil if it doesn't 490 func TestGetReleaseValueReleaseNotFound(t *testing.T) { 491 assertion := assert.New(t) 492 SetActionConfigFunction(testActionConfigWithRelease) 493 defer SetDefaultActionConfigFunction() 494 495 keys := []string{"txtOwnerId", "external-dns"} 496 values, err := GetReleaseValues(vzlog.DefaultLogger(), keys, "external-dns", "cert-manager") 497 assertion.NoErrorf(err, "Unexpected error: %v", err) 498 assertion.Equal(map[string]interface{}{}, values, "Found unexpected helmRelease value") 499 } 500 501 // Test_maskSensitiveData tests the maskSensitiveData function 502 func Test_maskSensitiveData(t *testing.T) { 503 // GIVEN a string with sensitive data 504 // WHEN the maskSensitiveData function is called 505 // THEN the returned string has sensitive values masked 506 str := `Running command: /usr/bin/helm upgrade mysql /verrazzano/platform-operator/thirdparty/charts/mysql 507 --wait --namespace keycloak --install -f /verrazzano/platform-operator/helm_config/overrides/mysql-values.yaml 508 -f /tmp/values-145495151.yaml 509 --set imageTag=8.0.26,image=ghcr.io/verrazzano/mysql,mysqlPassword=BgD2SBNaGm,mysqlRootPassword=ydqtBpasQ4` 510 expected := `Running command: /usr/bin/helm upgrade mysql /verrazzano/platform-operator/thirdparty/charts/mysql 511 --wait --namespace keycloak --install -f /verrazzano/platform-operator/helm_config/overrides/mysql-values.yaml 512 -f /tmp/values-145495151.yaml 513 --set imageTag=8.0.26,image=ghcr.io/verrazzano/mysql,mysqlPassword=*****,mysqlRootPassword=*****` 514 maskedStr := maskSensitiveData(str) 515 assert.Equal(t, expected, maskedStr) 516 517 // GIVEN a string without sensitive data 518 // WHEN the maskSensitiveData function is called 519 // THEN the returned string is unaltered 520 str = `Running command: /usr/bin/helm upgrade ingress-controller /verrazzano/platform-operator/thirdparty/charts/ingress-nginx 521 --wait --namespace ingress-nginx --install -f /verrazzano/platform-operator/helm_config/overrides/ingress-nginx-values.yaml 522 -f /tmp/values-037653479.yaml --set controller.image.tag=0.46.0-20211005200943-bd017fde2, 523 controller.image.repository=ghcr.io/verrazzano/nginx-ingress-controller, 524 defaultBackend.image.tag=0.46.0-20211005200943-bd017fde2, 525 defaultBackend.image.repository=ghcr.io/verrazzano/nginx-ingress-default-backend,controller.service.type=LoadBalancer` 526 maskedStr = maskSensitiveData(str) 527 assert.Equal(t, str, maskedStr) 528 } 529 530 // Test_GetReleaseAppVersion tests the GetReleaseAppVersion function 531 // GIVEN a call to GetReleaseAppVersion 532 // 533 // WHEN varying the inputs and underlying status 534 // THEN test the expected result is returned 535 func Test_GetReleaseAppVersion(t *testing.T) { 536 assertion := assert.New(t) 537 SetActionConfigFunction(testActionConfigWithRelease) 538 defer SetDefaultActionConfigFunction() 539 540 got, err := GetReleaseAppVersion(helmRelease, ns) 541 assertion.NoError(err) 542 assertion.Equal("1.0", got) 543 }