blob: 6e5705e02b28d3bfcf27bd5a34971f5e2a9c4d05 [file] [log] [blame]
Jakub Josefad56efe2018-02-01 13:56:44 +01001/**
2 * Helper pipeline for AWS deployments from kqueen
3 *
4 * Expected parameters:
5 * STACK_NAME Infrastructure stack name
6 * STACK_TEMPLATE File with stack template
7 *
8 * STACK_TEMPLATE_URL URL to git repo with stack templates
9 * STACK_TEMPLATE_CREDENTIALS Credentials to the templates repo
10 * STACK_TEMPLATE_BRANCH Stack templates repo branch
11 * STACK_COMPUTE_COUNT Number of compute nodes to launch
12 *
13 * HEAT_STACK_ENVIRONMENT Heat stack environmental parameters
14 * HEAT_STACK_ZONE Heat stack availability zone
15 * HEAT_STACK_PUBLIC_NET Heat stack floating IP pool
16 * OPENSTACK_API_URL OpenStack API address
17 * OPENSTACK_API_CREDENTIALS Credentials to the OpenStack API
18 * OPENSTACK_API_PROJECT OpenStack project to connect to
Jakub Josefad56efe2018-02-01 13:56:44 +010019 * OPENSTACK_API_VERSION Version of the OpenStack API (2/3)
20 *
21 * SALT_MASTER_CREDENTIALS Credentials to the Salt API
22 * SALT_MASTER_URL URL of Salt master
23 */
24
25common = new com.mirantis.mk.Common()
26git = new com.mirantis.mk.Git()
27openstack = new com.mirantis.mk.Openstack()
28orchestrate = new com.mirantis.mk.Orchestrate()
29python = new com.mirantis.mk.Python()
30salt = new com.mirantis.mk.Salt()
31
32// Define global variables
33def venv
34def venvPepper
35def outputs = [:]
36
37def ipRegex = "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"
38def envParams
39timeout(time: 12, unit: 'HOURS') {
40 node("python") {
41 try {
42 // Set build-specific variables
43 venv = "${env.WORKSPACE}/venv"
44 venvPepper = "${env.WORKSPACE}/venvPepper"
45
46 //
47 // Prepare machines
48 //
49 stage ('Create infrastructure') {
50 // value defaults
51 envParams = [
52 'cluster_zone': HEAT_STACK_ZONE,
53 'cluster_public_net': HEAT_STACK_PUBLIC_NET
54 ]
55
56 // no underscore in STACK_NAME
57 STACK_NAME = STACK_NAME.replaceAll('_', '-')
58 outputs.put('stack_name', STACK_NAME)
59
60 // set description
61 currentBuild.description = STACK_NAME
62
63 // get templates
64 git.checkoutGitRepository('template', STACK_TEMPLATE_URL, STACK_TEMPLATE_BRANCH, STACK_TEMPLATE_CREDENTIALS)
65
66 // create openstack env
Jakub Josefc1d849c2018-02-01 16:56:59 +010067 openstack.setupOpenstackVirtualenv(venv)
Jakub Josefad56efe2018-02-01 13:56:44 +010068 openstackCloud = openstack.createOpenstackEnv(venv,
69 OPENSTACK_API_URL, OPENSTACK_API_CREDENTIALS,
Jakub Josefe6f3d082018-02-01 14:53:36 +010070 OPENSTACK_API_PROJECT, "default", "", "default", "3")
Jakub Josefad56efe2018-02-01 13:56:44 +010071 openstack.getKeystoneToken(openstackCloud, venv)
72
73 // set reclass repo in heat env
74 try {
75 envParams.put('cfg_reclass_branch', STACK_RECLASS_BRANCH)
76 envParams.put('cfg_reclass_address', STACK_RECLASS_ADDRESS)
77 } catch (MissingPropertyException e) {
78 common.infoMsg("Property STACK_RECLASS_BRANCH or STACK_RECLASS_ADDRESS not found! Using default values from template.")
79 }
80
81 // launch stack
82 openstack.createHeatStack(openstackCloud, STACK_NAME, STACK_TEMPLATE, envParams, HEAT_STACK_ENVIRONMENT, venv)
83
84 // get SALT_MASTER_URL
85 saltMasterHost = openstack.getHeatStackOutputParam(openstackCloud, STACK_NAME, 'salt_master_ip', venv)
86 // check that saltMasterHost is valid
87 if (!saltMasterHost || !saltMasterHost.matches(ipRegex)) {
88 common.errorMsg("saltMasterHost is not a valid ip, value is: ${saltMasterHost}")
89 throw new Exception("saltMasterHost is not a valid ip")
90 }
91
92 currentBuild.description = "${STACK_NAME} ${saltMasterHost}"
93
94 SALT_MASTER_URL = "http://${saltMasterHost}:6969"
95
96 // Setup virtualenv for pepper
97 python.setupPepperVirtualenv(venvPepper, SALT_MASTER_URL, SALT_MASTER_CREDENTIALS)
98 }
99
100 stage('Install core infrastructure') {
101 def staticMgmtNetwork = false
102 if (common.validInputParam('STATIC_MGMT_NETWORK')) {
103 staticMgmtNetwork = STATIC_MGMT_NETWORK.toBoolean()
104 }
105 orchestrate.installFoundationInfra(venvPepper, staticMgmtNetwork)
106
107 if (common.checkContains('STACK_INSTALL', 'kvm')) {
108 orchestrate.installInfraKvm(venvPepper)
109 orchestrate.installFoundationInfra(venvPepper, staticMgmtNetwork)
110 }
111
112 orchestrate.validateFoundationInfra(venvPepper)
113 }
114
115 stage('Install Kubernetes infra') {
116 // configure kubernetes_control_address - save loadbalancer
117 def awsOutputs = aws.getOutputs(venv, aws_env_vars, STACK_NAME)
118 common.prettyPrint(awsOutputs)
119 if (awsOutputs.containsKey('ControlLoadBalancer')) {
120 salt.runSaltProcessStep(venvPepper, 'I@salt:master', 'reclass.cluster_meta_set', ['kubernetes_control_address', awsOutputs['ControlLoadBalancer']], null, true)
121 outputs.put('kubernetes_apiserver', 'https://' + awsOutputs['ControlLoadBalancer'])
122 }
123
124 // ensure certificates are generated properly
Andrey Shestakovf23ed002018-08-21 12:09:51 +0300125 salt.runSaltProcessStep(venvPepper, 'I@kubernetes:*', 'saltutil.refresh_pillar', [], null, true)
126 salt.enforceState(venvPepper, 'I@kubernetes:*', ['salt.minion.cert'], true)
Jakub Josefad56efe2018-02-01 13:56:44 +0100127
128 orchestrate.installKubernetesInfra(venvPepper)
129 }
130
131 stage('Install Kubernetes control') {
132 orchestrate.installKubernetesControl(venvPepper)
133
134 // collect artifacts (kubeconfig)
135 writeFile(file: 'kubeconfig', text: salt.getFileContent(venvPepper, 'I@kubernetes:master and *01*', '/etc/kubernetes/admin-kube-config'))
136 archiveArtifacts(artifacts: 'kubeconfig')
137 }
138
139 stage('Install Kubernetes computes') {
140 if (common.validInputParam('STACK_COMPUTE_COUNT')) {
141 if (STACK_COMPUTE_COUNT > 0) {
142 // get stack info
143 def scaling_group = aws.getOutputs(venv, aws_env_vars, STACK_NAME, 'ComputesScalingGroup')
144
145 //update autoscaling group
146 aws.updateAutoscalingGroup(venv, aws_env_vars, scaling_group, ["--desired-capacity " + STACK_COMPUTE_COUNT])
147
148 // wait for computes to boot up
149 aws.waitForAutoscalingInstances(venv, aws_env_vars, scaling_group)
150 sleep(60)
151 }
152 }
153
154 orchestrate.installKubernetesCompute(venvPepper)
155 }
156
157 stage('Finalize') {
158 outputsPretty = common.prettify(outputs)
159 print(outputsPretty)
160 writeFile(file: 'outputs.json', text: outputsPretty)
161 archiveArtifacts(artifacts: 'outputs.json')
162 }
163
164 } catch (Throwable e) {
165 currentBuild.result = 'FAILURE'
166 throw e
167 } finally {
168 if (currentBuild.result == 'FAILURE') {
169 common.errorMsg("Deploy job FAILED and was not deleted. Please fix the problem and delete stack on you own.")
170
171 if (common.validInputParam('SALT_MASTER_URL')) {
172 common.errorMsg("Salt master URL: ${SALT_MASTER_URL}")
173 }
174 }
175 }
176 }
177}
178
179