diff --git a/.gitignore b/.gitignore index 02005ab04..136454bcc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ bin target /mutagen.sh venv -.env \ No newline at end of file +.env +bigtable-admin.json +bigtable-writer.json diff --git a/DEVELOP.md b/DEVELOP.md index 80c2c195a..e5771b74c 100644 --- a/DEVELOP.md +++ b/DEVELOP.md @@ -81,3 +81,10 @@ To Solana as CPI instruction: Set the include path: ![](https://i.imgur.com/bDij6Cu.png) + + +## BigTable event persistence + +Guardian events can be persisted to a BigTable instance. Launch the devnet with flags supplying your database info to enable forwarding events: + + tilt up -- --num=1 --bigTablePersistence --gcpProject=your-project-id --bigTableKeyPath=./your-service-account-key.json diff --git a/Tiltfile b/Tiltfile index 045ff4ebe..7603c4f1a 100644 --- a/Tiltfile +++ b/Tiltfile @@ -6,6 +6,7 @@ # load("ext://namespace", "namespace_create", "namespace_inject") +load('ext://secret', 'secret_yaml_generic') # Runtime configuration @@ -17,9 +18,18 @@ config.define_string("num", False, "Number of guardian nodes to run") # config.define_string("namespace", False, "Kubernetes namespace to use") +# These arguments will enable writing Guardian events to a BigTable instance. +# Writing to BigTable is optional. These arguments are not required to run the devnet. +config.define_bool("bigTablePersistence", False, "Enable forwarding guardian events to BigTable") +config.define_string("gcpProject", False, "GCP project ID for BigTable persistence") +config.define_string("bigTableKeyPath", False, "Path to BigTable json key file") + cfg = config.parse() num_guardians = int(cfg.get("num", "5")) namespace = cfg.get("namespace", "wormhole") +bigTablePersistence = cfg.get("bigTablePersistence", False) +gcpProject = cfg.get("gcpProject", None) +bigTableKeyPath = cfg.get("bigTableKeyPath", "./bigtable-writer.json") # namespace @@ -47,6 +57,14 @@ local_resource( # bridge +if bigTablePersistence: + k8s_yaml_with_ns( + secret_yaml_generic( + "bridge-bigtable-key", + from_file = "bigtable-key.json=" + bigTableKeyPath + ) + ) + docker_build( ref = "guardiand-image", context = "bridge", @@ -63,6 +81,12 @@ def build_bridge_yaml(): if container["name"] != "guardiand": fail("container 0 is not guardiand") container["command"] += ["--devNumGuardians", str(num_guardians)] + if bigTablePersistence: + container["command"] += [ + "--bigTablePersistenceEnabled", + "--bigTableGCPProject", + gcpProject + ] return encode_yaml_stream(bridge_yaml) diff --git a/bridge/cmd/guardiand/bridge.go b/bridge/cmd/guardiand/bridge.go index 54a7fbc8c..3a73d5aab 100644 --- a/bridge/cmd/guardiand/bridge.go +++ b/bridge/cmd/guardiand/bridge.go @@ -79,6 +79,12 @@ var ( publicRPC *string publicREST *string + + bigTablePersistenceEnabled *bool + bigTableGCPProject *string + bigTableInstanceName *string + bigTableTableName *string + bigTableKeyPath *string ) func init() { @@ -119,6 +125,12 @@ func init() { publicRPC = BridgeCmd.Flags().String("publicRPC", "", "Listen address for public gRPC interface") publicREST = BridgeCmd.Flags().String("publicREST", "", "Listen address for public REST interface") + + bigTablePersistenceEnabled = BridgeCmd.Flags().Bool("bigTablePersistenceEnabled", false, "Turn on forwarding events to BigTable") + bigTableGCPProject = BridgeCmd.Flags().String("bigTableGCPProject", "", "Google Cloud project ID for storing events") + bigTableInstanceName = BridgeCmd.Flags().String("bigTableInstanceName", "", "BigTable instance name for storing events") + bigTableTableName = BridgeCmd.Flags().String("bigTableTableName", "", "BigTable table name to store events in") + bigTableKeyPath = BridgeCmd.Flags().String("bigTableKeyPath", "", "Path to json Service Account key") } var ( @@ -312,6 +324,21 @@ func runBridge(cmd *cobra.Command, args []string) { logger.Fatal("Please specify --terraContract") } + if *bigTablePersistenceEnabled { + if *bigTableGCPProject == "" { + logger.Fatal("Please specify --bigTableGCPProject") + } + if *bigTableInstanceName == "" { + logger.Fatal("Please specify --bigTableInstanceName") + } + if *bigTableTableName == "" { + logger.Fatal("Please specify --bigTableTableName") + } + if *bigTableKeyPath == "" { + logger.Fatal("Please specify --bigTableKeyPath") + } + } + ethContractAddr := eth_common.HexToAddress(*ethContract) bscContractAddr := eth_common.HexToAddress(*bscContract) solBridgeAddress, err := solana_types.PublicKeyFromBase58(*solanaBridgeAddress) diff --git a/devnet/bridge.yaml b/devnet/bridge.yaml index 2565e6ae4..9943b29cb 100644 --- a/devnet/bridge.yaml +++ b/devnet/bridge.yaml @@ -44,12 +44,21 @@ spec: # mount shared between containers for runtime state - name: bridge-rundir emptyDir: {} + - name: bridge-keysdir + secret: + secretName: bridge-bigtable-key + optional: true + items: + - key: bigtable-key.json + path: bigtable-key.json containers: - name: guardiand image: guardiand-image volumeMounts: - mountPath: /run/bridge name: bridge-rundir + - mountPath: /tmp/mounted-keys + name: bridge-keysdir command: # Uncomment this to enable in-place debugging using dlv # (not suitable for regular development since the process will no longer restart on its own) @@ -93,6 +102,12 @@ spec: - /tmp/admin.sock - --dataDir - /tmp/data + - --bigTableInstanceName + - wormhole-dev + - --bigTableTableName + - v2Events + - --bigTableKeyPath + - /tmp/mounted-keys/bigtable-key.json # - --logLevel=debug securityContext: capabilities: diff --git a/tilt_modules/extensions.json b/tilt_modules/extensions.json index 2acaef3d1..aac34fa06 100644 --- a/tilt_modules/extensions.json +++ b/tilt_modules/extensions.json @@ -4,6 +4,11 @@ "Name": "namespace", "ExtensionRegistry": "https://github.com/tilt-dev/tilt-extensions", "TimeFetched": "2020-12-05T16:06:07.229737938+01:00" + }, + { + "Name": "secret", + "ExtensionRegistry": "https://github.com/tilt-dev/tilt-extensions", + "TimeFetched": "2021-07-01T15:08:09.818136358-05:00" } ] } \ No newline at end of file diff --git a/tilt_modules/secret/README.md b/tilt_modules/secret/README.md new file mode 100644 index 000000000..4b88f5db6 --- /dev/null +++ b/tilt_modules/secret/README.md @@ -0,0 +1,51 @@ +# Secret + +Author: [Nick Santos](https://github.com/nicks) + +Helper functions for creating Kubernetes secrets. + +## Functions + +### secret_yaml_generic + +``` +secret_yaml_generic(name: str, namespace: str = "", from_file: Union[str, List] = None, secret_type: str = None): Blob +``` + +Returns YAML for a generic secret. + +* `from_file` ( str ) – equivalent to `kubectl create secret --from-file` +* `secret_type` ( str ) - equivalent to `kubectl create secret --type` + +### secret_create_generic + +``` +secret_create_generic(name: str, namespace: str = "", from_file: Union[str, List] = None, secret_type: str = None) +``` + +Deploys a secret to the cluster. Equivalent to + +``` +load('ext://namespace', 'secret_yaml_generic') +k8s_yaml(secret_yaml_generic('name', from_file=[...])) +``` + +## Example Usage + +### For a Postgres password: + +``` +load('ext://secret', 'secret_create_generic') +secret_create_generic('pgpass', from_file='.pgpass=./.pgpass') +``` + +### For Google Cloud Platform Key: + +``` +load('ext://secret', 'secret_generic_create') +secret_create_generic('gcp-key', from_file='key.json=./gcp-creds.json') +``` + +## Caveats + +- This extension doesn't do any validation to confirm that names or namespaces are valid. diff --git a/tilt_modules/secret/Tiltfile b/tilt_modules/secret/Tiltfile new file mode 100644 index 000000000..46f8c07e1 --- /dev/null +++ b/tilt_modules/secret/Tiltfile @@ -0,0 +1,75 @@ +# -*- mode: Python -*- + +def secret_yaml_generic(name, namespace="", from_file=None, secret_type=None, from_env_file=None): + """Returns YAML for a generic secret + + Args: + name: The secret name. + namespace: The namespace. + from_file: Use the from-file secret generator. May be a string or a list of strings. + Example: ["ssh--privatekey=path/to/id_rsa", "ssh-publickey=path/to/id_rsa.pub"] + from_env_file: Specify the path to a file to read lines of key=val pairs to create a secret + (i.e. a Docker .env file) + secret_type (optional): Specify the type of the secret + Example: 'kubernetes.io/dockerconfigjson' + + Returns: + The secret YAML as a blob + """ + + args = [ + "kubectl", + "create", + "secret", + "generic", + name, + ] + + if namespace: + args.extend(["-n", namespace]) + + generator = False + if from_file: + if type(from_file) == "string": + args.extend(["--from-file", from_file]) + generator = True + elif type(from_file) == "list": + for f in from_file: + args.extend(["--from-file", f]) + generator = True + else: + fail("Bad from_file argument: %s" % from_file) + + if from_env_file: + if type(from_env_file) != "string": + fail("from_env_file only accepts strings") + + args.extend(["--from-env-file", from_env_file]) + generator = True + + if not generator: + fail("No secret generator specified") + + if secret_type: + if type(secret_type) == "string": + args.extend(["--type", secret_type]) + else: + fail("Bad secret_type argument: %s" % secret_type) + + args.extend(["-o=yaml", "--dry-run=client"]) + return local(args) + +def secret_create_generic(name, namespace="", from_file=None, secret_type=None, from_env_file=None): + """Creates a secret in the current Kubernetes cluster. + + Args: + name: The secret name. + namespace: The namespace. + from_file: Use the from-file secret generator. May be a string or a list of strings. + Example: ["ssh--privatekey=path/to/id_rsa", "ssh-publickey=path/to/id_rsa.pub"] + from_env_file: Specify the path to a file to read lines of key=val pairs to create a secret + (i.e. a Docker .env file) + secret_type (optional): Specify the type of the secret + Example: 'kubernetes.io/dockerconfigjson' + """ + k8s_yaml(secret_yaml_generic(name, namespace, from_file, secret_type, from_env_file)) diff --git a/tilt_modules/secret/test/.pgpass b/tilt_modules/secret/test/.pgpass new file mode 100644 index 000000000..f66b2c47d --- /dev/null +++ b/tilt_modules/secret/test/.pgpass @@ -0,0 +1 @@ +hostname:5432:database:username:password \ No newline at end of file diff --git a/tilt_modules/secret/test/Tiltfile b/tilt_modules/secret/test/Tiltfile new file mode 100644 index 000000000..e283e345f --- /dev/null +++ b/tilt_modules/secret/test/Tiltfile @@ -0,0 +1,4 @@ +load('../Tiltfile', 'secret_create_generic') + +secret_create_generic('pgpass', namespace='default', from_file='.pgpass=./.pgpass') +k8s_yaml('job.yaml') diff --git a/tilt_modules/secret/test/job.yaml b/tilt_modules/secret/test/job.yaml new file mode 100644 index 000000000..f4989b301 --- /dev/null +++ b/tilt_modules/secret/test/job.yaml @@ -0,0 +1,24 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: secret-verify +spec: + backoffLimit: 1 + template: + spec: + containers: + - name: secret-verify + image: alpine + command: ["grep", "password", "/var/secrets/pgpass/.pgpass"] + volumeMounts: + - name: pgpass + mountPath: /var/secrets/pgpass + env: + - name: PGPASSFILE + value: /var/secrets/pgpass/.pgpass + restartPolicy: Never + volumes: + - name: pgpass + secret: + secretName: pgpass + defaultMode: 0600 \ No newline at end of file diff --git a/tilt_modules/secret/test/test.sh b/tilt_modules/secret/test/test.sh new file mode 100755 index 000000000..89fcf370f --- /dev/null +++ b/tilt_modules/secret/test/test.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cd $(dirname $0) + +set -ex +tilt ci +tilt down --delete-namespaces