index.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import * as awsx from '@pulumi/awsx'
  2. import * as aws from '@pulumi/aws'
  3. import * as eks from '@pulumi/eks'
  4. import * as docker from '@pulumi/docker'
  5. import * as k8s from '@pulumi/kubernetes'
  6. import * as pulumi from '@pulumi/pulumi'
  7. import { CaddyServiceDeployment } from 'pulumi-common'
  8. import * as fs from 'fs'
  9. const awsConfig = new pulumi.Config('aws')
  10. const config = new pulumi.Config()
  11. const wsProviderEndpointURI = config.require('wsProviderEndpointURI')
  12. const isAnonymous = config.require('isAnonymous') === 'true'
  13. const lbReady = config.get('isLoadBalancerReady') === 'true'
  14. const name = 'storage-node'
  15. const colossusPort = parseInt(config.get('colossusPort') || '3000')
  16. const storage = parseInt(config.get('storage') || '40')
  17. const isMinikube = config.getBoolean('isMinikube')
  18. let additionalParams: string[] | pulumi.Input<string>[] = []
  19. let volumeMounts: pulumi.Input<pulumi.Input<k8s.types.input.core.v1.VolumeMount>[]> = []
  20. let volumes: pulumi.Input<pulumi.Input<k8s.types.input.core.v1.Volume>[]> = []
  21. export let kubeconfig: pulumi.Output<any>
  22. export let colossusImage: pulumi.Output<string>
  23. let provider: k8s.Provider
  24. if (isMinikube) {
  25. provider = new k8s.Provider('local', {})
  26. // Create image from local app
  27. colossusImage = new docker.Image('joystream/colossus', {
  28. build: {
  29. context: '../../../',
  30. dockerfile: '../../../colossus.Dockerfile',
  31. },
  32. imageName: 'joystream/colossus:latest',
  33. skipPush: true,
  34. }).baseImageName
  35. // colossusImage = pulumi.interpolate`joystream/colossus:latest`
  36. } else {
  37. // Create a VPC for our cluster.
  38. const vpc = new awsx.ec2.Vpc('storage-node-vpc', { numberOfAvailabilityZones: 2, numberOfNatGateways: 1 })
  39. // Create an EKS cluster with the default configuration.
  40. const cluster = new eks.Cluster('eksctl-storage-node', {
  41. vpcId: vpc.id,
  42. subnetIds: vpc.publicSubnetIds,
  43. instanceType: 't2.medium',
  44. providerCredentialOpts: {
  45. profileName: awsConfig.get('profile'),
  46. },
  47. })
  48. provider = cluster.provider
  49. // Export the cluster's kubeconfig.
  50. kubeconfig = cluster.kubeconfig
  51. // Create a repository
  52. const repo = new awsx.ecr.Repository('colossus-image')
  53. // Build an image and publish it to our ECR repository.
  54. colossusImage = repo.buildAndPushImage({
  55. dockerfile: '../../../colossus.Dockerfile',
  56. context: '../../../',
  57. })
  58. }
  59. const resourceOptions = { provider: provider }
  60. // Create a Kubernetes Namespace
  61. const ns = new k8s.core.v1.Namespace(name, {}, resourceOptions)
  62. // Export the Namespace name
  63. export const namespaceName = ns.metadata.name
  64. const appLabels = { appClass: name }
  65. const pvc = new k8s.core.v1.PersistentVolumeClaim(
  66. `${name}-pvc`,
  67. {
  68. metadata: {
  69. labels: appLabels,
  70. namespace: namespaceName,
  71. name: `${name}-pvc`,
  72. },
  73. spec: {
  74. accessModes: ['ReadWriteOnce'],
  75. resources: {
  76. requests: {
  77. storage: `${storage}Gi`,
  78. },
  79. },
  80. },
  81. },
  82. resourceOptions
  83. )
  84. volumes.push({
  85. name: 'ipfs-data',
  86. persistentVolumeClaim: {
  87. claimName: `${name}-pvc`,
  88. },
  89. })
  90. const caddyEndpoints = [
  91. ` {
  92. reverse_proxy storage-node:${colossusPort}
  93. }`,
  94. ]
  95. export let endpoint1: pulumi.Output<string> = pulumi.interpolate``
  96. export let endpoint2: pulumi.Output<string> = pulumi.interpolate``
  97. if (!isMinikube) {
  98. const caddy = new CaddyServiceDeployment(
  99. 'caddy-proxy',
  100. { lbReady, namespaceName: namespaceName, caddyEndpoints },
  101. resourceOptions
  102. )
  103. endpoint1 = pulumi.interpolate`${caddy.primaryEndpoint}`
  104. endpoint2 = pulumi.interpolate`${caddy.secondaryEndpoint}`
  105. }
  106. export let appLink: pulumi.Output<string>
  107. if (lbReady) {
  108. appLink = pulumi.interpolate`https://${endpoint1}`
  109. if (!isAnonymous) {
  110. const remoteKeyFilePath = '/joystream/key-file.json'
  111. const providerId = config.require('providerId')
  112. const keyFile = config.require('keyFile')
  113. const publicUrl = config.get('publicURL') ? config.get('publicURL')! : appLink
  114. const keyConfig = new k8s.core.v1.ConfigMap('key-config', {
  115. metadata: { namespace: namespaceName, labels: appLabels },
  116. data: { 'fileData': fs.readFileSync(keyFile).toString() },
  117. })
  118. const keyConfigName = keyConfig.metadata.apply((m) => m.name)
  119. additionalParams = ['--provider-id', providerId, '--key-file', remoteKeyFilePath, '--public-url', publicUrl]
  120. volumeMounts.push({
  121. mountPath: remoteKeyFilePath,
  122. name: 'keyfile-volume',
  123. subPath: 'fileData',
  124. })
  125. volumes.push({
  126. name: 'keyfile-volume',
  127. configMap: {
  128. name: keyConfigName,
  129. },
  130. })
  131. const passphrase = config.get('passphrase')
  132. if (passphrase) {
  133. additionalParams.push('--passphrase', passphrase)
  134. }
  135. }
  136. }
  137. if (isAnonymous) {
  138. additionalParams.push('--anonymous')
  139. }
  140. // Create a Deployment
  141. const deployment = new k8s.apps.v1.Deployment(
  142. name,
  143. {
  144. metadata: {
  145. namespace: namespaceName,
  146. labels: appLabels,
  147. },
  148. spec: {
  149. replicas: 1,
  150. selector: { matchLabels: appLabels },
  151. template: {
  152. metadata: {
  153. labels: appLabels,
  154. },
  155. spec: {
  156. hostname: 'ipfs',
  157. containers: [
  158. {
  159. name: 'ipfs',
  160. image: 'ipfs/go-ipfs:latest',
  161. ports: [{ containerPort: 5001 }, { containerPort: 8080 }],
  162. command: ['/bin/sh', '-c'],
  163. args: [
  164. 'set -e; \
  165. /usr/local/bin/start_ipfs config profile apply lowpower; \
  166. /usr/local/bin/start_ipfs config --json Gateway.PublicGateways \'{"localhost": null }\'; \
  167. /usr/local/bin/start_ipfs config Datastore.StorageMax 200GB; \
  168. /sbin/tini -- /usr/local/bin/start_ipfs daemon --migrate=true',
  169. ],
  170. volumeMounts: [
  171. {
  172. name: 'ipfs-data',
  173. mountPath: '/data/ipfs',
  174. },
  175. ],
  176. },
  177. {
  178. name: 'colossus',
  179. image: colossusImage,
  180. imagePullPolicy: 'IfNotPresent',
  181. env: [
  182. {
  183. name: 'WS_PROVIDER_ENDPOINT_URI',
  184. // example 'wss://18.209.241.63.nip.io/'
  185. value: wsProviderEndpointURI,
  186. },
  187. {
  188. name: 'DEBUG',
  189. value: 'joystream:*',
  190. },
  191. ],
  192. volumeMounts,
  193. command: [
  194. 'yarn',
  195. 'colossus',
  196. '--ws-provider',
  197. wsProviderEndpointURI,
  198. '--ipfs-host',
  199. 'ipfs',
  200. ...additionalParams,
  201. ],
  202. ports: [{ containerPort: colossusPort }],
  203. },
  204. ],
  205. volumes,
  206. },
  207. },
  208. },
  209. },
  210. resourceOptions
  211. )
  212. // Create a LoadBalancer Service for the Deployment
  213. const service = new k8s.core.v1.Service(
  214. name,
  215. {
  216. metadata: {
  217. labels: appLabels,
  218. namespace: namespaceName,
  219. name: 'storage-node',
  220. },
  221. spec: {
  222. type: isMinikube ? 'NodePort' : 'ClusterIP',
  223. ports: [{ name: 'port-1', port: colossusPort }],
  224. selector: appLabels,
  225. },
  226. },
  227. resourceOptions
  228. )
  229. // Export the Service name
  230. export const serviceName = service.metadata.name
  231. // Export the Deployment name
  232. export const deploymentName = deployment.metadata.name