| 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) |
| } |