Tiltfile 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. # This Tiltfile contains the deployment and build config for the Wormhole devnet.
  2. #
  3. # We use Buildkit cache mounts and careful layering to avoid unnecessary rebuilds - almost
  4. # all source code changes result in small, incremental rebuilds. Dockerfiles are written such
  5. # that, for example, changing the contract source code won't cause Solana itself to be rebuilt.
  6. #
  7. load("ext://namespace", "namespace_create", "namespace_inject")
  8. load("ext://secret", "secret_yaml_generic")
  9. allow_k8s_contexts("ci")
  10. # Disable telemetry by default
  11. analytics_settings(False)
  12. # Runtime configuration
  13. config.define_bool("ci", False, "We are running in CI")
  14. config.define_bool("manual", False, "Set TRIGGER_MODE_MANUAL by default")
  15. config.define_string("num", False, "Number of guardian nodes to run")
  16. # You do not usually need to set this argument - this argument is for debugging only. If you do use a different
  17. # namespace, note that the "wormhole" namespace is hardcoded in tests and don't forget specifying the argument
  18. # when running "tilt down".
  19. #
  20. config.define_string("namespace", False, "Kubernetes namespace to use")
  21. # These arguments will enable writing Guardian events to a cloud BigTable instance.
  22. # Writing to a cloud BigTable is optional. These arguments are not required to run the devnet.
  23. config.define_string("gcpProject", False, "GCP project ID for BigTable persistence")
  24. config.define_string("bigTableKeyPath", False, "Path to BigTable json key file")
  25. # When running Tilt on a server, this can be used to set the public hostname Tilt runs on
  26. # for service links in the UI to work.
  27. config.define_string("webHost", False, "Public hostname for port forwards")
  28. # Components
  29. config.define_bool("pyth", False, "Enable Pyth-to-Wormhole component")
  30. config.define_bool("explorer", False, "Enable explorer component")
  31. config.define_bool("bridge_ui", False, "Enable bridge UI component")
  32. config.define_bool("e2e", False, "Enable E2E testing stack")
  33. cfg = config.parse()
  34. num_guardians = int(cfg.get("num", "1"))
  35. namespace = cfg.get("namespace", "wormhole")
  36. gcpProject = cfg.get("gcpProject", "local-dev")
  37. bigTableKeyPath = cfg.get("bigTableKeyPath", "./event_database/devnet_key.json")
  38. webHost = cfg.get("webHost", "localhost")
  39. ci = cfg.get("ci", False)
  40. pyth = cfg.get("pyth", ci)
  41. explorer = cfg.get("explorer", ci)
  42. bridge_ui = cfg.get("bridge_ui", ci)
  43. e2e = cfg.get("e2e", ci)
  44. if cfg.get("manual", False):
  45. trigger_mode = TRIGGER_MODE_MANUAL
  46. else:
  47. trigger_mode = TRIGGER_MODE_AUTO
  48. # namespace
  49. if not ci:
  50. namespace_create(namespace)
  51. def k8s_yaml_with_ns(objects):
  52. return k8s_yaml(namespace_inject(objects, namespace))
  53. # protos
  54. proto_deps = ["./proto", "./generate-protos.sh", "buf.yaml", "buf.gen.yaml"]
  55. local_resource(
  56. name = "proto-gen",
  57. deps = proto_deps,
  58. cmd = "tilt docker build -- --target go-export -f Dockerfile.proto -o type=local,dest=node .",
  59. env = {"DOCKER_BUILDKIT": "1"},
  60. trigger_mode = trigger_mode,
  61. )
  62. local_resource(
  63. name = "proto-gen-web",
  64. deps = proto_deps,
  65. resource_deps = ["proto-gen"],
  66. cmd = "tilt docker build -- --target node-export -f Dockerfile.proto -o type=local,dest=. .",
  67. env = {"DOCKER_BUILDKIT": "1"},
  68. trigger_mode = trigger_mode,
  69. )
  70. local_resource(
  71. name = "teal-gen",
  72. deps = ["staging/algorand/teal"],
  73. cmd = "tilt docker build -- --target teal-export -f Dockerfile.teal -o type=local,dest=. .",
  74. env = {"DOCKER_BUILDKIT": "1"},
  75. trigger_mode = trigger_mode,
  76. )
  77. # wasm
  78. local_resource(
  79. name = "wasm-gen",
  80. deps = ["solana"],
  81. dir = "solana",
  82. cmd = "tilt docker build -- -f Dockerfile.wasm -o type=local,dest=.. .",
  83. env = {"DOCKER_BUILDKIT": "1"},
  84. trigger_mode = trigger_mode,
  85. )
  86. # node
  87. if explorer:
  88. k8s_yaml_with_ns(
  89. secret_yaml_generic(
  90. "node-bigtable-key",
  91. from_file = "bigtable-key.json=" + bigTableKeyPath,
  92. ),
  93. )
  94. docker_build(
  95. ref = "guardiand-image",
  96. context = "node",
  97. dockerfile = "node/Dockerfile",
  98. )
  99. def build_node_yaml():
  100. node_yaml = read_yaml_stream("devnet/node.yaml")
  101. for obj in node_yaml:
  102. if obj["kind"] == "StatefulSet" and obj["metadata"]["name"] == "guardian":
  103. obj["spec"]["replicas"] = num_guardians
  104. container = obj["spec"]["template"]["spec"]["containers"][0]
  105. if container["name"] != "guardiand":
  106. fail("container 0 is not guardiand")
  107. container["command"] += ["--devNumGuardians", str(num_guardians)]
  108. if explorer:
  109. container["command"] += [
  110. "--bigTablePersistenceEnabled",
  111. "--bigTableInstanceName",
  112. "wormhole",
  113. "--bigTableTableName",
  114. "v2Events",
  115. "--bigTableTopicName",
  116. "new-vaa-devnet",
  117. "--bigTableKeyPath",
  118. "/tmp/mounted-keys/bigtable-key.json",
  119. "--bigTableGCPProject",
  120. gcpProject,
  121. ]
  122. return encode_yaml_stream(node_yaml)
  123. k8s_yaml_with_ns(build_node_yaml())
  124. k8s_resource(
  125. "guardian",
  126. resource_deps = ["proto-gen", "solana-devnet"],
  127. port_forwards = [
  128. port_forward(6060, name = "Debug/Status Server [:6060]", host = webHost),
  129. port_forward(7070, name = "Public gRPC [:7070]", host = webHost),
  130. port_forward(7071, name = "Public REST [:7071]", host = webHost),
  131. port_forward(2345, name = "Debugger [:2345]", host = webHost),
  132. ],
  133. trigger_mode = trigger_mode,
  134. )
  135. # spy
  136. k8s_yaml_with_ns("devnet/spy.yaml")
  137. k8s_resource(
  138. "spy",
  139. resource_deps = ["proto-gen", "guardian"],
  140. port_forwards = [
  141. port_forward(6061, container_port = 6060, name = "Debug/Status Server [:6061]", host = webHost),
  142. port_forward(7072, name = "Spy gRPC [:7072]", host = webHost),
  143. ],
  144. trigger_mode = trigger_mode,
  145. )
  146. # solana client cli (used for devnet setup)
  147. docker_build(
  148. ref = "bridge-client",
  149. context = ".",
  150. only = ["./proto", "./solana", "./ethereum", "./clients"],
  151. dockerfile = "Dockerfile.client",
  152. # Ignore target folders from local (non-container) development.
  153. ignore = ["./solana/*/target"],
  154. )
  155. # solana smart contract
  156. docker_build(
  157. ref = "solana-contract",
  158. context = "solana",
  159. dockerfile = "solana/Dockerfile",
  160. )
  161. # solana local devnet
  162. k8s_yaml_with_ns("devnet/solana-devnet.yaml")
  163. k8s_resource(
  164. "solana-devnet",
  165. resource_deps = ["wasm-gen"],
  166. port_forwards = [
  167. port_forward(8899, name = "Solana RPC [:8899]", host = webHost),
  168. port_forward(8900, name = "Solana WS [:8900]", host = webHost),
  169. port_forward(9000, name = "Solana PubSub [:9000]", host = webHost),
  170. ],
  171. trigger_mode = trigger_mode,
  172. )
  173. # eth devnet
  174. docker_build(
  175. ref = "eth-node",
  176. context = "./ethereum",
  177. dockerfile = "./ethereum/Dockerfile",
  178. # ignore local node_modules (in case they're present)
  179. ignore = ["./ethereum/node_modules"],
  180. # sync external scripts for incremental development
  181. # (everything else needs to be restarted from scratch for determinism)
  182. #
  183. # This relies on --update-mode=exec to work properly with a non-root user.
  184. # https://github.com/tilt-dev/tilt/issues/3708
  185. live_update = [
  186. sync("./ethereum/src", "/home/node/app/src"),
  187. ],
  188. )
  189. if pyth:
  190. # pyth autopublisher
  191. docker_build(
  192. ref = "pyth",
  193. context = ".",
  194. dockerfile = "third_party/pyth/Dockerfile.pyth",
  195. )
  196. k8s_yaml_with_ns("./devnet/pyth.yaml")
  197. k8s_resource("pyth", resource_deps = ["solana-devnet"], trigger_mode = trigger_mode)
  198. # pyth2wormhole client autoattester
  199. docker_build(
  200. ref = "p2w-attest",
  201. context = ".",
  202. only = ["./solana", "./third_party"],
  203. dockerfile = "./third_party/pyth/Dockerfile.p2w-attest",
  204. ignore = ["./solana/*/target"],
  205. )
  206. k8s_yaml_with_ns("devnet/p2w-attest.yaml")
  207. k8s_resource(
  208. "p2w-attest",
  209. resource_deps = ["solana-devnet", "pyth", "guardian"],
  210. port_forwards = [],
  211. trigger_mode = trigger_mode,
  212. )
  213. k8s_yaml_with_ns("devnet/eth-devnet.yaml")
  214. k8s_resource(
  215. "eth-devnet",
  216. port_forwards = [
  217. port_forward(8545, name = "Ganache RPC [:8545]", host = webHost),
  218. ],
  219. trigger_mode = trigger_mode,
  220. )
  221. k8s_resource(
  222. "eth-devnet2",
  223. port_forwards = [
  224. port_forward(8546, name = "Ganache RPC [:8546]", host = webHost),
  225. ],
  226. trigger_mode = trigger_mode,
  227. )
  228. if bridge_ui:
  229. docker_build(
  230. ref = "bridge-ui",
  231. context = ".",
  232. only = ["./ethereum", "./sdk", "./bridge_ui"],
  233. dockerfile = "bridge_ui/Dockerfile",
  234. live_update = [
  235. sync("./bridge_ui/src", "/app/bridge_ui/src"),
  236. ],
  237. )
  238. k8s_yaml_with_ns("devnet/bridge-ui.yaml")
  239. k8s_resource(
  240. "bridge-ui",
  241. resource_deps = ["proto-gen-web", "wasm-gen"],
  242. port_forwards = [
  243. port_forward(3000, name = "Bridge UI [:3000]", host = webHost),
  244. ],
  245. trigger_mode = trigger_mode,
  246. )
  247. # algorand
  248. k8s_yaml_with_ns("devnet/algorand.yaml")
  249. docker_build(
  250. ref = "algorand",
  251. context = "third_party/algorand",
  252. dockerfile = "third_party/algorand/Dockerfile",
  253. )
  254. k8s_resource(
  255. "algorand",
  256. resource_deps = ["teal-gen"],
  257. port_forwards = [
  258. port_forward(4001, name = "Algorand RPC [:4001]", host = webHost),
  259. port_forward(4002, name = "Algorand KMD [:4002]", host = webHost),
  260. ],
  261. trigger_mode = trigger_mode,
  262. )
  263. # e2e
  264. if e2e:
  265. k8s_yaml_with_ns("devnet/e2e.yaml")
  266. docker_build(
  267. ref = "e2e",
  268. context = "e2e",
  269. dockerfile = "e2e/Dockerfile",
  270. network = "host",
  271. )
  272. k8s_resource(
  273. "e2e",
  274. port_forwards = [
  275. port_forward(6080, name = "VNC [:6080]", host = webHost, link_path = "/vnc_auto.html"),
  276. ],
  277. trigger_mode = trigger_mode,
  278. )
  279. # bigtable
  280. if explorer:
  281. k8s_yaml_with_ns("devnet/bigtable.yaml")
  282. k8s_resource(
  283. "bigtable-emulator",
  284. port_forwards = [port_forward(8086, name = "BigTable clients [:8086]", host = webHost)],
  285. labels = ["explorer"],
  286. trigger_mode = trigger_mode,
  287. )
  288. k8s_resource("pubsub-emulator",
  289. port_forwards = [port_forward(8085, name = "PubSub listeners [:8085]")],
  290. labels = ["explorer"],
  291. )
  292. docker_build(
  293. ref = "cloud-functions",
  294. context = "./event_database/cloud_functions",
  295. dockerfile = "./event_database/cloud_functions/Dockerfile",
  296. live_update = [
  297. sync("./event_database/cloud_functions", "/app"),
  298. ],
  299. )
  300. k8s_resource(
  301. "cloud-functions",
  302. resource_deps = ["proto-gen", "bigtable-emulator", "pubsub-emulator"],
  303. port_forwards = [port_forward(8090, name = "Cloud Functions [:8090]")],
  304. labels = ["explorer"],
  305. trigger_mode = trigger_mode,
  306. )
  307. # explorer web app
  308. docker_build(
  309. ref = "explorer",
  310. context = "./explorer",
  311. dockerfile = "./explorer/Dockerfile",
  312. ignore = ["./explorer/node_modules"],
  313. live_update = [
  314. sync("./explorer/src", "/home/node/app/src"),
  315. sync("./explorer/public", "/home/node/app/public"),
  316. ],
  317. )
  318. k8s_yaml_with_ns("devnet/explorer.yaml")
  319. k8s_resource(
  320. "explorer",
  321. resource_deps = ["proto-gen-web"],
  322. port_forwards = [
  323. port_forward(8001, name = "Explorer Web UI [:8001]", host = webHost),
  324. ],
  325. labels = ["explorer"],
  326. trigger_mode = trigger_mode,
  327. )
  328. # terra devnet
  329. docker_build(
  330. ref = "terra-image",
  331. context = "./terra/devnet",
  332. dockerfile = "terra/devnet/Dockerfile",
  333. )
  334. docker_build(
  335. ref = "terra-contracts",
  336. context = "./terra",
  337. dockerfile = "./terra/Dockerfile",
  338. )
  339. k8s_yaml_with_ns("devnet/terra-devnet.yaml")
  340. k8s_resource(
  341. "terra-terrad",
  342. port_forwards = [
  343. port_forward(26657, name = "Terra RPC [:26657]", host = webHost),
  344. port_forward(1317, name = "Terra LCD [:1317]", host = webHost),
  345. ],
  346. trigger_mode = trigger_mode,
  347. )
  348. k8s_resource(
  349. "terra-fcd",
  350. port_forwards = [port_forward(3060, name = "Terra FCD [:3060]", host = webHost)],
  351. trigger_mode = trigger_mode,
  352. )