import { execCmd } from './cmd-utils' ; import { coerceContext , getClusterManagerForContext , readableContext } from './context-utils' ; import { envVar , fetchEnv } from './env-utils' ; import { CloudProvider } from './k8s-cluster/base' ; import { GCPClusterConfig } from './k8s-cluster/gcp' ; import { TerraformVars } from './terraform' ; import { deployModule , destroyModule } from './vm-testnet-utils' ; const FORNO_TERRAFORM_MODULE_NAME = 'forno' ; export async function deployForno ( celoEnv ) { const contexts = fetchEnv ( envVar . FORNO_FULL_NODE_CONTEXTS ) . split ( ',' ) . map ( coerceContext ) ; console . info ( 'Deploying Forno with full node contexts:' , contexts ) ; const terraformVars = await getFornoTerraformVars ( celoEnv , contexts ) ; await deployModule ( celoEnv , FORNO_TERRAFORM_MODULE_NAME , terraformVars ) ; console . info ( 'Note: in order to have an SSL certificate be properly provisioned, DNS entries for the relevant domains must point to the printed IP above.' ) ; } export async function destroyForno ( celoEnv ) { const contexts = fetchEnv ( envVar . FORNO_FULL_NODE_CONTEXTS ) . split ( ',' ) . map ( coerceContext ) ; console . info ( 'DESTROYING Forno' ) ; const terraformVars = await getFornoTerraformVars ( celoEnv , contexts ) ; await destroyModule ( celoEnv , FORNO_TERRAFORM_MODULE_NAME , terraformVars ) ; } interface ContextInfoTerraformVars { service_network_endpoint_group_name : string ; zone : string ; } async function getFornoTerraformVars ( celoEnv , contexts ) { let gcloudProject ; const getContextInfos = async ( port , service , namespace ) : Promise < { [ context ] : ContextInfoTerraformVars ; } > => contexts . reduce ( async ( aggPromise , context ) => { const agg = await aggPromise ; const clusterManager = getClusterManagerForContext ( celoEnv , context ) ; if ( clusterManager . cloudProvider !== CloudProvider . GCP ) { throw Error ( `Forno only accepts GCP contexts, context ${ context } is ${ clusterManager . cloudProvider }` ) ; } const contextGcloudProject = ( clusterManager . clusterConfig as GCPClusterConfig ) . projectName ; if ( gcloudProject === undefined ) { gcloudProject = contextGcloudProject ; } else if ( gcloudProject !== contextGcloudProject ) { throw Error ( `All contexts must be in the same Google Cloud project` ) ; } await clusterManager . switchToClusterContext ( true ) ; const [ output ] = await execCmd ( `kubectl get svc ${ service } -n ${ namespace } -o jsonpath="{.metadata.annotations.cloud\\.google\\.com/neg-status}"` ) ; if ( ! output . trim ( ) ) { throw Error ( `Expected cloud.google.com/neg-status annotation for service ${ service }` ) ; } const outputParsed = JSON . parse ( output ) ; if ( ! outputParsed . network_endpoint_groups [ port ] || ! outputParsed . zones . length ) { throw Error ( `Expected NEG for ${ port } and > 0 zones, instead got NEG: ${ outputParsed . network_endpoint_groups [ port ] } and zones ${ outputParsed . zones }` ) ; } return { ... agg , [ readableContext ( context ) ] : { service_network_endpoint_group_name : outputParsed . network_endpoint_groups [ port ] , zone : outputParsed . zones [ 0 ] , } , } ; } , Promise . resolve ( { } ) ) ; const domains = fetchEnv ( envVar . FORNO_DOMAINS ) . split ( ',' ) . map ( ( domain ) => { if ( ! domain . endsWith ( '.' ) ) { return `${ domain }.` ; } return domain ; } ) ; const HTTP_RPC_PORT = 8545 ; const WS_RPC_PORT = 8546 ; const KONG_RPC_PORT = 80 ; const contextInfosHttp = await getContextInfos ( HTTP_RPC_PORT , `${ celoEnv }-fullnodes-rpc` , celoEnv ) ; const contextInfosWs = await getContextInfos ( WS_RPC_PORT , `${ celoEnv }-fullnodes-rpc` , celoEnv ) ; const contextInfosKong = await getContextInfos ( KONG_RPC_PORT , 'kong' , 'kong' ) ; const bannedCIDRs = fetchEnv ( envVar . FORNO_BANNED_CIDR ) . split ( ',' ) ; return { backend_max_requests_per_second : '100' , backend_max_requests_per_second_kong : '10000' , celo_env : celoEnv , context_info_http : JSON . stringify ( contextInfosHttp ) , context_info_ws : JSON . stringify ( contextInfosWs ) , context_info_kong : JSON . stringify ( contextInfosKong ) , gcloud_credentials_path : fetchEnv ( envVar . GOOGLE_APPLICATION_CREDENTIALS ) , gcloud_project : gcloudProject ! , ssl_cert_domains : JSON . stringify ( domains ) , banned_cidr : JSON . stringify ( bannedCIDRs ) , vpc_network_name : fetchEnv ( envVar . FORNO_VPC_NETWORK_NAME ) , } ; }