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