diff --git a/Tiltfile b/Tiltfile index a3b2f2c..4d582c1 100644 --- a/Tiltfile +++ b/Tiltfile @@ -5,6 +5,12 @@ helm_remote("postgresql", set=["postgresqlPassword=password", "postgresqlDatabase=mev_inspect"], ) +load('ext://secret', 'secret_from_dict') +k8s_yaml(secret_from_dict("mev-inspect-db-credentials", inputs = { + "username" : "postgres", + "password": "password", +})) + docker_build('mev-inspect', '.', live_update=[ sync('.', '/app'), diff --git a/k8s/app.yaml b/k8s/app.yaml index 4916c7f..b614467 100644 --- a/k8s/app.yaml +++ b/k8s/app.yaml @@ -23,12 +23,12 @@ spec: - name: POSTGRES_USER valueFrom: secretKeyRef: - name: mev-inspect-db-password + name: mev-inspect-db-credentials key: username - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: - name: mev-inspect-db-password + name: mev-inspect-db-credentials key: password livenessProbe: exec: diff --git a/tilt_modules/extensions.json b/tilt_modules/extensions.json index 28c435a..c59980c 100644 --- a/tilt_modules/extensions.json +++ b/tilt_modules/extensions.json @@ -9,6 +9,11 @@ "Name": "global_vars", "ExtensionRegistry": "https://github.com/tilt-dev/tilt-extensions", "TimeFetched": "2021-09-03T08:56:48.751933-04:00" + }, + { + "Name": "secret", + "ExtensionRegistry": "https://github.com/tilt-dev/tilt-extensions", + "TimeFetched": "2021-09-09T08:57:26.199313-06: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 0000000..b4e84f6 --- /dev/null +++ b/tilt_modules/secret/README.md @@ -0,0 +1,70 @@ +# 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://secret', 'secret_yaml_generic') +k8s_yaml(secret_yaml_generic('name', from_file=[...])) +``` + +### secret_from_dict + +``` +secret_from_dict(name: str, namespace: str = "", inputs = None): blob +``` + +Returns YAML for a secret from a dictionary. + +* `inputs` ( dict) - A dict of keys and values to use. Nesting is not supported + +## 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_create_generic') +secret_create_generic('gcp-key', from_file='key.json=./gcp-creds.json') +``` + +### From a dict: + +``` +load('ext://secret', 'secret_from_dict') +k8s_yaml(secret_from_dict("secrets", inputs = { + 'SOME_TOKEN' : os.getenv('SOME_TOKEN') +})) +``` + +## 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 0000000..bc5a75a --- /dev/null +++ b/tilt_modules/secret/Tiltfile @@ -0,0 +1,105 @@ +# -*- 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_from_dict(name, namespace="", inputs={}): + """Returns YAML for a generic secret + Args: + name: The configmap name. + namespace: The namespace. + inputs: A dict of keys and values to use. Nesting is not supported + Returns: + The secret YAML as a blob + """ + + args = [ + "kubectl", + "create", + "secret", + "generic", + name, + ] + + if namespace: + args.extend(["-n", namespace]) + + if type(inputs) != "dict": + fail("Bad argument to secret_from_dict, inputs was not dict typed") + + for k,v in inputs.items(): + args.extend(["--from-literal", "%s=%s" % (k,v)]) + + args.extend(["-o=yaml", "--dry-run=client"]) + return local(args, quiet=True) + +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 0000000..f66b2c4 --- /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 0000000..52dfde5 --- /dev/null +++ b/tilt_modules/secret/test/Tiltfile @@ -0,0 +1,7 @@ +load('../Tiltfile', 'secret_create_generic', 'secret_from_dict') + +k8s_yaml(secret_from_dict("secrets", inputs = { + 'SOME_TOKEN' : os.getenv('SOME_TOKEN') +})) +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 0000000..301b92f --- /dev/null +++ b/tilt_modules/secret/test/job.yaml @@ -0,0 +1,33 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: secret-verify +spec: + backoffLimit: 1 + template: + spec: + containers: + - name: secret-verify-dict + image: alpine + command: [ "/bin/echo", "$(SOME_TOKEN)" ] + env: + - name: TEST_VAR + valueFrom: + secretKeyRef: + name: secrets + key: SOME_TOKEN + - 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 diff --git a/tilt_modules/secret/test/test.sh b/tilt_modules/secret/test/test.sh new file mode 100755 index 0000000..1305c33 --- /dev/null +++ b/tilt_modules/secret/test/test.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd "$(dirname "$0")" + +export SOME_TOKEN=abc123 + +set -ex +tilt ci +tilt down --delete-namespaces