1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00
sap-jenkins-library/test/groovy/CommonStepsTest.groovy
Anil Keshav 8dc2a1bfb4
feat: Add imagePushToRegistry step (#4609)
* imagePushToRegistry new step

* adding copy and push functionality

* including only copy correctly

* groovy step for imagePushToRegistry

* create .docker folder

* imagePushToRegistry new step

* adding copy and push functionality

* including only copy correctly

* groovy step for imagePushToRegistry

* create .docker folder

* fix CopyImage

* test

* test

* Correct docker config path

* Update

* Update

* Update

* Update

* Update

* Use creds from Vault

* Use creds from Vault

* Use creds from Vault

* Use creds from Vault

* Test

* Comment some logic

* Test: move regexp logic

* Test

* Update

* Update

* Clean up

* Update

* Update

* Update interface

* Rename function

* imagePushToRegistry: small refactoring (#4688)

* imagePushToRegistry new step

* adding copy and push functionality

* including only copy correctly

* groovy step for imagePushToRegistry

* create .docker folder

* Correct docker config path

* Update

* Update

* Update

* Update

* Update

* Use creds from Vault

* Use creds from Vault

* Use creds from Vault

* Use creds from Vault

* Test

* Comment some logic

* Test: move regexp logic

* Test

* Update

* Update

* Clean up

* Update

* Update

---------

Co-authored-by: Keshav <anil.keshav@sap.com>
Co-authored-by: Muhammadali Nazarov <muhammadalinazarov@gmail.com>

* Update step yaml file

* Update interface

* Rename func

* Update tests

* Update interface, create mock methods, update tests

* Update mock

* Add md file

* Fix groovy doc, unit test, go unit test

* Update

* Add unit tests

* Support tagLatest param

* Fetch source creds from Vault

* Update yaml file

* Support multiple images

* Update test

* Support copy images in parallel

* Update yaml

* Clean up

* Return err if no creds provided

* Fix tests

* Add err msg

* Add debug log

* Do not use CPE for targetImages

* Support platform

* Delete Jenkins specific creds

* Update groovy: do not handle Jenkins creds

* Delete unused code

* Fix: Support platform

* Fix: Support platform

* Apply suggestion from code review

Co-authored-by: Egor Balakin <14162703+m1ron0xFF@users.noreply.github.com>

* Apply suggestion from code review

Co-authored-by: Egor Balakin <14162703+m1ron0xFF@users.noreply.github.com>

* Add tests for parseDockerImageName

* Add comment that tagArtifactVersion is not supported yet

* Set limit of running goroutines

* Fix: Set limit of running goroutines

* The tagArtifactVersion is not supported yet

---------

Co-authored-by: Muhammadali Nazarov <muhammadalinazarov@gmail.com>
Co-authored-by: Egor Balakin <egor.balakin@sap.com>
Co-authored-by: Vyacheslav Starostin <vyacheslav.starostin@sap.com>
Co-authored-by: Vyacheslav Starostin <32613074+vstarostin@users.noreply.github.com>
Co-authored-by: Egor Balakin <14162703+m1ron0xFF@users.noreply.github.com>
2023-11-30 15:06:31 +06:00

366 lines
18 KiB
Groovy

import static java.util.stream.Collectors.toList
import static org.hamcrest.Matchers.empty
import static org.hamcrest.Matchers.equalTo
import static org.hamcrest.Matchers.is
import static org.junit.Assert.assertThat
import static org.junit.Assert.fail
import static util.StepHelper.getSteps
import java.io.File
import java.util.stream.Collectors
import java.lang.reflect.Field
import org.codehaus.groovy.runtime.metaclass.MethodSelectionException
import org.hamcrest.Matchers
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import groovy.io.FileType
import hudson.AbortException
import util.BasePiperTest
import util.JenkinsReadYamlRule
import util.JenkinsStepRule
import util.Rules
/*
* Intended for collecting generic checks applied to all steps.
*/
public class CommonStepsTest extends BasePiperTest{
@Rule
public RuleChain ruleChain = Rules.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
/*
* With that test we ensure the very first action inside a method body of a call method
* for a not white listed step is the check for the script handed over properly.
* Actually we assert for the exception type (AbortException) and for the exception message.
* In case a new step is added this step will fail. It is the duty of the author of the
* step to either follow the pattern of checking the script first or to add the step
* to the white list.
*/
@Test
public void scriptReferenceNotHandedOverTest() {
// all steps not adopting the usual pattern of working with the script.
def whitelistScriptReference = [
'abapEnvironmentPipeline',
'buildSetResult',
'commonPipelineEnvironment',
'handlePipelineStepErrors',
'pipelineExecute',
'piperExecuteBin',
'piperPipeline',
'prepareDefaultValues',
'runClosures',
'setupCommonPipelineEnvironment',
]
List steps = getSteps().stream()
.filter {! whitelistScriptReference.contains(it)}
.forEach {checkReference(it)}
}
private void checkReference(step) {
try {
def script = loadScript("${step}.groovy")
try {
System.setProperty('com.sap.piper.featureFlag.failOnMissingScript', 'true')
try {
script.call([:])
} catch(AbortException | MissingMethodException e) {
throw e
} catch(Exception e) {
fail "Unexpected exception ${e.getClass().getName()} caught from step '${step}': ${e.getMessage()}"
}
fail("Expected AbortException not raised by step '${step}'")
} catch(MissingMethodException e) {
// can be improved: exception handling as some kind of control flow.
// we can also check for the methods and call the appropriate one.
try {
script.call([:]) {}
} catch(AbortException e1) {
throw e1
} catch(Exception e1) {
fail "Unexpected exception ${e1.getClass().getName()} caught from step '${step}': ${e1.getMessage()}"
}
fail("Expected AbortException not raised by step '${step}'")
}
} catch(AbortException e) {
assertThat("Step ''${step} does not fail with expected error message in case mandatory parameter 'script' is not provided.",
e.getMessage() ==~ /.*\[ERROR\]\[.*\] No reference to surrounding script provided with key 'script', e.g. 'script: this'./,
is(equalTo(true)))
} finally {
System.clearProperty('com.sap.piper.featureFlag.failOnMissingScript')
}
}
private static fieldRelatedWhitelist = [
'abapAddonAssemblyKitCheckCVs', //implementing new golang pattern without fields
'abapAddonAssemblyKitCheckPV', //implementing new golang pattern without fields
'abapAddonAssemblyKitCreateTargetVector', //implementing new golang pattern without fields
'abapAddonAssemblyKitPublishTargetVector', //implementing new golang pattern without fields
'abapAddonAssemblyKitRegisterPackages', //implementing new golang pattern without fields
'abapAddonAssemblyKitReleasePackages', //implementing new golang pattern without fields
'abapAddonAssemblyKitReserveNextPackages', //implementing new golang pattern without fields
'abapEnvironmentBuild', //implementing new golang pattern without fields
'abapEnvironmentAssemblePackages', //implementing new golang pattern without fields
'abapEnvironmentAssembleConfirm', //implementing new golang pattern without fields
'abapEnvironmentCheckoutBranch', //implementing new golang pattern without fields
'abapEnvironmentCloneGitRepo', //implementing new golang pattern without fields
'abapEnvironmentCreateTag', //implementing new golang pattern without fields
'abapEnvironmentPullGitRepo', //implementing new golang pattern without fields
'abapEnvironmentPipeline', // special step (infrastructure)
'abapEnvironmentRunATCCheck', //implementing new golang pattern without fields
'abapEnvironmentRunAUnitTest', //implementing new golang pattern without fields
'abapEnvironmentCreateSystem', //implementing new golang pattern without fields
'abapEnvironmentPushATCSystemConfig', //implementing new golang pattern without fields
'artifactPrepareVersion',
'cloudFoundryCreateService', //implementing new golang pattern without fields
'cloudFoundryCreateServiceKey', //implementing new golang pattern without fields
'cloudFoundryCreateSpace', //implementing new golang pattern without fields
'cloudFoundryDeleteService', //implementing new golang pattern without fields
'cloudFoundryDeleteSpace', //implementing new golang pattern without fields
'cloudFoundryDeploy', //implementing new golang pattern without fields
'cnbBuild', //implementing new golang pattern without fields
'durationMeasure', // only expects parameters via signature
'prepareDefaultValues', // special step (infrastructure)
'piperPipeline', // special step (infrastructure)
'pipelineStashFilesAfterBuild', // intended to be called from pipelineStashFiles
'pipelineStashFilesBeforeBuild', // intended to be called from pipelineStashFiles
'pipelineStashFiles', // only forwards to before/after step
'pipelineExecute', // special step (infrastructure)
'commonPipelineEnvironment', // special step (infrastructure)
'handlePipelineStepErrors', // special step (infrastructure)
'piperStageWrapper', //intended to be called from within stages
'buildSetResult',
'runClosures',
'checkmarxExecuteScan', //implementing new golang pattern without fields
'checkmarxOneExecuteScan', //implementing new golang pattern without fields
'githubCreateIssue', //implementing new golang pattern without fields
'githubCreatePullRequest', //implementing new golang pattern without fields
'githubPublishRelease', //implementing new golang pattern without fields
'githubCheckBranchProtection', //implementing new golang pattern without fields
'githubCommentIssue', //implementing new golang pattern without fields
'githubSetCommitStatus', //implementing new golang pattern without fields
'kubernetesDeploy', //implementing new golang pattern without fields
'piperExecuteBin', //implementing new golang pattern without fields
'protecodeExecuteScan', //implementing new golang pattern without fields
'xsDeploy', //implementing new golang pattern without fields
'npmExecuteScripts', //implementing new golang pattern without fields
'npmExecuteLint', //implementing new golang pattern without fields
'malwareExecuteScan', //implementing new golang pattern without fields
'mavenBuild', //implementing new golang pattern without fields
'mavenExecute', //implementing new golang pattern without fields
'mavenExecuteIntegration', //implementing new golang pattern without fields
'mavenExecuteStaticCodeChecks', //implementing new golang pattern without fields
'mtaBuild', //implementing new golang pattern without fields
'nexusUpload', //implementing new golang pattern without fields
'piperPipelineStageArtifactDeployment', //stage without step flags
'pipelineCreateScanSummary', //stage without step flags
'sonarExecuteScan', //implementing new golang pattern without fields
'gctsCreateRepository', //implementing new golang pattern without fields
'gctsRollback', //implementing new golang pattern without fields
'gctsExecuteABAPQualityChecks', //implementing new golang pattern without fields
'gctsExecuteABAPUnitTests', //implementing new golang pattern without fields
'gctsCloneRepository', //implementing new golang pattern without fields
'codeqlExecuteScan', //implementing new golang pattern without fields
'credentialdiggerScan', //implementing new golang pattern without fields
'fortifyExecuteScan', //implementing new golang pattern without fields
'gctsDeploy', //implementing new golang pattern without fields
'containerSaveImage', //implementing new golang pattern without fields
'detectExecuteScan', //implementing new golang pattern without fields
'kanikoExecute', //implementing new golang pattern without fields
'karmaExecuteTests', //implementing new golang pattern without fields
'gitopsUpdateDeployment', //implementing new golang pattern without fields
'vaultRotateSecretId', //implementing new golang pattern without fields
'deployIntegrationArtifact', //implementing new golang pattern without fields
'newmanExecute', //implementing new golang pattern without fields
'terraformExecute', //implementing new golang pattern without fields
'whitesourceExecuteScan', //implementing new golang pattern without fields
'uiVeri5ExecuteTests', //implementing new golang pattern without fields
'integrationArtifactDeploy', //implementing new golang pattern without fields
'integrationArtifactUpdateConfiguration', //implementing new golang pattern without fields
'integrationArtifactGetMplStatus', //implementing new golang pattern without fields
'integrationArtifactGetServiceEndpoint', //implementing new golang pattern without fields
'integrationArtifactDownload', //implementing new golang pattern without fields
'integrationArtifactUpload', //implementing new golang pattern without fields
'integrationArtifactTransport', //implementing new golang pattern without fields
'integrationArtifactTriggerIntegrationTest', //implementing new golang pattern without fields
'integrationArtifactUnDeploy', //implementing new golang pattern without fields
'integrationArtifactResource', //implementing new golang pattern without fields
'containerExecuteStructureTests', //implementing new golang pattern without fields
'transportRequestUploadSOLMAN', //implementing new golang pattern without fields
'transportRequestReqIDFromGit', //implementing new golang pattern without fields
'transportRequestDocIDFromGit', //implementing new golang pattern without fields
'gaugeExecuteTests', //implementing new golang pattern without fields
'batsExecuteTests', //implementing new golang pattern without fields
'transportRequestUploadRFC', //implementing new golang pattern without fields
'writePipelineEnv', //implementing new golang pattern without fields
'readPipelineEnv', //implementing new golang pattern without fields
'transportRequestUploadCTS', //implementing new golang pattern without fields
'isChangeInDevelopment', //implementing new golang pattern without fields
'golangBuild', //implementing new golang pattern without fields
'helmExecute', //implementing new golang pattern without fields
'apiProxyDownload', //implementing new golang pattern without fields
'apiKeyValueMapDownload', //implementing new golang pattern without fields
'apiProviderDownload', //implementing new golang pattern without fields
'apiProxyUpload', //implementing new golang pattern without fields
'gradleExecuteBuild', //implementing new golang pattern without fields
'shellExecute', //implementing new golang pattern without fields
'apiKeyValueMapUpload', //implementing new golang pattern without fields
'apiProviderUpload', //implementing new golang pattern without fields
'pythonBuild', //implementing new golang pattern without fields
'awsS3Upload',
'apiProxyList', //implementing new golang pattern without fields
'azureBlobUpload',
'awsS3Upload',
'ansSendEvent',
'apiProviderList', //implementing new golang pattern without fields
'tmsUpload',
'tmsExport',
'imagePushToRegistry',
]
@Test
public void generalConfigKeysSetPresentTest() {
def fieldName = 'GENERAL_CONFIG_KEYS'
// the steps added to the fieldRelatedWhitelist do not take the general config at all
def stepsWithoutGeneralConfigKeySet = fieldCheck(fieldName, fieldRelatedWhitelist.plus(['gaugeExecuteTests',
'pipelineRestartSteps']))
assertThat("Steps without ${fieldName} field (or that field is not a Set): ${stepsWithoutGeneralConfigKeySet}",
stepsWithoutGeneralConfigKeySet, is(empty()))
}
@Test
public void stepConfigKeysSetPresentTest() {
def fieldName = 'STEP_CONFIG_KEYS'
def stepsWithoutStepConfigKeySet = fieldCheck(fieldName, fieldRelatedWhitelist.plus('setupCommonPipelineEnvironment'))
assertThat("Steps without ${fieldName} field (or that field is not a Set): ${stepsWithoutStepConfigKeySet}",
stepsWithoutStepConfigKeySet, is(empty()))
}
@Test
public void parametersKeysSetPresentTest() {
def fieldName = 'PARAMETER_KEYS'
def stepsWithoutParametersKeySet = fieldCheck(fieldName, fieldRelatedWhitelist.plus('setupCommonPipelineEnvironment'))
assertThat("Steps without ${fieldName} field (or that field is not a Set): ${stepsWithoutParametersKeySet}",
stepsWithoutParametersKeySet, is(empty()))
}
private fieldCheck(fieldName, whitelist) {
def stepsWithoutGeneralConfigKeySet = []
for(def step in getSteps()) {
if(whitelist.contains(step)) continue
def fields = loadScript("${step}.groovy").getClass().getDeclaredFields() as Set
Field generalConfigKeyField = fields.find{ it.getName() == fieldName}
if(! generalConfigKeyField ||
! generalConfigKeyField
.getType()
.isAssignableFrom(Set.class)) {
stepsWithoutGeneralConfigKeySet.add(step)
}
}
return stepsWithoutGeneralConfigKeySet
}
@Test
public void stepsWithWrongFieldNameTest() {
def whitelist = [
'abapEnvironmentPipeline',
'commonPipelineEnvironment',
'piperPipeline',
'piperExecuteBin',
'buildSetResult',
'runClosures'
]
def stepsWithWrongStepName = []
for(def step in getSteps()) {
if(whitelist.contains(step)) continue
def script = loadScript("${step}.groovy")
def fields = script.getClass().getDeclaredFields() as Set
Field stepNameField = fields.find { it.getName() == 'STEP_NAME'}
if(! stepNameField) {
stepsWithWrongStepName.add(step)
continue
}
boolean notAccessible = false
def fieldName
if(!stepNameField.isAccessible()) {
stepNameField.setAccessible(true)
notAccessible = true
}
try {
fieldName = stepNameField.get(script)
} finally {
if(notAccessible) stepNameField.setAccessible(false)
}
if(fieldName != step) {
stepsWithWrongStepName.add(step)
}
}
assertThat("Steps with wrong step name or without STEP_NAME field.: ${stepsWithWrongStepName}",
stepsWithWrongStepName, is(empty()))
}
/*
* With that test we ensure that all return types of the call methods of all the steps
* are void. Return types other than void are not possible when running inside declarative
* pipelines. Parameters shared between several steps needs to be shared via the commonPipelineEnvironment.
*/
@Test
public void returnTypeForCallMethodsIsVoidTest() {
def stepsWithCallMethodsOtherThanVoid = []
def whitelist = [
'durationMeasure',
'mavenExecute'
]
for(def step in getSteps()) {
def methods = loadScript("${step}.groovy").getClass().getDeclaredMethods() as List
Collection callMethodsWithReturnTypeOtherThanVoid =
methods.stream()
.filter { ! whitelist.contains(step) }
.filter { it.getName() == 'call' &&
it.getReturnType() != Void.TYPE }
.collect(toList())
if(!callMethodsWithReturnTypeOtherThanVoid.isEmpty()) stepsWithCallMethodsOtherThanVoid << step
}
assertThat("Steps with call methods with return types other than void: ${stepsWithCallMethodsOtherThanVoid}",
stepsWithCallMethodsOtherThanVoid, is(empty()))
}
}