processorDeployment.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import * as k8s from '@pulumi/kubernetes'
  2. import * as pulumi from '@pulumi/pulumi'
  3. import { PostgresServiceDeployment } from 'pulumi-common'
  4. /**
  5. * ServiceDeployment is an example abstraction that uses a class to fold together the common pattern of a
  6. * Kubernetes Deployment and its associated Service object.
  7. * This class deploys a db, a migration job, graphql server and processor
  8. */
  9. export class ProcessorServiceDeployment extends pulumi.ComponentResource {
  10. public readonly deployment: k8s.apps.v1.Deployment
  11. public readonly service: k8s.core.v1.Service
  12. public readonly endpoint: string
  13. constructor(name: string, args: ServiceDeploymentArgs, opts?: pulumi.ComponentResourceOptions) {
  14. super('processor:service:ProcessorServiceDeployment', name, {}, opts)
  15. const config = new pulumi.Config()
  16. const DB_PASS = config.require('dbPassword')
  17. const DB_USERNAME = 'postgres'
  18. const PROCESSOR_DATABASE_NAME = 'processor'
  19. const DB_PORT = '5432'
  20. // Name passed in the constructor will be the endpoint for accessing the service
  21. this.endpoint = 'graphql-server'
  22. const processorDbName = 'processor-db'
  23. const processorDb = new PostgresServiceDeployment(
  24. processorDbName,
  25. {
  26. namespaceName: args.namespaceName,
  27. env: [
  28. { name: 'POSTGRES_USER', value: DB_USERNAME },
  29. { name: 'POSTGRES_PASSWORD', value: DB_PASS },
  30. { name: 'POSTGRES_DB', value: PROCESSOR_DATABASE_NAME },
  31. { name: 'PGPORT', value: DB_PORT },
  32. ],
  33. storage: args.storage,
  34. },
  35. { parent: this }
  36. )
  37. const processorMigrationJob = new k8s.batch.v1.Job(
  38. 'processor-db-migration',
  39. {
  40. metadata: {
  41. namespace: args.namespaceName,
  42. },
  43. spec: {
  44. backoffLimit: 0,
  45. template: {
  46. spec: {
  47. containers: [
  48. {
  49. name: 'db-migration',
  50. image: args.joystreamAppsImage,
  51. imagePullPolicy: 'IfNotPresent',
  52. resources: { requests: { cpu: '100m', memory: '100Mi' } },
  53. env: [
  54. {
  55. name: 'WARTHOG_DB_HOST',
  56. value: processorDbName,
  57. },
  58. {
  59. name: 'DB_HOST',
  60. value: processorDbName,
  61. },
  62. { name: 'WARTHOG_DB_DATABASE', value: PROCESSOR_DATABASE_NAME },
  63. { name: 'WARTHOG_DB_USERNAME', value: DB_USERNAME },
  64. { name: 'WARTHOG_DB_PASSWORD', value: DB_PASS },
  65. { name: 'WARTHOG_DB_PORT', value: DB_PORT },
  66. { name: 'DB_NAME', value: PROCESSOR_DATABASE_NAME },
  67. { name: 'DB_PASS', value: DB_PASS },
  68. { name: 'DB_USER', value: DB_USERNAME },
  69. { name: 'DB_PORT', value: DB_PORT },
  70. ],
  71. command: ['/bin/sh', '-c'],
  72. args: [
  73. // 'yarn workspace query-node config:dev;',
  74. 'yarn workspace query-node-root db:prepare; yarn workspace query-node-root db:migrate',
  75. ],
  76. },
  77. ],
  78. restartPolicy: 'Never',
  79. },
  80. },
  81. },
  82. },
  83. { parent: this, dependsOn: processorDb.service }
  84. )
  85. let appLabels = { appClass: 'graphql-server' }
  86. this.deployment = new k8s.apps.v1.Deployment(
  87. 'graphql-server',
  88. {
  89. metadata: {
  90. namespace: args.namespaceName,
  91. labels: appLabels,
  92. },
  93. spec: {
  94. replicas: 1,
  95. selector: { matchLabels: appLabels },
  96. template: {
  97. metadata: {
  98. labels: appLabels,
  99. },
  100. spec: {
  101. containers: [
  102. {
  103. name: 'graphql-server',
  104. image: args.joystreamAppsImage,
  105. imagePullPolicy: 'IfNotPresent',
  106. env: [
  107. { name: 'DB_HOST', value: processorDbName },
  108. { name: 'DB_PASS', value: DB_PASS },
  109. { name: 'DB_USER', value: DB_USERNAME },
  110. { name: 'DB_PORT', value: DB_PORT },
  111. { name: 'DB_NAME', value: PROCESSOR_DATABASE_NAME },
  112. { name: 'WARTHOG_DB_DATABASE', value: PROCESSOR_DATABASE_NAME },
  113. { name: 'WARTHOG_DB_USERNAME', value: DB_USERNAME },
  114. { name: 'WARTHOG_DB_PASSWORD', value: DB_PASS },
  115. { name: 'WARTHOG_APP_PORT', value: '4002' },
  116. // Why do we need this anyway?
  117. { name: 'GRAPHQL_SERVER_HOST', value: 'graphql-server' },
  118. ],
  119. ports: [{ name: 'graph-ql-port', containerPort: 4002 }],
  120. args: ['workspace', 'query-node-root', 'query-node:start:prod'],
  121. },
  122. ],
  123. },
  124. },
  125. },
  126. },
  127. { parent: this, dependsOn: processorMigrationJob }
  128. )
  129. // Create a Service for the GraphQL Server
  130. this.service = new k8s.core.v1.Service(
  131. 'graphql-server',
  132. {
  133. metadata: {
  134. labels: appLabels,
  135. namespace: args.namespaceName,
  136. name: this.endpoint,
  137. },
  138. spec: {
  139. ports: [{ name: 'port-1', port: 8081, targetPort: 'graph-ql-port' }],
  140. selector: appLabels,
  141. },
  142. },
  143. { parent: this }
  144. )
  145. const indexerURL = args.externalIndexerUrl || `http://indexer:4000/graphql`
  146. appLabels = { appClass: 'processor' }
  147. const processorDeployment = new k8s.apps.v1.Deployment(
  148. `processor`,
  149. {
  150. metadata: {
  151. namespace: args.namespaceName,
  152. labels: appLabels,
  153. },
  154. spec: {
  155. replicas: 1,
  156. selector: { matchLabels: appLabels },
  157. template: {
  158. metadata: {
  159. labels: appLabels,
  160. },
  161. spec: {
  162. containers: [
  163. {
  164. name: 'processor',
  165. image: args.joystreamAppsImage,
  166. imagePullPolicy: 'IfNotPresent',
  167. env: [
  168. {
  169. name: 'INDEXER_ENDPOINT_URL',
  170. value: indexerURL,
  171. },
  172. { name: 'TYPEORM_HOST', value: processorDbName },
  173. { name: 'TYPEORM_DATABASE', value: PROCESSOR_DATABASE_NAME },
  174. { name: 'DEBUG', value: 'index-builder:*' },
  175. { name: 'PROCESSOR_POLL_INTERVAL', value: '1000' },
  176. { name: 'DB_PASS', value: DB_PASS },
  177. { name: 'DB_USER', value: DB_USERNAME },
  178. { name: 'DB_PORT', value: DB_PORT },
  179. { name: 'WARTHOG_DB_DATABASE', value: PROCESSOR_DATABASE_NAME },
  180. { name: 'WARTHOG_DB_USERNAME', value: DB_USERNAME },
  181. { name: 'WARTHOG_DB_PASSWORD', value: DB_PASS },
  182. { name: 'WARTHOG_DB_PORT', value: DB_PORT },
  183. // These are note required but must be defined or processor will not startup
  184. { name: 'WARTHOG_APP_HOST', value: 'graphql-server' },
  185. { name: 'WARTHOG_APP_PORT', value: '4002' },
  186. ],
  187. volumeMounts: [
  188. {
  189. mountPath: '/joystream/query-node/mappings/lib/generated/types/typedefs.json',
  190. name: 'processor-volume',
  191. subPath: 'fileData',
  192. },
  193. ],
  194. args: ['workspace', 'query-node-root', 'processor:start'],
  195. },
  196. ],
  197. volumes: [
  198. {
  199. name: 'processor-volume',
  200. configMap: {
  201. name: args.defsConfig,
  202. },
  203. },
  204. ],
  205. },
  206. },
  207. },
  208. },
  209. { parent: this, dependsOn: this.service }
  210. )
  211. }
  212. }
  213. interface Environment {
  214. name: string
  215. value: string
  216. }
  217. export interface ServiceDeploymentArgs {
  218. namespaceName: pulumi.Output<string>
  219. joystreamAppsImage: pulumi.Output<string>
  220. defsConfig: pulumi.Output<string> | undefined
  221. externalIndexerUrl: string | undefined
  222. env?: Environment[]
  223. storage: number
  224. }