Browse Source

Add caddy wrapper, export hostname

Anuj Bansal 3 years ago
parent
commit
79d139c83c

+ 1 - 0
devops/infrastructure/query-node/.gitignore

@@ -3,3 +3,4 @@
 kubeconfig.yml
 package-lock.json
 .env
+Pulumi.*.yaml

+ 0 - 5
devops/infrastructure/query-node/Pulumi.dev.yaml

@@ -1,5 +0,0 @@
-encryptionsalt: v1:6JCZJ+FPZ/o=:v1:axvmG3ZflvqylUwZ:8J9F+CmjCVz8Z8lY9LhB0PlMHSIk8w==
-config:
-  aws:profile: joystream-user
-  aws:region: us-east-1
-  query-node:isMinikube: "false"

+ 3 - 0
devops/infrastructure/query-node/Pulumi.yaml

@@ -10,6 +10,9 @@ template:
     isMinikube:
       description: Whether you are deploying to minikube
       default: false
+    isLoadBalancerReady:
+      description: Whether the load balancer service is ready and has been assigned an IP
+      default: false
     membersFilePath:
       description: Path to members.json file for processor initialization
     workersFilePath:

+ 13 - 1
devops/infrastructure/query-node/README.md

@@ -47,13 +47,25 @@ After cloning this repo, from this working directory, run these commands:
    $ puluim config set isMinikube false
    ```
 
-1. Create a `.env` file in this directory and set the database and other variables in it
+1. Create a `.env` file in this directory (`cp ../../../.env ./.env`) and set the database and other variables in it
+
+   Make sure to set `GRAPHQL_SERVER_PORT=4001`
 
 1. Stand up the Kubernetes cluster:
 
    Running `pulumi up -y` will deploy the EKS cluster. Note, provisioning a
    new EKS cluster takes between 10-15 minutes.
 
+1. Once the stack is up and running, we will modify the Caddy config to get SSL certificate for the load balancer
+
+   Modify the config variable `isLoadBalancerReady`
+
+   ```bash
+   $ pulumi config set isLoadBalancerReady true
+   ```
+
+   Run `pulumi up -y` to update the Caddy config
+
 1. Access the Kubernetes Cluster using `kubectl`
 
    To access your new Kubernetes cluster using `kubectl`, we need to set up the

+ 137 - 0
devops/infrastructure/query-node/caddy.ts

@@ -0,0 +1,137 @@
+import * as k8s from '@pulumi/kubernetes'
+import * as pulumi from '@pulumi/pulumi'
+import * as dns from 'dns'
+
+/**
+ * ServiceDeployment is an example abstraction that uses a class to fold together the common pattern of a
+ * Kubernetes Deployment and its associated Service object.
+ */
+export class CaddyServiceDeployment extends pulumi.ComponentResource {
+  public readonly deployment: k8s.apps.v1.Deployment
+  public readonly service: k8s.core.v1.Service
+  public readonly hostname?: pulumi.Output<string>
+  public readonly primaryEndpoint?: pulumi.Output<string>
+  public readonly secondaryEndpoint?: pulumi.Output<string>
+
+  constructor(name: string, args: ServiceDeploymentArgs, opts?: pulumi.ComponentResourceOptions) {
+    super('k8sjs:service:ServiceDeployment', name, {}, opts)
+
+    const labels = { app: name }
+    let volumes: pulumi.Input<pulumi.Input<k8s.types.input.core.v1.Volume>[]> = []
+    let caddyVolumeMounts: pulumi.Input<pulumi.Input<k8s.types.input.core.v1.VolumeMount>[]> = []
+
+    async function lookupPromise(url: string): Promise<dns.LookupAddress[]> {
+      return new Promise((resolve, reject) => {
+        dns.lookup(url, { all: true }, (err: any, addresses: dns.LookupAddress[]) => {
+          if (err) reject(err)
+          resolve(addresses)
+        })
+      })
+    }
+
+    this.service = new k8s.core.v1.Service(
+      name,
+      {
+        metadata: {
+          name: name,
+          namespace: args.namespaceName,
+          labels: labels,
+        },
+        spec: {
+          type: 'LoadBalancer',
+          ports: [
+            { name: 'http', port: 80 },
+            { name: 'https', port: 443 },
+          ],
+          selector: labels,
+        },
+      },
+      { parent: this }
+    )
+
+    this.hostname = this.service.status.loadBalancer.ingress[0].hostname
+
+    if (args.lbReady) {
+      let caddyConfig: pulumi.Output<string>
+      const lbIps: pulumi.Output<dns.LookupAddress[]> = this.hostname.apply((dnsName) => {
+        return lookupPromise(dnsName)
+      })
+
+      function getProxyString(ipAddress: pulumi.Output<string>) {
+        return pulumi.interpolate`${ipAddress}.nip.io/indexer/* {
+          uri strip_prefix /indexer
+          reverse_proxy query-node:4000
+        }
+
+        ${ipAddress}.nip.io/graphql/* {
+          uri strip_prefix /graphql
+          reverse_proxy query-node:8081
+        }
+        `
+      }
+
+      caddyConfig = pulumi.interpolate`${getProxyString(lbIps[0].address)}
+        ${getProxyString(lbIps[1].address)}`
+
+      this.primaryEndpoint = pulumi.interpolate`${lbIps[0].address}.nip.io`
+      this.secondaryEndpoint = pulumi.interpolate`${lbIps[1].address}.nip.io`
+
+      const keyConfig = new k8s.core.v1.ConfigMap(
+        name,
+        {
+          metadata: { namespace: args.namespaceName, labels: labels },
+          data: { 'fileData': caddyConfig },
+        },
+        { parent: this }
+      )
+      const keyConfigName = keyConfig.metadata.apply((m) => m.name)
+
+      caddyVolumeMounts.push({
+        mountPath: '/etc/caddy/Caddyfile',
+        name: 'caddy-volume',
+        subPath: 'fileData',
+      })
+      volumes.push({
+        name: 'caddy-volume',
+        configMap: {
+          name: keyConfigName,
+        },
+      })
+    }
+
+    this.deployment = new k8s.apps.v1.Deployment(
+      name,
+      {
+        metadata: { namespace: args.namespaceName, labels: labels },
+        spec: {
+          selector: { matchLabels: labels },
+          replicas: 1,
+          template: {
+            metadata: { labels: labels },
+            spec: {
+              containers: [
+                {
+                  name: 'caddy',
+                  image: 'caddy',
+                  ports: [
+                    { name: 'caddy-http', containerPort: 80 },
+                    { name: 'caddy-https', containerPort: 443 },
+                  ],
+                  volumeMounts: caddyVolumeMounts,
+                },
+              ],
+              volumes,
+            },
+          },
+        },
+      },
+      { parent: this }
+    )
+  }
+}
+
+export interface ServiceDeploymentArgs {
+  namespaceName: pulumi.Output<string>
+  lbReady?: boolean
+  isMinikube?: boolean
+}

+ 9 - 3
devops/infrastructure/query-node/index.ts

@@ -5,6 +5,7 @@ import * as pulumi from '@pulumi/pulumi'
 import { configMapFromFile } from './configMap'
 import * as k8s from '@pulumi/kubernetes'
 import * as s3Helpers from './s3Helpers'
+import { CaddyServiceDeployment } from './caddy'
 import { workers } from 'cluster'
 // import * as fs from 'fs'
 
@@ -424,9 +425,9 @@ const service = new k8s.core.v1.Service(
     metadata: {
       labels: appLabels,
       namespace: namespaceName,
+      name: 'query-node',
     },
     spec: {
-      type: isMinikube ? 'NodePort' : 'LoadBalancer',
       ports: [
         { name: 'port-1', port: 8081, targetPort: 'graph-ql-port' },
         { name: 'port-2', port: 4000, targetPort: 4002 },
@@ -441,6 +442,11 @@ const service = new k8s.core.v1.Service(
 export const serviceName = service.metadata.name
 
 // When "done", this will print the public IP.
-export let serviceHostname: pulumi.Output<string>
+// export let serviceHostname: pulumi.Output<string>
 
-serviceHostname = service.status.loadBalancer.ingress[0].hostname
+// serviceHostname = service.status.loadBalancer.ingress[0].hostname
+const lbReady = config.get('isLoadBalancerReady') === 'true'
+const caddy = new CaddyServiceDeployment('caddy-proxy', { lbReady, namespaceName: namespaceName }, resourceOptions)
+
+export const endpoint1 = caddy.primaryEndpoint
+export const endpoint2 = caddy.secondaryEndpoint