blob: 029b334fa4a4e5dd952d7d2846cad9925acfc879 [file] [log] [blame]
package com.mirantis.mcp
/*
Example usage:
def ccpCiCd = new com.mirantis.mcp.CCPCICD().newInstance(this, env)
ccpCiCd.applyPipelineParameters()
ccpCiCd.fetchEnvConfiguration()
ccpCiCd.parametrizeConfig()
ccpCiCd.build()
ccpCiCd.deploy()
ccpCiCd.cleanup()
*/
/*
Since groovy-cps demands that local variables may be serialized,
any locally defined classes must also be serializable
More details: https://issues.jenkins-ci.org/browse/JENKINS-32213
*/
public class ccpCICD implements Serializable {
/*
Endless loop in DefaultInvoker.getProperty when accessing field
via getter/setter without @
This issue fixed in groovy pipeline plugin 2.25
More details https://issues.jenkins-ci.org/browse/JENKINS-31484
*/
/*
nodeContext - is a context of which node the job is executed
through this context, CCPCICD class able to use
dir() writeFile() sh() and other workflow basic steps
See more https://jenkins.io/doc/pipeline/steps/workflow-basic-steps/
nodeContext must be passed in class constructor
*/
def nodeContext = null
/*
Job parameters are accessed through environment
*/
def env = null
/*
Directory where stored environment configuration
*/
def envConfigurationPath = null
def File envConfiguration = null
/*
Path to entry point yaml in environment directory
*/
def ccpConfigPath = null
def File virtualEnv = null // Currently not supported
def URI configurationRepo = null
/*
parsed `ccp config dump` output
*/
def Map ccpConfig = null
/*
Part of ccp parameters which will be overwritten by job
parametrizeConfig() method
*/
def Map ciConfig = [
'registry' : [
'address' : null,
'username' : null,
'password' : null,
],
'kubernetes' : [
'namespace' : 'ccp',
'server' : null,
'insecure' : true,
'username' : null,
'password' : null
],
'repositories': [
'path' : null
]
]
// Using loadYAML and dumpYAML from common library
def common = new com.mirantis.mcp.Common()
// default CCP repo (using in upgrade)
def URI ccpSource = new URI('git+https://gerrit.mcp.mirantis.net/ccp/fuel-ccp')
def public void setConfURL(URI confURI) {
if (confURI == null) {
throw new IllegalArgumentException('Invalid argument')
}
this.@configurationRepo = confURI
}
def public URI getConfURL() {
return this.@configurationRepo
}
/*
According JENKINS-31314 constructor can be used only for assignments properties
More details: https://issues.jenkins-ci.org/browse/JENKINS-31314
*/
def public ccpCICD(nodeContext, env) {
this.@nodeContext = nodeContext
this.@env = env
}
/*
Transform jenkins job arguments to ccp configuration
Parameters can be overriden with customParameters Map
Example usage:
applyPipelineParameters([
'images': [
'image_specs': [
'etcd': [
'tag': '41a45e5a9db5'
]
]
]
])
*/
def public void applyPipelineParameters(Map customParameters = null) {
if (this.@env.CONF_GERRIT_URL != null) {
this.setConfURL(new URI(this.@env.CONF_GERRIT_URL))
}
if (this.@env.CONF_ENTRYPOINT != null) {
this.setCcpConfigPath(new File(this.@env.CONF_ENTRYPOINT))
}
if (this.@env.KUBERNETES_URL != null) {
this.setKubernetesURL(new URI(this.@env.KUBERNETES_URL), this.@env.CREDENTIALS_ID)
} else {
this.@ciConfig.remove('kubernetes')
}
if (this.@env.DOCKER_REGISTRY != null) {
this.setRegistry(new URI(this.@env.DOCKER_REGISTRY), this.env.DOCKER_REGISTRY_CREDENTIAL_ID ? this.env.DOCKER_REGISTRY_CREDENTIAL_ID : 'artifactory')
} else {
this.@ciConfig.remove('registry')
}
this.@ciConfig['repositories']['path'] = env.WORKSPACE + '/repos'
if (customParameters != null) {
this.@ciConfig = this.@ciConfig + customParameters
}
}
def public File getCcpConfigPath() {
return this.@ccpConfigPath
}
def public void setCcpConfigPath(File configPath) {
this.@ccpConfigPath = configPath
}
/*
Set k8s endpoint and credentials
Example usage:
this.setKubernetesURL(new URI('https://host:443'), 'kubernetes-api')
*/
def public void setKubernetesURL(URI kubernetesURL, String credentialId) {
if (credentialId != null) {
this.@nodeContext.withCredentials([
[
$class : 'UsernamePasswordMultiBinding',
credentialsId : credentialId,
passwordVariable : 'K8S_PASSWORD',
usernameVariable : 'K8S_USERNAME'
]
]) {
this.@ciConfig['kubernetes']['username'] = env.K8S_USERNAME
this.@ciConfig['kubernetes']['password'] = env.K8S_PASSWORD
}
}
/*
//TODO(sryabin) properly parse return from getUserInfo()
if (kubernetesURL.getUserInfo()) {
this.@ciConfig['kubernetes']['username'] = kubernetesURL.getUserInfo()
}
*/
this.@ciConfig['kubernetes']['server'] = kubernetesURL.toString()
}
def public void setRegistry(String registryEndpoint, String credentialId) {
if (credentialId) {
this.@nodeContext.withCredentials([
[
$class : 'UsernamePasswordMultiBinding',
credentialsId : credentialId,
passwordVariable : 'REGISTRY_PASSWORD',
usernameVariable : 'REGISTRY_USERNAME'
]
]) {
this.@ciConfig['registry']['username'] = env.REGISTRY_USERNAME
this.@ciConfig['registry']['password'] = env.REGISTRY_PASSWORD
}
} else {
this.@ciConfig['registry'].remove('username')
this.@ciConfig['registry'].remove('password')
}
this.@ciConfig['registry']['address'] = registryEndpoint;
}
def public setEnvConfigurationDir() {
// TODO(sryabin) cleanup this dir in desctructor
this.@envConfigurationPath = File.createTempDir(this.@env.WORKSPACE + '/envConfiguration', 'ccpci')
}
def public File getEnvConfigurationDir() {
return this.@envConfigurationPath
}
def public File fetchEnvConfiguration() {
def gitTools = new com.mirantis.mcp.Git()
this.setEnvConfigurationDir()
gitTools.gitSSHCheckout ([
credentialsId : this.@env.CONF_GERRIT_CREDENTIAL_ID,
branch : "master",
host : this.getConfURL().getHost(),
port : this.getConfURL().getPort(),
project: this.getConfURL().getPath(),
targetDir: this.getEnvConfigurationDir().toString()
])
return this.getEnvConfigurationDir()
}
def public String configDump() {
return this.ccpExecute('config dump')
}
/*
merge ccp configuration from repo and custom parameters from job arguments
*/
def public Map parametrizeConfig() {
this.fetchEnvConfiguration()
this.@ccpConfig = this.@common.loadYAML(this.configDump())
this.setCcpConfigPath(new File('parametrized.yaml'));
this.@nodeContext.writeFile file: this.getEnvConfigurationDir().toString() + '/' + this.getCcpConfigPath().toString(), \
text: this.@common.dumpYAML(this.@ccpConfig + this.@ciConfig)
}
def public Map getConfig() {
return this.@ccpConfig
}
/*
Example usage:
ci.ccpExecute('config dump')
*/
def public String ccpExecute(args) {
// TODO(sryabin) pass custom args from class property, like debug
def String output = null;
this.@nodeContext.dir(this.getEnvConfigurationDir().toString()) {
output = this.@nodeContext.sh(
script: "PYTHONWARNINGS='ignore:Unverified HTTPS request' ccp --config-file " + this.getCcpConfigPath().toString() + " ${args}",
returnStdout: true
)
}
return output
}
/*
Update fuel-ccp
@param virtualEnv File, path to python virtual environment, currently not supported
@param source URI, if not present, use upstream
@param upgradePip Boolean, upgrade pip if required
Usage example:
upgradeCcp(new File('/home/ubuntu/.venv'), new URI('http://github.com/openstack/fuel-ccp'))
*/
def public void upgradeCcp(File virtualEnv = null, URI source, Boolean upgradePip = false) {
if (virtualEnv) {
throw new UnsupportedOperationException('Python virtual environments not implemented')
}
try {
def output = this.@nodeContext.sh(
script: 'pip install --user --upgrade ' + ((source != null) ? source : this.@ccpSource).toString(),
returnStdout: true
)
} catch (e) {
// TODO(sryabin) catch in stderr "You should consider upgrading via the 'pip install --upgrade pip' command."
if (upgradePip == true) {
this.@nodeContext.sh(
script: 'pip install --upgrade pip',
returnStdout: true
)
}
// FIXME(sryabin) infinity loop
//this.upgradeCcp(virtualEnv, source, false)
}
}
def public void upgradeCcp() {
this.upgradeCcp(null, null, true)
}
def build() {
//TODO(sryabin) implement build -c functionality
return this.ccpExecute('build')
}
def deploy() {
//TODO(sryabin) implement build -c functionality
return this.ccpExecute('deploy')
}
def cleanup() {
try {
this.ccpExecute('cleanup')
} catch (err) {
this.ccpExecute('cleanup --skip-os-cleanup')
}
}
def fetch() {
// TODO(sryabin) implement fetch -f functioanlity
return this.ccpExecute('fetch')
}
}
/*
Workaround for scope limitation, and
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: unclassified new
def ccpCiCd = new com.mirantis.mcp.CCPCICD()
def CCPCICD ci = new ccpCiCd.ccpCICD(this, env) return no such CCPCICD()
Example usage:
def ccpCiCd = new com.mirantis.mcp.CCPCICD().newInstance(this, env)
*/
def newInstance(nodeContext, env) {
return new ccpCICD(nodeContext,env)
}