🌳
pt0/deployF/ethF/doSyncTekuF.mts
3import * as _ from 'lodash-es'
4import fs from 'fs'
5import { jwtPath } from './jwtPathF.mts'
20export const checkpointSyncCtx = genContext()
22const tekuDeploymentTmpl = ({
23 name, p2pPort, ethNetwork, wsCheckpointS, httpWeb3Prov,
24 execEngProv, jwtSecretName, keystoresSecName, ethRewardAddr,
25 enableValidator, relayHostPort, vcapiKeystorePwSecName,
26 vcapiKeystoresName, image,
27}: {
28 name: string, p2pPort: number, ethNetwork: string, wsCheckpointS: string | null,
29 httpWeb3Prov: string, execEngProv: string, jwtSecretName: string, keystoresSecName: string,
30 ethRewardAddr: string, enableValidator: boolean, relayHostPort: string | null,
31 vcapiKeystorePwSecName: string, vcapiKeystoresName: string, image: string,
32}) => {
34 const dataDir = '/data'
35 const keystoresDir = '/keystores'
36 const vcapiPwPath = '/vcapidir/vcpw.txt'
37 const vcapiKeystoresDir = `/${vcapiKeystoresName}`
39 let volumeMounts = [
40 {
41 name: vcapiKeystoresName,
42 mountPath: vcapiKeystoresDir,
43 },
44 {
45 name: vcapiKeystorePwSecName, subPath: vcapiKeystorePwSecName,
46 mountPath: vcapiPwPath,
47 },
48 {
49 name: jwtSecretName, subPath: jwtSecretName,
50 mountPath: jwtPath,
51 },
52 {
53 mountPath: dataDir,
54 name
55 },
56 ]
58 let volumes = [
59 {
60 name: vcapiKeystorePwSecName,
61 secret: {
62 secretName: vcapiKeystorePwSecName
63 }
64 },
65 {
66 name: jwtSecretName,
67 secret: {
68 secretName: jwtSecretName
69 }
70 },
71 {
72 name: name,
73 persistentVolumeClaim: {
74 claimName: name,
75 }
76 },
77 {
78 name: vcapiKeystoresName,
79 persistentVolumeClaim: {
80 claimName: vcapiKeystoresName
81 }
82 }
83 ]
85 const {checkpointSyncUrl} = checkpointSyncCtx.getStore() as {checkpointSyncUrl?: string} || {}
87 let args = _.chain([
88 `--network=${ethNetwork}`,
89 `--eth1-endpoints=${httpWeb3Prov}`,
90 `--ee-endpoint=${execEngProv}`,
91 `--ee-jwt-secret-file=${jwtPath}`,
92 `--data-base-path=${dataDir}`,
93 '--validators-keystore-locking-enabled=false', // otherwise validator fails because can't create .lock file in secrets dir
94 wsCheckpointS && `--ws-checkpoint=${wsCheckpointS}`,
95 `--validators-proposer-default-fee-recipient=${ethRewardAddr}`,
96 `--p2p-port=${p2pPort}`,
97 '--log-destination=CONSOLE',
98 '--rest-api-enabled', // using to check sync status
99 `--rest-api-host-allowlist=*`,
100 relayHostPort && `--builder-endpoint=http://${relayHostPort}`,
101 relayHostPort && '--validators-builder-registration-default-enabled=true',
102 checkpointSyncUrl && `--checkpoint-sync-url=${checkpointSyncUrl}`,
103 ]).compact().value().join(' ').split(' ')
105 if (enableValidator) {
106 args = _.union(args, [
107 `--validator-keys=${keystoresDir}:${keystoresDir}`,
108 ])
109 volumes = _.union(volumes, [
110 {
111 name: keystoresSecName,
112 secret: {
113 secretName: keystoresSecName
114 }
115 }
116 ])
117 volumeMounts = _.union(volumeMounts, [
118 {
119 name: keystoresSecName,
120 mountPath: keystoresDir,
121 },
122 ])
123 }
126 return {
127 apiVersion: 'apps/v1',
128 kind: 'Deployment',
129 metadata: {
130 name
131 },
132 spec: {
133 selector: {
134 matchLabels: {
135 name
136 }
137 },
138 replicas: 1,
139 strategy: {
140 type: 'Recreate'
141 },
142 template: {
143 metadata: {
144 labels: {
145 name,
146 [deployLabel]: name,
147 }
148 },
149 spec: {
150 securityContext: {
151 runAsUser: 1000,
152 fsGroup: 1000,
153 },
154 initContainers: [
155 getFixpermsContainer({volumeMounts}),
156 ],
157 containers: [
158 {
159 image, command: [], args,
160 name,
161 volumeMounts,
162 },
163 ],
164 volumes
165 }
166 }
167 }
168 }
171export const tekuBeaconApiNodePortTmpl = ({name, selfName, port, nodePort}: {name: string, selfName: string, port: number, nodePort: number}) => {
172 return {
173 apiVersion: 'v1',
174 kind: 'Service',
175 metadata: {
176 name: selfName,
177 },
178 spec: {
179 ports: [
180 {
181 nodePort,
182 port,
183 protocol: 'TCP',
184 targetPort: port
185 }
186 ],
187 selector: {
188 name
189 },
190 type: 'NodePort'
191 },
192 }
196export const doApplyTeku = async ({ethNetwork, tekuNodePort: p2pPort, localValSecretsPath, ethRewardAddr, enableValidator, relayHostPort, eth1ClientName, wsCheckpointS, image, tekuBeaconApiNodePort, guardValidatorFn, ethValidatorNums}: {
197 ethNetwork: string, tekuNodePort: number, localValSecretsPath?: string, ethRewardAddr: string,
198 enableValidator: boolean, relayHostPort: string | null, eth1ClientName: string,
199 wsCheckpointS: string | null, image: string, tekuBeaconApiNodePort: number | null,
200 guardValidatorFn?: GuardValidatorFn, ethValidatorNums?: number[],
201}) => {
202 const name = `teku-${ethNetwork}`
203 const {cluster_name, cfApiKeySecretName, domainNames} = getKlusterCtx()
204 const action = getAction()
206 const {execEngProv, httpWeb3Prov, jwtSecretName} = getEth2Vals({ethNetwork, eth1ClientName})
208 const keystoresSecName = name + '-keystores'
210 const vcapiKeystorePwSecName = `${name}-vcapi-keystore-pw`
211 const vcapiKeystoresName = `${name}-vcapi-keystores`
213 const deployment = tekuDeploymentTmpl({
214 image,
215 name, ethNetwork, p2pPort, keystoresSecName, httpWeb3Prov, execEngProv,
216 jwtSecretName, ethRewardAddr, enableValidator, relayHostPort,
217 wsCheckpointS, vcapiKeystorePwSecName, vcapiKeystoresName
218 })
219 const claimCheckConfigMap = await withClaimCheck({
220 deployment, enabled: enableValidator === true && action != 'delete',
221 ethValidatorNum: ethValidatorNums?.[0], cluster_name, domainName: domainNames?.[0], cfApiKeySecretName,
222 })
224 let resources = [
225 ...(claimCheckConfigMap ? [claimCheckConfigMap] : []),
226 deployment,
227 kubeSvcTmpl({name, portNos: [
228 5051, // rest for check sync status
229 5052,
230 ]}),
231 dualNodePortTmpl({name, selfName: name + '-p2p', nodePort: p2pPort}),
232 secretFileTemplate({name: vcapiKeystorePwSecName as any, kubeName: vcapiKeystorePwSecName}),
234 ...await genericPvcTmplA({action, cluster_name, name: vcapiKeystoresName, sizeGb: 40, useM2: true}),
235 ...await genericPvcTmplA({action, cluster_name, name, sizeGb: 500, useM2: true}),
236 ]
237 if (tekuBeaconApiNodePort) {
238 resources.push(
239 tekuBeaconApiNodePortTmpl({name, selfName: name + 'privbeaconapi', port: 5051, nodePort: tekuBeaconApiNodePort})
240 )
241 }
243 if (action != 'delete') {
244 resources.push(secretFileTemplate({name: jwtSecretName as any, kubeName: jwtSecretName}))
245 }
247 if (enableValidator === true) {
248 assertDefined(guardValidatorFn, {enableValidator})
249 assertDefined(localValSecretsPath, {enableValidator})
250 await guardValidatorFn({ethNetwork, name, cluster_name, action})
251 const {walletPassSecName} = getEth2Vals({ethNetwork, eth1ClientName})
253 const secretsH: Record<string, string> = {}
254 if (action != 'delete') {
255 const walletPassS = getPlainNoMappedSec(walletPassSecName as any)
256 const keystoreFiles = _.filter(fs.readdirSync(localValSecretsPath), (path) => {
257 // filter .DS_Store, deposit_data files etc
258 return _.endsWith(path, '.json') && _.startsWith(path, 'keystore-')
259 })
260 _.each(keystoreFiles, (filename) => {
261 const passTxtFilename = _.replace(filename, '.json', '.txt')
262 const keystoreVal = fs.readFileSync([localValSecretsPath, filename].join('/'), 'utf8')
263 secretsH[filename] = keystoreVal
264 secretsH[passTxtFilename] = walletPassS
265 })
266 }
267 resources = _.union(resources, [
268 secretTemplate({name: keystoresSecName, secretsH})
269 ])
270 }
272 resources = _.union(resources, cleanupResOnDelete({action, enabled: enableValidator, secNames: [keystoresSecName]}))
274 await guardOnlyRecreateDeploymentsResourcesAction({resources, action, cluster_name})