github.com/SAP/jenkins-library@v1.362.0/cmd/gitopsUpdateDeployment_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package cmd 5 6 import ( 7 "errors" 8 "github.com/SAP/jenkins-library/pkg/piperutils" 9 "github.com/go-git/go-git/v5" 10 "github.com/go-git/go-git/v5/plumbing" 11 "github.com/stretchr/testify/assert" 12 "io" 13 "os" 14 "path/filepath" 15 "strings" 16 "testing" 17 ) 18 19 func TestBuildRegistryPlusImage(t *testing.T) { 20 t.Parallel() 21 t.Run("build full image", func(t *testing.T) { 22 t.Parallel() 23 registryImage, err := buildRegistryPlusImage(&gitopsUpdateDeploymentOptions{ 24 ContainerRegistryURL: "https://myregistry.com/registry/containers", 25 ContainerImageNameTag: "myFancyContainer:1337", 26 }) 27 assert.NoError(t, err) 28 assert.Equal(t, "myregistry.com/myFancyContainer:1337", registryImage) 29 }) 30 31 t.Run("without registry", func(t *testing.T) { 32 t.Parallel() 33 registryImage, err := buildRegistryPlusImage(&gitopsUpdateDeploymentOptions{ 34 ContainerRegistryURL: "", 35 ContainerImageNameTag: "myFancyContainer:1337", 36 }) 37 assert.NoError(t, err) 38 assert.Equal(t, "myFancyContainer:1337", registryImage) 39 }) 40 t.Run("without faulty URL", func(t *testing.T) { 41 t.Parallel() 42 _, err := buildRegistryPlusImage(&gitopsUpdateDeploymentOptions{ 43 ContainerRegistryURL: "//myregistry.com/registry/containers", 44 ContainerImageNameTag: "myFancyContainer:1337", 45 }) 46 assert.EqualError(t, err, "registry URL could not be extracted: invalid registry url") 47 }) 48 } 49 50 func TestBuildRegistryPlusImageWithoutTag(t *testing.T) { 51 t.Parallel() 52 t.Run("build full image", func(t *testing.T) { 53 t.Parallel() 54 registryImage, tag, err := buildRegistryPlusImageAndTagSeparately(&gitopsUpdateDeploymentOptions{ 55 ContainerRegistryURL: "https://myregistry.com/registry/containers", 56 ContainerImageNameTag: "myFancyContainer:1337", 57 }) 58 assert.NoError(t, err) 59 assert.Equal(t, "myregistry.com/myFancyContainer", registryImage) 60 assert.Equal(t, "1337", tag) 61 }) 62 63 t.Run("without registry", func(t *testing.T) { 64 t.Parallel() 65 registryImage, tag, err := buildRegistryPlusImageAndTagSeparately(&gitopsUpdateDeploymentOptions{ 66 ContainerRegistryURL: "", 67 ContainerImageNameTag: "myFancyContainer:1337", 68 }) 69 assert.NoError(t, err) 70 assert.Equal(t, "myFancyContainer", registryImage) 71 assert.Equal(t, "1337", tag) 72 }) 73 t.Run("without faulty URL", func(t *testing.T) { 74 t.Parallel() 75 _, _, err := buildRegistryPlusImageAndTagSeparately(&gitopsUpdateDeploymentOptions{ 76 ContainerRegistryURL: "//myregistry.com/registry/containers", 77 ContainerImageNameTag: "myFancyContainer:1337", 78 }) 79 assert.EqualError(t, err, "registry URL could not be extracted: invalid registry url") 80 }) 81 } 82 83 func TestRunGitopsUpdateDeploymentWithKubectl(t *testing.T) { 84 var validConfiguration = &gitopsUpdateDeploymentOptions{ 85 BranchName: "main", 86 CommitMessage: "This is the commit message", 87 ServerURL: "https://github.com", 88 Username: "admin3", 89 Password: "validAccessToken", 90 FilePath: "dir1/dir2/depl.yaml", 91 ContainerName: "myContainer", 92 ContainerRegistryURL: "https://myregistry.com/registry/containers", 93 ContainerImageNameTag: "myFancyContainer:1337", 94 Tool: "kubectl", 95 } 96 97 t.Parallel() 98 t.Run("successful run", func(t *testing.T) { 99 t.Parallel() 100 gitUtilsMock := &gitUtilsMock{} 101 runnerMock := &gitOpsExecRunnerMock{} 102 runnerMock.expectedYaml = expectedYaml 103 104 err := runGitopsUpdateDeployment(validConfiguration, runnerMock, gitUtilsMock, &filesMock{}) 105 assert.NoError(t, err) 106 assert.Equal(t, validConfiguration.BranchName, gitUtilsMock.changedBranch) 107 assert.Len(t, gitUtilsMock.savedFiles, 1) 108 assert.Equal(t, expectedYaml, gitUtilsMock.savedFiles[0]) 109 assert.Equal(t, "This is the commit message", gitUtilsMock.commitMessage) 110 assert.Equal(t, "kubectl", runnerMock.executable) 111 assert.Equal(t, "patch", runnerMock.params[0]) 112 assert.Equal(t, "--local", runnerMock.params[1]) 113 assert.Equal(t, "--output=yaml", runnerMock.params[2]) 114 assert.Equal(t, `--patch={"spec":{"template":{"spec":{"containers":[{"name":"myContainer","image":"myregistry.com/myFancyContainer:1337"}]}}}}`, runnerMock.params[3]) 115 assert.True(t, strings.Contains(runnerMock.params[4], filepath.Join("dir1/dir2/depl.yaml"))) 116 }) 117 118 t.Run("default commit message", func(t *testing.T) { 119 t.Parallel() 120 var configuration = *validConfiguration 121 configuration.CommitMessage = "" 122 123 gitUtilsMock := &gitUtilsMock{} 124 runnerMock := &gitOpsExecRunnerMock{} 125 runnerMock.expectedYaml = expectedYaml 126 127 err := runGitopsUpdateDeployment(&configuration, runnerMock, gitUtilsMock, &filesMock{}) 128 assert.NoError(t, err) 129 assert.Equal(t, validConfiguration.BranchName, gitUtilsMock.changedBranch) 130 assert.Len(t, gitUtilsMock.savedFiles, 1) 131 assert.Equal(t, expectedYaml, gitUtilsMock.savedFiles[0]) 132 assert.Equal(t, "Updated myregistry.com/myFancyContainer to version 1337", gitUtilsMock.commitMessage) 133 assert.Equal(t, "kubectl", runnerMock.executable) 134 assert.Equal(t, "patch", runnerMock.params[0]) 135 assert.Equal(t, "--local", runnerMock.params[1]) 136 assert.Equal(t, "--output=yaml", runnerMock.params[2]) 137 assert.Equal(t, `--patch={"spec":{"template":{"spec":{"containers":[{"name":"myContainer","image":"myregistry.com/myFancyContainer:1337"}]}}}}`, runnerMock.params[3]) 138 assert.True(t, strings.Contains(runnerMock.params[4], filepath.Join("dir1/dir2/depl.yaml"))) 139 }) 140 141 t.Run("ChartPath not used for kubectl", func(t *testing.T) { 142 t.Parallel() 143 var configuration = *validConfiguration 144 configuration.ChartPath = "chartPath" 145 146 gitUtilsMock := &gitUtilsMock{} 147 runnerMock := &gitOpsExecRunnerMock{} 148 runnerMock.expectedYaml = expectedYaml 149 150 err := runGitopsUpdateDeployment(&configuration, runnerMock, gitUtilsMock, &filesMock{}) 151 assert.NoError(t, err) 152 assert.Equal(t, configuration.BranchName, gitUtilsMock.changedBranch) 153 assert.Len(t, gitUtilsMock.savedFiles, 1) 154 assert.Equal(t, expectedYaml, gitUtilsMock.savedFiles[0]) 155 assert.Equal(t, "kubectl", runnerMock.executable) 156 assert.Equal(t, "patch", runnerMock.params[0]) 157 assert.Equal(t, "--local", runnerMock.params[1]) 158 assert.Equal(t, "--output=yaml", runnerMock.params[2]) 159 assert.Equal(t, `--patch={"spec":{"template":{"spec":{"containers":[{"name":"myContainer","image":"myregistry.com/myFancyContainer:1337"}]}}}}`, runnerMock.params[3]) 160 assert.True(t, strings.Contains(runnerMock.params[4], filepath.Join("dir1/dir2/depl.yaml"))) 161 }) 162 163 t.Run("HelmValues not used for kubectl", func(t *testing.T) { 164 t.Parallel() 165 var configuration = *validConfiguration 166 configuration.HelmValues = []string{"HelmValues"} 167 168 gitUtilsMock := &gitUtilsMock{} 169 runnerMock := &gitOpsExecRunnerMock{} 170 runnerMock.expectedYaml = expectedYaml 171 172 err := runGitopsUpdateDeployment(&configuration, runnerMock, gitUtilsMock, &filesMock{}) 173 assert.NoError(t, err) 174 assert.Equal(t, configuration.BranchName, gitUtilsMock.changedBranch) 175 assert.Len(t, gitUtilsMock.savedFiles, 1) 176 assert.Equal(t, expectedYaml, gitUtilsMock.savedFiles[0]) 177 assert.Equal(t, "kubectl", runnerMock.executable) 178 assert.Equal(t, "patch", runnerMock.params[0]) 179 assert.Equal(t, "--local", runnerMock.params[1]) 180 assert.Equal(t, "--output=yaml", runnerMock.params[2]) 181 assert.Equal(t, `--patch={"spec":{"template":{"spec":{"containers":[{"name":"myContainer","image":"myregistry.com/myFancyContainer:1337"}]}}}}`, runnerMock.params[3]) 182 assert.True(t, strings.Contains(runnerMock.params[4], filepath.Join("dir1/dir2/depl.yaml"))) 183 }) 184 185 t.Run("DeploymentName not used for kubectl", func(t *testing.T) { 186 t.Parallel() 187 var configuration = *validConfiguration 188 configuration.DeploymentName = "DeploymentName" 189 190 gitUtilsMock := &gitUtilsMock{} 191 runnerMock := &gitOpsExecRunnerMock{} 192 runnerMock.expectedYaml = expectedYaml 193 194 err := runGitopsUpdateDeployment(&configuration, runnerMock, gitUtilsMock, &filesMock{}) 195 assert.NoError(t, err) 196 assert.Equal(t, configuration.BranchName, gitUtilsMock.changedBranch) 197 assert.Len(t, gitUtilsMock.savedFiles, 1) 198 assert.Equal(t, expectedYaml, gitUtilsMock.savedFiles[0]) 199 assert.Equal(t, "kubectl", runnerMock.executable) 200 assert.Equal(t, "patch", runnerMock.params[0]) 201 assert.Equal(t, "--local", runnerMock.params[1]) 202 assert.Equal(t, "--output=yaml", runnerMock.params[2]) 203 assert.Equal(t, `--patch={"spec":{"template":{"spec":{"containers":[{"name":"myContainer","image":"myregistry.com/myFancyContainer:1337"}]}}}}`, runnerMock.params[3]) 204 assert.True(t, strings.Contains(runnerMock.params[4], filepath.Join("dir1/dir2/depl.yaml"))) 205 }) 206 t.Run("successful run with glob", func(t *testing.T) { 207 t.Parallel() 208 gitUtilsMock := &gitUtilsMock{} 209 runnerMock := &gitOpsExecRunnerMock{} 210 fsMock := &filesMock{} 211 runnerMock.expectedYaml = expectedYaml 212 var configuration = *validConfiguration 213 configuration.FilePath = "glob/kubectl/**/*.yaml" 214 215 err := runGitopsUpdateDeployment(&configuration, runnerMock, gitUtilsMock, fsMock) 216 assert.NoError(t, err) 217 assert.Equal(t, validConfiguration.BranchName, gitUtilsMock.changedBranch) 218 assert.Len(t, gitUtilsMock.savedFiles, 2) 219 assert.Equal(t, expectedYaml, gitUtilsMock.savedFiles[0]) 220 assert.Equal(t, expectedYaml, gitUtilsMock.savedFiles[1]) 221 222 assert.Equal(t, "kubectl", runnerMock.executable) 223 assert.Equal(t, "patch", runnerMock.params[0]) 224 assert.Equal(t, "--local", runnerMock.params[1]) 225 assert.Equal(t, "--output=yaml", runnerMock.params[2]) 226 assert.Equal(t, `--patch={"spec":{"template":{"spec":{"containers":[{"name":"myContainer","image":"myregistry.com/myFancyContainer:1337"}]}}}}`, runnerMock.params[3]) 227 assert.True(t, strings.Contains(runnerMock.params[4], filepath.Join("glob/kubectl/dir1/depl.yaml"))) 228 229 assert.Equal(t, "patch", runnerMock.params[5]) 230 assert.Equal(t, "--local", runnerMock.params[6]) 231 assert.Equal(t, "--output=yaml", runnerMock.params[7]) 232 assert.Equal(t, `--patch={"spec":{"template":{"spec":{"containers":[{"name":"myContainer","image":"myregistry.com/myFancyContainer:1337"}]}}}}`, runnerMock.params[8]) 233 assert.True(t, strings.Contains(runnerMock.params[9], filepath.Join("glob/kubectl/dir2/depl.yaml"))) 234 }) 235 236 t.Run("missing ContainerName", func(t *testing.T) { 237 t.Parallel() 238 var configuration = *validConfiguration 239 configuration.ContainerName = "" 240 241 gitUtilsMock := &gitUtilsMock{} 242 runnerMock := &gitOpsExecRunnerMock{} 243 244 err := runGitopsUpdateDeployment(&configuration, runnerMock, gitUtilsMock, &filesMock{}) 245 assert.EqualError(t, err, "missing required fields for kubectl: the following parameters are necessary for kubectl: [containerName]") 246 }) 247 248 t.Run("error on kubectl execution", func(t *testing.T) { 249 t.Parallel() 250 runner := &gitOpsExecRunnerMock{failOnRunExecutable: true} 251 252 err := runGitopsUpdateDeployment(validConfiguration, runner, &gitUtilsMock{}, &filesMock{}) 253 assert.EqualError(t, err, "error on kubectl execution: failed to apply kubectl command: failed to apply kubectl command: error happened") 254 }) 255 256 t.Run("invalid URL", func(t *testing.T) { 257 t.Parallel() 258 var configuration = *validConfiguration 259 configuration.ContainerRegistryURL = "//myregistry.com/registry/containers" 260 261 err := runGitopsUpdateDeployment(&configuration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, &filesMock{}) 262 assert.EqualError(t, err, "error on kubectl execution: failed to apply kubectl command: registry URL could not be extracted: invalid registry url") 263 }) 264 265 t.Run("error on plain clone", func(t *testing.T) { 266 t.Parallel() 267 gitUtils := &gitUtilsMock{failOnClone: true} 268 269 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{expectedYaml: expectedYaml}, gitUtils, &filesMock{}) 270 assert.EqualError(t, err, "repository could not get prepared: failed to plain clone repository: error on clone") 271 }) 272 273 t.Run("error on change branch", func(t *testing.T) { 274 t.Parallel() 275 gitUtils := &gitUtilsMock{failOnChangeBranch: true} 276 277 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, gitUtils, &filesMock{}) 278 assert.EqualError(t, err, "repository could not get prepared: failed to change branch: error on change branch") 279 }) 280 281 t.Run("error on commit changes", func(t *testing.T) { 282 t.Parallel() 283 gitUtils := &gitUtilsMock{failOnCommit: true} 284 285 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, gitUtils, &filesMock{}) 286 assert.EqualError(t, err, "failed to commit and push changes: committing changes failed: error on commit") 287 }) 288 289 t.Run("error on push commits", func(t *testing.T) { 290 t.Parallel() 291 gitUtils := &gitUtilsMock{failOnPush: true} 292 293 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, gitUtils, &filesMock{}) 294 assert.EqualError(t, err, "failed to commit and push changes: pushing changes failed: error on push") 295 }) 296 297 t.Run("error on temp dir creation", func(t *testing.T) { 298 t.Parallel() 299 fileUtils := &filesMock{failOnCreation: true} 300 301 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, fileUtils) 302 assert.EqualError(t, err, "failed to create temporary directory: error appeared") 303 }) 304 305 t.Run("error on file write", func(t *testing.T) { 306 t.Parallel() 307 fileUtils := &filesMock{failOnWrite: true} 308 309 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{expectedYaml: expectedYaml}, &gitUtilsMock{}, fileUtils) 310 assert.EqualError(t, err, "failed to write file: error appeared") 311 }) 312 313 t.Run("error on temp dir deletion", func(t *testing.T) { 314 t.Parallel() 315 fileUtils := &filesMock{failOnDeletion: true} 316 317 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, fileUtils) 318 assert.NoError(t, err) 319 _ = piperutils.Files{}.RemoveAll(fileUtils.path) 320 }) 321 } 322 323 func TestRunGitopsUpdateDeploymentWithInvalid(t *testing.T) { 324 t.Parallel() 325 t.Run("invalid deploy tool is not supported", func(t *testing.T) { 326 var configuration = &gitopsUpdateDeploymentOptions{ 327 BranchName: "main", 328 CommitMessage: "This is the commit message", 329 ServerURL: "https://github.com", 330 Username: "admin3", 331 Password: "validAccessToken", 332 FilePath: "dir1/dir2/depl.yaml", 333 ContainerName: "myContainer", 334 ContainerRegistryURL: "https://myregistry.com", 335 ContainerImageNameTag: "registry/containers/myFancyContainer:1337", 336 Tool: "invalid", 337 ChartPath: "./helm", 338 DeploymentName: "myFancyDeployment", 339 HelmValues: []string{"./helm/additionalValues.yaml"}, 340 } 341 342 err := runGitopsUpdateDeployment(configuration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, &filesMock{}) 343 assert.EqualError(t, err, "tool invalid is not supported") 344 }) 345 } 346 347 func TestRunGitopsUpdateDeploymentWithHelm(t *testing.T) { 348 var validConfiguration = &gitopsUpdateDeploymentOptions{ 349 BranchName: "main", 350 CommitMessage: "This is the commit message", 351 ServerURL: "https://github.com", 352 Username: "admin3", 353 Password: "validAccessToken", 354 FilePath: "dir1/dir2/depl.yaml", 355 ContainerRegistryURL: "https://myregistry.com", 356 ContainerImageNameTag: "registry/containers/myFancyContainer:1337", 357 Tool: "helm", 358 ChartPath: "./helm", 359 DeploymentName: "myFancyDeployment", 360 HelmValues: []string{"./helm/additionalValues.yaml"}, 361 } 362 363 t.Parallel() 364 t.Run("successful run", func(t *testing.T) { 365 t.Parallel() 366 gitUtilsMock := &gitUtilsMock{} 367 runnerMock := &gitOpsExecRunnerMock{} 368 runnerMock.expectedYaml = expectedYaml 369 370 err := runGitopsUpdateDeployment(validConfiguration, runnerMock, gitUtilsMock, &filesMock{}) 371 assert.NoError(t, err) 372 assert.Equal(t, validConfiguration.BranchName, gitUtilsMock.changedBranch) 373 assert.Len(t, gitUtilsMock.savedFiles, 1) 374 assert.Equal(t, "---\n"+expectedYaml, gitUtilsMock.savedFiles[0]) 375 assert.Equal(t, "This is the commit message", gitUtilsMock.commitMessage) 376 assert.Equal(t, "helm", runnerMock.executable) 377 assert.Equal(t, "template", runnerMock.params[0]) 378 assert.Equal(t, "myFancyDeployment", runnerMock.params[1]) 379 assert.Equal(t, filepath.Join(gitUtilsMock.temporaryDirectory, "helm"), runnerMock.params[2]) 380 assert.Equal(t, "--set=image.repository=myregistry.com/registry/containers/myFancyContainer", runnerMock.params[3]) 381 assert.Equal(t, "--set=image.tag=1337", runnerMock.params[4]) 382 assert.Equal(t, "--values", runnerMock.params[5]) 383 assert.Equal(t, "./helm/additionalValues.yaml", runnerMock.params[6]) 384 }) 385 386 t.Run("default commit message", func(t *testing.T) { 387 t.Parallel() 388 var configuration = *validConfiguration 389 configuration.CommitMessage = "" 390 391 gitUtilsMock := &gitUtilsMock{} 392 runnerMock := &gitOpsExecRunnerMock{} 393 runnerMock.expectedYaml = expectedYaml 394 395 err := runGitopsUpdateDeployment(&configuration, runnerMock, gitUtilsMock, &filesMock{}) 396 assert.NoError(t, err) 397 assert.Equal(t, configuration.BranchName, gitUtilsMock.changedBranch) 398 assert.Len(t, gitUtilsMock.savedFiles, 1) 399 assert.Equal(t, "---\n"+expectedYaml, gitUtilsMock.savedFiles[0]) 400 assert.Equal(t, "Updated myregistry.com/registry/containers/myFancyContainer to version 1337", gitUtilsMock.commitMessage) 401 assert.Equal(t, "helm", runnerMock.executable) 402 assert.Equal(t, "template", runnerMock.params[0]) 403 assert.Equal(t, "myFancyDeployment", runnerMock.params[1]) 404 assert.Equal(t, filepath.Join(gitUtilsMock.temporaryDirectory, "helm"), runnerMock.params[2]) 405 assert.Equal(t, "--set=image.repository=myregistry.com/registry/containers/myFancyContainer", runnerMock.params[3]) 406 assert.Equal(t, "--set=image.tag=1337", runnerMock.params[4]) 407 assert.Equal(t, "--values", runnerMock.params[5]) 408 assert.Equal(t, "./helm/additionalValues.yaml", runnerMock.params[6]) 409 }) 410 411 t.Run("ContainerName not used for helm", func(t *testing.T) { 412 t.Parallel() 413 var configuration = *validConfiguration 414 configuration.ContainerName = "containerName" 415 416 gitUtilsMock := &gitUtilsMock{} 417 runnerMock := &gitOpsExecRunnerMock{} 418 runnerMock.expectedYaml = expectedYaml 419 420 err := runGitopsUpdateDeployment(&configuration, runnerMock, gitUtilsMock, &filesMock{}) 421 assert.NoError(t, err) 422 assert.Equal(t, configuration.BranchName, gitUtilsMock.changedBranch) 423 assert.Len(t, gitUtilsMock.savedFiles, 1) 424 assert.Equal(t, "---\n"+expectedYaml, gitUtilsMock.savedFiles[0]) 425 assert.Equal(t, "helm", runnerMock.executable) 426 assert.Equal(t, "template", runnerMock.params[0]) 427 assert.Equal(t, "myFancyDeployment", runnerMock.params[1]) 428 assert.Equal(t, filepath.Join(gitUtilsMock.temporaryDirectory, "helm"), runnerMock.params[2]) 429 assert.Equal(t, "--set=image.repository=myregistry.com/registry/containers/myFancyContainer", runnerMock.params[3]) 430 assert.Equal(t, "--set=image.tag=1337", runnerMock.params[4]) 431 assert.Equal(t, "--values", runnerMock.params[5]) 432 assert.Equal(t, "./helm/additionalValues.yaml", runnerMock.params[6]) 433 }) 434 435 t.Run("HelmValues is optional", func(t *testing.T) { 436 t.Parallel() 437 var configuration = *validConfiguration 438 configuration.HelmValues = nil 439 440 gitUtilsMock := &gitUtilsMock{} 441 runnerMock := &gitOpsExecRunnerMock{} 442 runnerMock.expectedYaml = expectedYaml 443 444 err := runGitopsUpdateDeployment(&configuration, runnerMock, gitUtilsMock, &filesMock{}) 445 assert.NoError(t, err) 446 assert.Equal(t, configuration.BranchName, gitUtilsMock.changedBranch) 447 assert.Len(t, gitUtilsMock.savedFiles, 1) 448 assert.Equal(t, "---\n"+expectedYaml, gitUtilsMock.savedFiles[0]) 449 assert.Equal(t, "helm", runnerMock.executable) 450 assert.Equal(t, "template", runnerMock.params[0]) 451 assert.Equal(t, "myFancyDeployment", runnerMock.params[1]) 452 assert.Equal(t, filepath.Join(gitUtilsMock.temporaryDirectory, "helm"), runnerMock.params[2]) 453 assert.Equal(t, "--set=image.repository=myregistry.com/registry/containers/myFancyContainer", runnerMock.params[3]) 454 assert.Equal(t, "--set=image.tag=1337", runnerMock.params[4]) 455 }) 456 t.Run("successful run with glob", func(t *testing.T) { 457 t.Parallel() 458 gitUtilsMock := &gitUtilsMock{} 459 runnerMock := &gitOpsExecRunnerMock{} 460 fsMock := &filesMock{} 461 runnerMock.expectedYaml = expectedYaml 462 var configuration = *validConfiguration 463 configuration.ChartPath = "glob/helm/dir*/helm" 464 configuration.HelmValues = nil 465 466 err := runGitopsUpdateDeployment(&configuration, runnerMock, gitUtilsMock, fsMock) 467 assert.NoError(t, err) 468 assert.Equal(t, validConfiguration.BranchName, gitUtilsMock.changedBranch) 469 assert.Len(t, gitUtilsMock.savedFiles, 1) 470 assert.Equal(t, "---\n"+expectedYaml+"---\n"+expectedYaml, gitUtilsMock.savedFiles[0]) 471 assert.Equal(t, "This is the commit message", gitUtilsMock.commitMessage) 472 assert.Equal(t, "helm", runnerMock.executable) 473 474 assert.Equal(t, "template", runnerMock.params[0]) 475 assert.Equal(t, "myFancyDeployment", runnerMock.params[1]) 476 assert.Equal(t, filepath.Join(gitUtilsMock.temporaryDirectory, "glob/helm/dir1/helm"), runnerMock.params[2]) 477 assert.Equal(t, "--set=image.repository=myregistry.com/registry/containers/myFancyContainer", runnerMock.params[3]) 478 assert.Equal(t, "--set=image.tag=1337", runnerMock.params[4]) 479 480 assert.Equal(t, "template", runnerMock.params[5]) 481 assert.Equal(t, "myFancyDeployment", runnerMock.params[6]) 482 assert.Equal(t, filepath.Join(gitUtilsMock.temporaryDirectory, "glob/helm/dir2/helm"), runnerMock.params[7]) 483 assert.Equal(t, "--set=image.repository=myregistry.com/registry/containers/myFancyContainer", runnerMock.params[8]) 484 assert.Equal(t, "--set=image.tag=1337", runnerMock.params[9]) 485 }) 486 487 t.Run("erroneous URL", func(t *testing.T) { 488 t.Parallel() 489 var configuration = *validConfiguration 490 configuration.ContainerRegistryURL = "://myregistry.com" 491 492 err := runGitopsUpdateDeployment(&configuration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, &filesMock{}) 493 assert.EqualError(t, err, `failed to apply helm command: failed to extract registry URL, image name, and image tag: registry URL could not be extracted: invalid registry url: parse "://myregistry.com": missing protocol scheme`) 494 }) 495 496 t.Run("missing ChartPath", func(t *testing.T) { 497 t.Parallel() 498 var configuration = *validConfiguration 499 configuration.ChartPath = "" 500 501 err := runGitopsUpdateDeployment(&configuration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, &filesMock{}) 502 assert.EqualError(t, err, "missing required fields for helm: the following parameters are necessary for helm: [chartPath]") 503 }) 504 505 t.Run("missing DeploymentName", func(t *testing.T) { 506 t.Parallel() 507 var configuration = *validConfiguration 508 configuration.DeploymentName = "" 509 510 err := runGitopsUpdateDeployment(&configuration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, &filesMock{}) 511 assert.EqualError(t, err, "missing required fields for helm: the following parameters are necessary for helm: [deploymentName]") 512 }) 513 514 t.Run("missing DeploymentName and ChartPath", func(t *testing.T) { 515 t.Parallel() 516 var configuration = *validConfiguration 517 configuration.DeploymentName = "" 518 configuration.ChartPath = "" 519 520 err := runGitopsUpdateDeployment(&configuration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, &filesMock{}) 521 assert.EqualError(t, err, "missing required fields for helm: the following parameters are necessary for helm: [chartPath deploymentName]") 522 }) 523 524 t.Run("erroneous tag", func(t *testing.T) { 525 t.Parallel() 526 var configuration = *validConfiguration 527 configuration.ContainerImageNameTag = "registry/containers/myFancyContainer:" 528 529 err := runGitopsUpdateDeployment(&configuration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, &filesMock{}) 530 assert.EqualError(t, err, "failed to apply helm command: failed to extract registry URL, image name, and image tag: tag could not be extracted") 531 }) 532 533 t.Run("erroneous image name", func(t *testing.T) { 534 t.Parallel() 535 var configuration = *validConfiguration 536 configuration.ContainerImageNameTag = ":1.0.1" 537 538 err := runGitopsUpdateDeployment(&configuration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, &filesMock{}) 539 assert.EqualError(t, err, "failed to apply helm command: failed to extract registry URL, image name, and image tag: image name could not be extracted") 540 }) 541 542 t.Run("error on helm execution", func(t *testing.T) { 543 t.Parallel() 544 runner := &gitOpsExecRunnerMock{failOnRunExecutable: true} 545 546 err := runGitopsUpdateDeployment(validConfiguration, runner, &gitUtilsMock{}, &filesMock{}) 547 assert.EqualError(t, err, "failed to apply helm command: failed to execute helm command: error happened") 548 }) 549 550 t.Run("error on plain clone", func(t *testing.T) { 551 t.Parallel() 552 gitUtils := &gitUtilsMock{failOnClone: true} 553 554 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, gitUtils, &filesMock{}) 555 assert.EqualError(t, err, "repository could not get prepared: failed to plain clone repository: error on clone") 556 }) 557 558 t.Run("error on change branch", func(t *testing.T) { 559 t.Parallel() 560 gitUtils := &gitUtilsMock{failOnChangeBranch: true} 561 562 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, gitUtils, &filesMock{}) 563 assert.EqualError(t, err, "repository could not get prepared: failed to change branch: error on change branch") 564 }) 565 566 t.Run("error on commit changes", func(t *testing.T) { 567 t.Parallel() 568 gitUtils := &gitUtilsMock{failOnCommit: true} 569 570 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, gitUtils, &filesMock{}) 571 assert.EqualError(t, err, "failed to commit and push changes: committing changes failed: error on commit") 572 }) 573 574 t.Run("error on push commits", func(t *testing.T) { 575 t.Parallel() 576 gitUtils := &gitUtilsMock{failOnPush: true} 577 578 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, gitUtils, &filesMock{}) 579 assert.EqualError(t, err, "failed to commit and push changes: pushing changes failed: error on push") 580 }) 581 582 t.Run("error on temp dir creation", func(t *testing.T) { 583 t.Parallel() 584 fileUtils := &filesMock{failOnCreation: true} 585 586 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, fileUtils) 587 assert.EqualError(t, err, "failed to create temporary directory: error appeared") 588 }) 589 590 t.Run("error on file write", func(t *testing.T) { 591 t.Parallel() 592 fileUtils := &filesMock{failOnWrite: true} 593 594 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, fileUtils) 595 assert.EqualError(t, err, "failed to write file: error appeared") 596 }) 597 598 t.Run("error on temp dir deletion", func(t *testing.T) { 599 t.Parallel() 600 fileUtils := &filesMock{failOnDeletion: true} 601 602 err := runGitopsUpdateDeployment(validConfiguration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, fileUtils) 603 assert.NoError(t, err) 604 _ = piperutils.Files{}.RemoveAll(fileUtils.path) 605 }) 606 } 607 608 func TestRunGitopsUpdateDeploymentWithKustomize(t *testing.T) { 609 var validConfiguration = &gitopsUpdateDeploymentOptions{ 610 BranchName: "main", 611 CommitMessage: "This is the commit message", 612 ServerURL: "https://github.com", 613 Username: "admin3", 614 Password: "validAccessToken", 615 FilePath: "kustomization.yaml", 616 ContainerRegistryURL: "https://myregistry.com", 617 ContainerImageNameTag: "containers/myFancyContainer:1337", 618 Tool: "kustomize", 619 DeploymentName: "myFancyDeployment", 620 } 621 622 t.Parallel() 623 t.Run("successful run", func(t *testing.T) { 624 t.Parallel() 625 gitUtilsMock := &gitUtilsMock{} 626 runnerMock := &gitOpsExecRunnerMock{} 627 fsMock := &filesMock{} 628 runnerMock.expectedYaml = expectedKustomize 629 630 err := runGitopsUpdateDeployment(validConfiguration, runnerMock, gitUtilsMock, fsMock) 631 assert.NoError(t, err) 632 assert.Equal(t, validConfiguration.BranchName, gitUtilsMock.changedBranch) 633 assert.Len(t, gitUtilsMock.savedFiles, 1) 634 assert.Equal(t, expectedKustomize, gitUtilsMock.savedFiles[0]) 635 assert.Equal(t, "This is the commit message", gitUtilsMock.commitMessage) 636 assert.Equal(t, "kustomize", runnerMock.executable) 637 assert.Equal(t, "edit", runnerMock.params[0]) 638 assert.Equal(t, "set", runnerMock.params[1]) 639 assert.Equal(t, "image", runnerMock.params[2]) 640 assert.Equal(t, "myFancyDeployment=myregistry.com/containers/myFancyContainer:1337", runnerMock.params[3]) 641 }) 642 t.Run("successful run with glob", func(t *testing.T) { 643 t.Parallel() 644 gitUtilsMock := &gitUtilsMock{} 645 runnerMock := &gitOpsExecRunnerMock{} 646 fsMock := &filesMock{} 647 runnerMock.expectedYaml = expectedKustomize 648 var configuration = *validConfiguration 649 configuration.FilePath = "glob/kustomize/**/*.yaml" 650 651 err := runGitopsUpdateDeployment(&configuration, runnerMock, gitUtilsMock, fsMock) 652 assert.NoError(t, err) 653 assert.Equal(t, validConfiguration.BranchName, gitUtilsMock.changedBranch) 654 assert.Len(t, gitUtilsMock.savedFiles, 2) 655 assert.Equal(t, expectedKustomize, gitUtilsMock.savedFiles[0]) 656 assert.Equal(t, expectedKustomize, gitUtilsMock.savedFiles[1]) 657 assert.Equal(t, "This is the commit message", gitUtilsMock.commitMessage) 658 assert.Equal(t, "kustomize", runnerMock.executable) 659 assert.Equal(t, "edit", runnerMock.params[0]) 660 assert.Equal(t, "set", runnerMock.params[1]) 661 assert.Equal(t, "image", runnerMock.params[2]) 662 assert.Equal(t, "myFancyDeployment=myregistry.com/containers/myFancyContainer:1337", runnerMock.params[3]) 663 assert.Equal(t, "edit", runnerMock.params[4]) 664 assert.Equal(t, "set", runnerMock.params[5]) 665 assert.Equal(t, "image", runnerMock.params[6]) 666 assert.Equal(t, "myFancyDeployment=myregistry.com/containers/myFancyContainer:1337", runnerMock.params[7]) 667 }) 668 t.Run("with forcePush", func(t *testing.T) { 669 t.Parallel() 670 runner := &gitOpsExecRunnerMock{} 671 validConfiguration.ForcePush = true 672 gitUtilsMock := &gitUtilsMock{forcePush: true} 673 674 err := runGitopsUpdateDeployment(validConfiguration, runner, gitUtilsMock, &filesMock{}) 675 assert.NoError(t, err) 676 assert.Equal(t, "This is the commit message", gitUtilsMock.commitMessage) 677 }) 678 679 t.Run("error on kustomize execution", func(t *testing.T) { 680 t.Parallel() 681 runner := &gitOpsExecRunnerMock{failOnRunExecutable: true} 682 683 err := runGitopsUpdateDeployment(validConfiguration, runner, &gitUtilsMock{}, &filesMock{}) 684 assert.EqualError(t, err, "failed to apply kustomize command: failed to execute kustomize command: error happened") 685 }) 686 687 t.Run("missing FilePath", func(t *testing.T) { 688 t.Parallel() 689 var configuration = *validConfiguration 690 configuration.FilePath = "" 691 692 err := runGitopsUpdateDeployment(&configuration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, &filesMock{}) 693 assert.EqualError(t, err, "missing required fields for kustomize: the following parameters are necessary for kustomize: [filePath]") 694 }) 695 696 t.Run("missing DeploymentName", func(t *testing.T) { 697 t.Parallel() 698 var configuration = *validConfiguration 699 configuration.DeploymentName = "" 700 701 err := runGitopsUpdateDeployment(&configuration, &gitOpsExecRunnerMock{}, &gitUtilsMock{}, &filesMock{}) 702 assert.EqualError(t, err, "missing required fields for kustomize: the following parameters are necessary for kustomize: [deploymentName]") 703 }) 704 } 705 706 func TestRunGitopsUpdateDeploymentWithGlobbing(t *testing.T) { 707 var validConfiguration = &gitopsUpdateDeploymentOptions{ 708 Tool: toolKubectl, 709 ContainerName: "yes", 710 DeploymentName: "myFancyDeployment", 711 } 712 713 t.Run("globbing fails", func(t *testing.T) { 714 t.Parallel() 715 gitUtilsMock := &gitUtilsMock{} 716 runnerMock := &gitOpsExecRunnerMock{} 717 fsMock := &filesMock{failOnGlob: true} 718 719 err := runGitopsUpdateDeployment(validConfiguration, runnerMock, gitUtilsMock, fsMock) 720 assert.EqualError(t, err, "unable to expand globbing pattern: error appeared") 721 }) 722 t.Run("globbing finds 0 files", func(t *testing.T) { 723 t.Parallel() 724 gitUtilsMock := &gitUtilsMock{skipClone: true} 725 runnerMock := &gitOpsExecRunnerMock{} 726 fsMock := &filesMock{} 727 var config = *validConfiguration 728 config.FilePath = "xxx" 729 730 err := runGitopsUpdateDeployment(&config, runnerMock, gitUtilsMock, fsMock) 731 assert.EqualError(t, err, "no matching files found for provided globbing pattern") 732 }) 733 } 734 735 type gitOpsExecRunnerMock struct { 736 out io.Writer 737 params []string 738 executable string 739 failOnRunExecutable bool 740 dir string 741 expectedYaml string 742 } 743 744 func (e *gitOpsExecRunnerMock) Stdout(out io.Writer) { 745 e.out = out 746 } 747 748 func (gitOpsExecRunnerMock) Stderr(io.Writer) { 749 panic("implement me") 750 } 751 752 func (e *gitOpsExecRunnerMock) SetDir(d string) { 753 e.dir = d 754 } 755 756 func (e *gitOpsExecRunnerMock) RunExecutable(executable string, params ...string) error { 757 if e.failOnRunExecutable { 758 return errors.New("error happened") 759 } 760 e.executable = executable 761 e.params = append(e.params, params...) 762 if executable == "kustomize" { 763 return fileUtils.FileWrite(filepath.Join(e.dir, "kustomization.yaml"), []byte(e.expectedYaml), 0755) 764 765 } else { 766 _, err := e.out.Write([]byte(e.expectedYaml)) 767 return err 768 } 769 } 770 771 type filesMock struct { 772 failOnCreation bool 773 failOnDeletion bool 774 failOnWrite bool 775 failOnRead bool 776 failOnGlob bool 777 path string 778 } 779 780 func (f filesMock) FileWrite(path string, content []byte, perm os.FileMode) error { 781 if f.failOnWrite { 782 return errors.New("error appeared") 783 } 784 return piperutils.Files{}.FileWrite(path, content, perm) 785 } 786 787 func (f filesMock) FileRead(path string) ([]byte, error) { 788 if f.failOnRead { 789 return []byte{}, errors.New("error appeared") 790 } 791 return piperutils.Files{}.FileRead(path) 792 } 793 794 func (f filesMock) TempDir(dir string, pattern string) (name string, err error) { 795 if f.failOnCreation { 796 return "", errors.New("error appeared") 797 } 798 return piperutils.Files{}.TempDir("", pattern) 799 } 800 801 func (f *filesMock) RemoveAll(path string) error { 802 if f.failOnDeletion { 803 f.path = path 804 return errors.New("error appeared") 805 } 806 return piperutils.Files{}.RemoveAll(path) 807 } 808 809 func (f *filesMock) Glob(pattern string) (matches []string, err error) { 810 if f.failOnGlob { 811 return nil, errors.New("error appeared") 812 } 813 return piperutils.Files{}.Glob(pattern) 814 } 815 816 type gitUtilsMock struct { 817 savedFiles []string 818 changedBranch string 819 commitMessage string 820 temporaryDirectory string 821 failOnClone bool 822 failOnChangeBranch bool 823 failOnCommit bool 824 failOnPush bool 825 skipClone bool 826 forcePush bool 827 } 828 829 func (gitUtilsMock) GetWorktree() (*git.Worktree, error) { 830 return nil, nil 831 } 832 833 func (v *gitUtilsMock) ChangeBranch(branchName string) error { 834 if v.failOnChangeBranch { 835 return errors.New("error on change branch") 836 } 837 v.changedBranch = branchName 838 return nil 839 } 840 841 func (v *gitUtilsMock) CommitFiles(newFiles []string, commitMessage string, _ string) (plumbing.Hash, error) { 842 if v.failOnCommit { 843 return [20]byte{}, errors.New("error on commit") 844 } 845 846 v.commitMessage = commitMessage 847 848 for _, newFile := range newFiles { 849 filepath := filepath.Join(v.temporaryDirectory, newFile) 850 fileContent, err := piperutils.Files{}.FileRead(filepath) 851 if err != nil { 852 return [20]byte{}, errors.New("could not find file " + filepath) 853 } 854 v.savedFiles = append(v.savedFiles, string(fileContent)) 855 } 856 return [20]byte{123}, nil 857 } 858 859 func (v gitUtilsMock) PushChangesToRepository(_ string, _ string, force *bool, caCerts []byte) error { 860 if v.failOnPush { 861 return errors.New("error on push") 862 } 863 if v.forcePush && !*force { 864 return errors.New("expected forcePush but not defined") 865 } 866 return nil 867 } 868 869 func (v *gitUtilsMock) PlainClone(_, _, _, _, directory string, caCerts []byte) error { 870 if v.skipClone { 871 return nil 872 } 873 if v.failOnClone { 874 return errors.New("error on clone") 875 } 876 v.temporaryDirectory = directory 877 878 err := piperutils.Files{}.MkdirAll(filepath.Join(directory, "dir1/dir2"), 0755) 879 if err != nil { 880 return err 881 } 882 err = piperutils.Files{}.FileWrite(filepath.Join(directory, "dir1/dir2/depl.yaml"), []byte(existingYaml), 0755) 883 err = piperutils.Files{}.MkdirAll(filepath.Join(directory, "glob/kubectl/dir1"), 0755) 884 err = piperutils.Files{}.MkdirAll(filepath.Join(directory, "glob/kubectl/dir2"), 0755) 885 err = piperutils.Files{}.FileWrite(filepath.Join(directory, "glob/kubectl/dir1/depl.yaml"), []byte(existingYaml), 0755) 886 err = piperutils.Files{}.FileWrite(filepath.Join(directory, "glob/kubectl/dir2/depl.yaml"), []byte(existingYaml), 0755) 887 888 err = piperutils.Files{}.MkdirAll(filepath.Join(directory, "helm"), 0755) 889 err = piperutils.Files{}.MkdirAll(filepath.Join(directory, "glob/helm/dir1/helm"), 0755) 890 err = piperutils.Files{}.MkdirAll(filepath.Join(directory, "glob/helm/dir2/helm"), 0755) 891 892 err = piperutils.Files{}.FileWrite(filepath.Join(directory, "kustomization.yaml"), []byte(existingKustomize), 0755) 893 err = piperutils.Files{}.MkdirAll(filepath.Join(directory, "glob/kustomize/dir1"), 0755) 894 err = piperutils.Files{}.MkdirAll(filepath.Join(directory, "glob/kustomize/dir2"), 0755) 895 err = piperutils.Files{}.FileWrite(filepath.Join(directory, "glob/kustomize/dir1/kustomization.yaml"), []byte(existingKustomize), 0755) 896 err = piperutils.Files{}.FileWrite(filepath.Join(directory, "glob/kustomize/dir2/kustomization.yaml"), []byte(existingKustomize), 0755) 897 return nil 898 } 899 900 var existingYaml = `apiVersion: apps/v1 901 kind: Deployment 902 metadata: 903 name: myFancyApp 904 labels: 905 tier: application 906 spec: 907 replicas: 4 908 selector: 909 matchLabels: 910 run: myContainer 911 template: 912 metadata: 913 labels: 914 run: myContainer 915 spec: 916 containers: 917 - image: myregistry.com/myFancyContainer:1336 918 name: myContainer` 919 920 var expectedYaml = `apiVersion: apps/v1 921 kind: Deployment 922 metadata: 923 name: myFancyApp 924 labels: 925 tier: application 926 spec: 927 replicas: 4 928 selector: 929 matchLabels: 930 run: myContainer 931 template: 932 metadata: 933 labels: 934 run: myContainer 935 spec: 936 containers: 937 - image: myregistry.com/myFancyContainer:1337 938 name: myContainer` 939 940 var existingKustomize = `apiVersion: kustomize.config.k8s.io/v1beta1 941 kind: Kustomization 942 943 images: 944 - name: myFancyDeployment 945 newTag: "0" 946 ` 947 var expectedKustomize = `apiVersion: kustomize.config.k8s.io/v1beta1 948 kind: Kustomization 949 950 images: 951 - name: myFancyDeployment 952 newName: registry/containers/myFancyContainer 953 newTag: "1337" 954 `