main.yml 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. ---
  2. # some data sanity checks
  3. - assert:
  4. that: cluster is defined
  5. fail_msg: "ERROR: Variable cluster is not defined, but is required."
  6. success_msg: "OK, cluster is defined - federating {{ cluster }}"
  7. - assert:
  8. that: clusters is defined and (clusters.keys() | length) > 0 and clusters[cluster] is defined
  9. fail_msg: "ERROR: Variable clusters is not defined or is missing cluster {{ cluster }}, but is required."
  10. success_msg: "OK, clusters are defined and cluster is found."
  11. - assert:
  12. that: api_ep is defined
  13. fail_msg: "ERROR: Variable api_ep is not defined, but is required."
  14. success_msg: "OK, api_ep is defined."
  15. - assert:
  16. that: api_token is defined
  17. fail_msg: "ERROR: Variable api_token is not defined, but is required."
  18. success_msg: "OK, api_token is defined."
  19. # is there anything to do?
  20. - name: check for cluster definitions in central
  21. uri:
  22. method: GET
  23. return_content: true
  24. validate_certs: false
  25. url: "https://{{ api_ep }}/v1/clusters"
  26. headers:
  27. Authorization: Bearer {{ api_token }}
  28. Accept: application/json
  29. register: cluster_query
  30. - name: assume cluster isn't found in the result
  31. set_fact:
  32. cluster_found: false
  33. - name: unless found
  34. set_fact:
  35. cluster_found: true
  36. when:
  37. - cluster_query.json.clusters | length > 0
  38. - (cluster_query.json.clusters | items2dict(key_name='name', value_name='status'))[clusters[cluster].name] is defined
  39. - ((cluster_query.json.clusters | items2dict(key_name='name', value_name='status'))[clusters[cluster].name]).sensorVersion is defined
  40. # (this last one is because roxctl creates a cluster record but leaves its status at null until services check in)
  41. # step 1: we could have lots of fun (authentication in place)
  42. - block:
  43. - name: init bundle check
  44. uri:
  45. method: GET
  46. return_content: true
  47. validate_certs: false
  48. url: "https://{{ api_ep }}/v1/cluster-init/init-bundles"
  49. headers:
  50. Authorization: Bearer {{ api_token }}
  51. Accept: application/json
  52. register: init_bundle_response
  53. - name: assume init bundle isn't there
  54. set_fact:
  55. init_bundle_present: false
  56. - name: unless found
  57. set_fact:
  58. init_bundle_present: true
  59. when:
  60. - init_bundle_response.json['items'] | length > 0
  61. - (init_bundle_response.json['items'] | items2dict(key_name='name', value_name='expiresAt'))[clusters[cluster].name] is defined
  62. - name: generate init bundle
  63. uri:
  64. method: POST
  65. return_content: true
  66. validate_certs: false
  67. url: "https://{{ api_ep }}/v1/cluster-init/init-bundles"
  68. headers:
  69. Authorization: Bearer {{ api_token }}
  70. Accept: application/json
  71. Content-Type: application/json
  72. body: '{"name":"{{ clusters[cluster].name | string }}"}'
  73. register: init_bundle_content
  74. when:
  75. - not init_bundle_present
  76. - name: store init bundle - operator
  77. copy:
  78. dest: "{{ ansible_facts['user_dir'] }}/{{ cluster }}-init-bundle.yaml"
  79. content: "{{ init_bundle_content.json.kubectlBundle | b64decode }}"
  80. owner: "{{ ansible_user }}"
  81. group: "{{ ansible_user }}"
  82. mode: 0600
  83. when:
  84. - not init_bundle_present
  85. - clusters[cluster].method == 'operator'
  86. - name: store init bundle - helm
  87. copy:
  88. dest: "{{ ansible_facts['user_dir'] }}/{{ cluster }}-helm-bundle.yaml"
  89. content: "{{ init_bundle_content.json.helmValuesBundle | b64decode }}"
  90. owner: "{{ ansible_user }}"
  91. group: "{{ ansible_user }}"
  92. mode: 0600
  93. when:
  94. - not init_bundle_present
  95. - clusters[cluster].method == 'helm'
  96. - name: make sure namespace is there
  97. kubernetes.core.k8s:
  98. kubeconfig: "{{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}"
  99. validate_certs: no
  100. api_version: v1
  101. kind: Namespace
  102. name: "{{ clusters[cluster].namespace }}"
  103. namespace: ""
  104. state: present
  105. - name: create init bundle
  106. kubernetes.core.k8s:
  107. kubeconfig: "{{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}"
  108. validate_certs: no
  109. src: "{{ ansible_facts['user_dir'] }}/{{ cluster }}-init-bundle.yaml"
  110. namespace: "{{ clusters[cluster].namespace }}"
  111. when:
  112. - clusters[cluster].method == 'operator'
  113. when:
  114. - clusters[cluster].method in ['operator', 'helm']
  115. - not cluster_found
  116. # no init bundles for method 'roxctl'
  117. # step 2: there's so much we can do (not really, just make sure artifacts are either present or created)
  118. # operator has its securedcluster resource
  119. - block:
  120. - name: securedcluster cr check
  121. k8s_info:
  122. kubeconfig: "{{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}"
  123. validate_certs: no
  124. api_version: platform.stackrox.io/v1alpha1
  125. kind: SecuredCluster
  126. namespace: "{{ clusters[cluster].namespace }}"
  127. name: "{{ clusters[cluster].name }}"
  128. register: secured_cr
  129. - name: create cr resource
  130. kubernetes.core.k8s:
  131. kubeconfig: "{{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}"
  132. validate_certs: no
  133. template: templates/securedcluster-cr.yml
  134. when: secured_cr.resources | length == 0
  135. when:
  136. - clusters[cluster].method == 'operator'
  137. - not cluster_found
  138. # roxctl doesn't really leave any specific signature, so check for sensor.sh
  139. - block:
  140. - name: sensor.sh check
  141. stat:
  142. path: "{{ ansible_facts['user_dir'] }}/{{ cluster }}-secured/sensor.sh"
  143. register: sensor_script_present
  144. - name: check for deployments anyway as well
  145. k8s_info:
  146. kubeconfig: "{{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}"
  147. validate_certs: no
  148. api_version: apps/v1
  149. kind: deployment
  150. namespace: "{{ clusters[cluster].namespace }}"
  151. label_selectors:
  152. - app.kubernetes.io/instance=stackrox-secured-cluster-services
  153. register: sensor_deployments_present
  154. - name: create sensor.sh resources
  155. ansible.builtin.command:
  156. argv:
  157. - /usr/local/bin/roxctl
  158. - -e
  159. - "{{ api_ep }}"
  160. - --token-file={{ ansible_facts['user_dir'] }}/api-token
  161. - sensor
  162. - generate
  163. - openshift
  164. - --openshift-version=4
  165. - --admission-controller-scan-inline=true
  166. - --admission-controller-timeout=10
  167. - --admission-controller-listen-on-events
  168. - --admission-controller-listen-on-creates
  169. - --admission-controller-listen-on-updates
  170. - --central={{ api_ep }}
  171. - --collection-method=kernel-module
  172. - --slim-collector=true
  173. - --name={{ clusters[cluster].name }}
  174. - --output-dir=./{{ clusters[cluster].name }}-secured
  175. chdir: "{{ ansible_facts['user_dir'] }}"
  176. when: not sensor_script_present.stat.exists
  177. - name: apply sensor.sh resources
  178. ansible.builtin.command:
  179. argv:
  180. - /usr/bin/env
  181. - REGISTRY_USERNAME={{ pull_user }}
  182. - REGISTRY_PASSWORD={{ pull_pass }}
  183. - KUBECONFIG={{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}
  184. - "{{ ansible_facts['user_dir'] }}/{{ clusters[cluster].name }}-secured/sensor.sh"
  185. chdir: "{{ ansible_facts['user_dir'] }}"
  186. when: sensor_deployments_present.resources | length < 2
  187. when:
  188. - clusters[cluster].method == 'roxctl'
  189. - not cluster_found
  190. # helm uses some undecipherable configmaps, so we need to use the binary
  191. - block:
  192. - name: helm chart check
  193. ansible.builtin.command:
  194. argv:
  195. - /usr/bin/env
  196. - KUBECONFIG={{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}
  197. - /usr/local/bin/helm
  198. - -n
  199. - stackrox
  200. - list
  201. - -o
  202. - json
  203. - --filter
  204. - stackrox.*services
  205. chdir: "{{ ansible_facts['user_dir'] }}"
  206. register: helm_chart_status
  207. - name: assert chart isn't there
  208. set_fact:
  209. helm_chart_present: false
  210. - name: unless proven otherwise
  211. set_fact:
  212. helm_chart_present: true
  213. when:
  214. - helm_chart_status.stdout | from_json | list | length > 0
  215. - (helm_chart_status.stdout | from_json | list)[0].status == "deployed"
  216. - name: create helm vars
  217. template:
  218. src: templates/helm-vars.yml
  219. dest: "{{ ansible_facts['user_dir'] }}/{{ clusters[cluster].name }}-helm-vars.yaml"
  220. mode: 0600
  221. owner: "{{ ansible_user }}"
  222. group: "{{ ansible_user }}"
  223. when:
  224. - not helm_chart_present
  225. - name: check the repo
  226. ansible.builtin.command:
  227. argv:
  228. - /usr/bin/env
  229. - KUBECONFIG={{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}
  230. - /usr/local/bin/helm
  231. - repo
  232. - list
  233. - -o
  234. - json
  235. chdir: "{{ ansible_facts['user_dir'] }}"
  236. ignore_errors: yes
  237. register: repo_is_there
  238. - debug: var=repo_is_there
  239. - name: add the repo
  240. ansible.builtin.command:
  241. argv:
  242. - /usr/bin/env
  243. - KUBECONFIG={{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}
  244. - /usr/local/bin/helm
  245. - repo
  246. - add
  247. - rhacs
  248. - https://mirror.openshift.com/pub/rhacs/charts/
  249. chdir: "{{ ansible_facts['user_dir'] }}"
  250. when: repo_is_there.failed or (repo_is_there.stdout | from_json | list | length) == 0
  251. - name: apply helm chart
  252. ansible.builtin.command:
  253. argv:
  254. - /usr/bin/env
  255. - KUBECONFIG={{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}
  256. - /usr/local/bin/helm
  257. - install
  258. - -n
  259. - stackrox
  260. - --create-namespace
  261. - stackrox-secured-cluster-services
  262. - rhacs/secured-cluster-services
  263. - -f
  264. - "{{ ansible_facts['user_dir'] }}/{{ clusters[cluster].name }}-helm-bundle.yaml"
  265. - -f
  266. - "{{ ansible_facts['user_dir'] }}/{{ clusters[cluster].name }}-helm-vars.yaml"
  267. chdir: "{{ ansible_facts['user_dir'] }}"
  268. when:
  269. - not helm_chart_present
  270. when:
  271. - clusters[cluster].method == 'helm'
  272. - not cluster_found
  273. # step 3: there is just you and me (wait for pods to pop up)
  274. - name: any pending pods?
  275. k8s_info:
  276. kubeconfig: "{{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}"
  277. validate_certs: no
  278. api_version: v1
  279. kind: pod
  280. namespace: "{{ clusters[cluster].namespace }}"
  281. field_selectors:
  282. - status.phase=Pending
  283. register: pending_pods
  284. - name: fix pending sensor by decreasing requests
  285. kubernetes.core.k8s_json_patch:
  286. kubeconfig: "{{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}"
  287. validate_certs: no
  288. api_version: apps/v1
  289. kind: deployment
  290. name: sensor
  291. namespace: "{{ clusters[cluster].namespace }}"
  292. patch:
  293. - op: replace
  294. path: /spec/template/spec/containers/0/resources/requests/cpu
  295. value: 750m
  296. when:
  297. - (pending_pods.resources | length) > 0
  298. - pending_pods.resources[0].metadata.labels.app == 'sensor'
  299. - name: fix pending collectors by deleting random operators
  300. kubernetes.core.k8s:
  301. kubeconfig: "{{ ansible_facts['user_dir'] }}/kubeconfig-{{ cluster }}"
  302. validate_certs: no
  303. api_version: apps/v1
  304. kind: deployment
  305. name: "{{ item.name }}"
  306. namespace: "{{ item.namespace }}"
  307. state: absent
  308. loop:
  309. - name: cluster-autoscaler-operator
  310. namespace: openshift-machine-api
  311. - name: cluster-baremetal-operator
  312. namespace: openshift-machine-api
  313. - name: csi-snapshot-controller-operator
  314. namespace: openshift-cluster-storage-operator
  315. - name: csi-snapshot-controller
  316. namespace: openshift-cluster-storage-operator
  317. when:
  318. - (pending_pods.resources | length) > 0
  319. - pending_pods.resources[0].metadata.labels.app == 'collector'
  320. # step 4: i can give you more (any sort of corrections needed? pending pods?)
  321. - name: wait for sensor to show up
  322. debug:
  323. msg: waiting for sensor
  324. - name: wait for admission-control to show up
  325. debug:
  326. msg: waiting for admission-control
  327. - name: wait for collector to show up
  328. debug:
  329. msg: waiting for collector
  330. # step 5: don't you know the time has arrived (just recheck the cluster in central - it should be healthy)
  331. ...