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