Merge pull request #64 from flashbots/kube2
Add support for running mev-inspect on local kubernetes
This commit is contained in:
commit
5a4308e562
3
.gitignore
vendored
3
.gitignore
vendored
@ -16,3 +16,6 @@ htmlcov
|
||||
|
||||
# don't commit cache
|
||||
cache
|
||||
|
||||
# k8s
|
||||
.helm
|
||||
|
@ -6,14 +6,16 @@ RUN pip install -U pip \
|
||||
|
||||
ENV PATH="${PATH}:/root/.poetry/bin"
|
||||
|
||||
COPY . /app
|
||||
COPY ./pyproject.toml /app/pyproject.toml
|
||||
COPY ./poetry.lock /app/poetry.lock
|
||||
WORKDIR /app/
|
||||
|
||||
# poetry uses virtual env by default, turn this off inside container
|
||||
RUN poetry config virtualenvs.create false && \
|
||||
poetry install
|
||||
|
||||
COPY . /app
|
||||
|
||||
# easter eggs 😝
|
||||
RUN echo "PS1='🕵️:\[\033[1;36m\]\h \[\033[1;34m\]\W\[\033[0;35m\]\[\033[1;36m\]$ \[\033[0m\]'" >> ~/.bashrc
|
||||
|
||||
CMD /bin/bash
|
||||
CMD ["/bin/bash"]
|
||||
|
22
Tiltfile
Normal file
22
Tiltfile
Normal file
@ -0,0 +1,22 @@
|
||||
load('ext://helm_remote', 'helm_remote')
|
||||
helm_remote("postgresql",
|
||||
repo_name='bitnami',
|
||||
repo_url='https://charts.bitnami.com/bitnami',
|
||||
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'),
|
||||
run('cd /app && poetry install',
|
||||
trigger='./pyproject.toml'),
|
||||
],
|
||||
)
|
||||
|
||||
k8s_yaml("k8s/app.yaml")
|
@ -1,5 +1,3 @@
|
||||
import os
|
||||
|
||||
from logging.config import fileConfig
|
||||
|
||||
from sqlalchemy import engine_from_config
|
||||
@ -7,10 +5,12 @@ from sqlalchemy import pool
|
||||
|
||||
from alembic import context
|
||||
|
||||
from mev_inspect.db import get_sqlalchemy_database_uri
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
config.set_main_option("sqlalchemy.url", os.environ["SQLALCHEMY_DATABASE_URI"])
|
||||
config.set_main_option("sqlalchemy.url", get_sqlalchemy_database_uri())
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
|
39
k8s/app.yaml
Normal file
39
k8s/app.yaml
Normal file
@ -0,0 +1,39 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mev-inspect-deployment
|
||||
labels:
|
||||
app: mev-inspect
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mev-inspect
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mev-inspect
|
||||
spec:
|
||||
containers:
|
||||
- name: mev-inspect
|
||||
image: mev-inspect:latest
|
||||
command: [ "/bin/bash", "-c", "--" ]
|
||||
args: [ "while true; do sleep 30; done;" ]
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mev-inspect-db-credentials
|
||||
key: username
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mev-inspect-db-credentials
|
||||
key: password
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- ls
|
||||
- /
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 5
|
@ -4,8 +4,16 @@ from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
|
||||
def get_sqlalchemy_database_uri():
|
||||
username = os.getenv("POSTGRES_USER")
|
||||
password = os.getenv("POSTGRES_PASSWORD")
|
||||
server = "postgresql"
|
||||
db_name = "mev_inspect"
|
||||
return f"postgresql://{username}:{password}@{server}/{db_name}"
|
||||
|
||||
|
||||
def get_engine():
|
||||
return create_engine(os.getenv("SQLALCHEMY_DATABASE_URI"))
|
||||
return create_engine(get_sqlalchemy_database_uri())
|
||||
|
||||
|
||||
def get_session():
|
||||
|
@ -42,6 +42,8 @@ attach = 'scripts.poetry.docker:attach'
|
||||
exec = 'scripts.poetry.docker:exec'
|
||||
inspect = 'scripts.poetry.inspect:inspect'
|
||||
inspect-many = 'scripts.poetry.inspect:inspect_many'
|
||||
inspect-block = 'scripts.inspect_block:inspect_block'
|
||||
inspect-many-blocks = 'scripts.inspect_block:inspect_many_blocks'
|
||||
|
||||
[tool.black]
|
||||
exclude = '''
|
||||
|
19
tilt_modules/extensions.json
Normal file
19
tilt_modules/extensions.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"Extensions": [
|
||||
{
|
||||
"Name": "helm_remote",
|
||||
"ExtensionRegistry": "https://github.com/tilt-dev/tilt-extensions",
|
||||
"TimeFetched": "2021-09-03T08:56:46.938205-04:00"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
13
tilt_modules/global_vars/README.md
Normal file
13
tilt_modules/global_vars/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Git Resource
|
||||
|
||||
Author: [Bob Jackman](https://github.com/kogi)
|
||||
|
||||
An extension for reading/writing global variable values
|
||||
|
||||
## Usage
|
||||
|
||||
```python
|
||||
set_global('foo', some_value)
|
||||
print(get_global('foo'))
|
||||
unset_global('foo')
|
||||
```
|
11
tilt_modules/global_vars/Tiltfile
Normal file
11
tilt_modules/global_vars/Tiltfile
Normal file
@ -0,0 +1,11 @@
|
||||
def get_global(name):
|
||||
return os.getenv(_get_env_name(name))
|
||||
|
||||
def set_global(name, value):
|
||||
os.putenv(_get_env_name(name), value)
|
||||
|
||||
def unset_global(name):
|
||||
os.unsetenv(_get_env_name(name))
|
||||
|
||||
def _get_env_name(name):
|
||||
return 'TILT_GLOBAL_VAR_%s' % name.upper()
|
41
tilt_modules/helm_remote/README.md
Normal file
41
tilt_modules/helm_remote/README.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Helm Remote
|
||||
|
||||
Author: [Bob Jackman](https://github.com/kogi)
|
||||
|
||||
Install a remotely hosted Helm chart in a way that it will be properly uninstalled when running `tilt down`
|
||||
|
||||
## Usage
|
||||
|
||||
#### Install a Remote Chart
|
||||
|
||||
```py
|
||||
load('ext://helm_remote', 'helm_remote')
|
||||
helm_remote('myChartName')
|
||||
```
|
||||
|
||||
##### Additional Parameters
|
||||
|
||||
```
|
||||
helm_remote(chart, repo_url='', repo_name='', release_name='', namespace='', version='', username='', password='', values=[], set=[])
|
||||
```
|
||||
|
||||
* `chart` ( str ) – the name of the chart to install
|
||||
* `repo_name` ( str ) – the name of the repo within which to find the chart (assuming the repo is already added locally)
|
||||
<br> if omitted, defaults to the same value as `chart_name`
|
||||
* `repo_url` ( str ) – the URL of the repo within which to find the chart (equivalent to `helm repo add <repo_name> <repo_url>`)
|
||||
* `release_name` (str) - the name of the helm release
|
||||
<br> if omitted, defaults to the same value as `chart_name`
|
||||
* `namespace` ( str ) – the namespace to deploy the chart to (equivalent to helm's `--namespace <namespace>` flags)
|
||||
* `version` ( str ) – the version of the chart to install. If omitted, defaults to latest version (equivalent to helm's `--version` flag)
|
||||
* `username` ( str ) – repository authentication username, if needed (equivalent to helm's `--username` flag)
|
||||
* `password` ( str ) – repository authentication password, if needed (equivalent to helm's `--password` flag)
|
||||
* `values` ( Union [ str , List [ str ]]) – Specify one or more values files (in addition to the values.yaml file in the chart). Equivalent to the helm's `--values` or `-f` flags
|
||||
* `set` ( Union [ str , List [ str ]]) – Directly specify one or more values (equivalent to helm's `--set` flag)
|
||||
* `allow_duplicates` ( bool ) - Allow duplicate resources. Usually duplicate resources indicate a programmer error.
|
||||
But some charts specify resources twice.
|
||||
* `create_namespace` ( bool ) - Create the namespace specified in `namespace` ( equivalent to helm's `--create_namespace` flag)
|
||||
|
||||
#### Change the Cache Location
|
||||
|
||||
By default `helm_remote` will store retrieved helm charts in the `.helm` directory at your workspace's root.
|
||||
This location can be customized by calling `os.putenv('TILT_HELM_REMOTE_CACHE_DIR', new_directory)` before loading the module.
|
214
tilt_modules/helm_remote/Tiltfile
Normal file
214
tilt_modules/helm_remote/Tiltfile
Normal file
@ -0,0 +1,214 @@
|
||||
load('ext://global_vars', 'get_global', 'set_global')
|
||||
|
||||
def _get_skip():
|
||||
return get_global(_get_skip_name())
|
||||
|
||||
|
||||
def _set_skip(value):
|
||||
set_global(_get_skip_name(), value)
|
||||
|
||||
|
||||
def _get_skip_name():
|
||||
return 'HELM_REMOTE_SKIP_UPDATES'
|
||||
|
||||
|
||||
if _get_skip() == None:
|
||||
_set_skip('False') # Gets set to true after the first update, preventing further updates within the same build/instance/up
|
||||
|
||||
|
||||
def _find_root_tiltfile_dir():
|
||||
# Find top-level Tilt path
|
||||
current = os.path.abspath('./')
|
||||
while current != '/':
|
||||
if os.path.exists(os.path.join(current, 'tilt_modules')):
|
||||
return current
|
||||
|
||||
current = os.path.dirname(current)
|
||||
|
||||
fail('Could not find root Tiltfile')
|
||||
|
||||
def _find_cache_dir():
|
||||
from_env = os.getenv('TILT_HELM_REMOTE_CACHE_DIR', '')
|
||||
if from_env != '':
|
||||
return from_env
|
||||
return os.path.join(_find_root_tiltfile_dir(), '.helm')
|
||||
|
||||
# this is the root directory into which remote helm charts will be pulled/cloned/untar'd
|
||||
# use `os.putenv('TILT_HELM_REMOTE_CACHE_DIR', new_dir)` to change
|
||||
helm_remote_cache_dir = _find_cache_dir()
|
||||
watch_settings(ignore=helm_remote_cache_dir)
|
||||
|
||||
# TODO: =====================================
|
||||
# if it ever becomes possible for loaded files to also load their own extensions
|
||||
# this method can be replaced by `load('ext://namespace', 'namespace_create')
|
||||
def namespace_create(name):
|
||||
"""Returns YAML for a namespace
|
||||
Args: name: The namespace name. Currently not validated.
|
||||
"""
|
||||
k8s_yaml(blob("""apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: %s
|
||||
""" % name))
|
||||
# TODO: end TODO
|
||||
# =====================================
|
||||
|
||||
|
||||
def helm_remote(chart, repo_url='', repo_name='', release_name='', values=[], set=[], namespace='', version='', username='', password='', allow_duplicates=False, create_namespace=False):
|
||||
# ======== Helper methods
|
||||
def get_local_repo(repo_name, repo_url):
|
||||
# if no repos are present, helm exit code is >0 and stderr output buffered
|
||||
added_helm_repos = decode_yaml(local('helm repo list --output yaml 2>/dev/null || true', command_bat='helm repo list --output yaml 2> nul || ver>nul', quiet=True))
|
||||
repo = [item for item in added_helm_repos if item['name'] == chart and item['url'] == repo_url] if added_helm_repos != None else []
|
||||
|
||||
return repo[0] if len(repo) > 0 else None
|
||||
|
||||
# Command string builder with common argument logic
|
||||
def build_helm_command(command, auth=None, version=None):
|
||||
command = 'helm ' + command
|
||||
|
||||
if auth != None:
|
||||
username, password = auth
|
||||
if username != '':
|
||||
command += ' --username %s' % shlex.quote(username)
|
||||
if password != '':
|
||||
command += ' --password %s' % shlex.quote(password)
|
||||
|
||||
if version != None and version != 'latest':
|
||||
command += ' --version %s' % shlex.quote(version)
|
||||
|
||||
return command
|
||||
|
||||
def fetch_chart_details(chart, repo_name, auth, version):
|
||||
command = build_helm_command('search repo %s/%s --output yaml' % (shlex.quote(repo_name), shlex.quote(chart)), None, version)
|
||||
results = decode_yaml(local(command, quiet=True))
|
||||
|
||||
return results[0] if len(results) > 0 else None
|
||||
|
||||
|
||||
# ======== Condition Incoming Arguments
|
||||
if repo_name == '':
|
||||
repo_name = chart
|
||||
if release_name == '':
|
||||
release_name = chart
|
||||
if namespace == '':
|
||||
namespace = 'default'
|
||||
if version == '':
|
||||
version = 'latest'
|
||||
|
||||
# ======== Validate before we start trusting chart/repo names
|
||||
|
||||
# Based on helm chart conventions, and the fact we don't want anyone traversing directories
|
||||
# validate is to essentially ensure there's no special characters aside from '-' being used
|
||||
# str.isalnum accepts dots, which is only dangerous when slashes are allowed
|
||||
# https://helm.sh/docs/chart_best_practices/conventions/#chart-names
|
||||
|
||||
if chart.replace('-', '').isalnum() == False or chart != chart.replace('.', ''):
|
||||
# https://helm.sh/docs/chart_best_practices/conventions/#chart-names
|
||||
fail('Chart name is not valid')
|
||||
|
||||
if repo_name != chart and repo_name.replace('-', '').isalnum() == False or repo_name != repo_name.replace('.', ''):
|
||||
# https://helm.sh/docs/chart_best_practices/conventions/#chart-names
|
||||
fail('Repo name is not valid')
|
||||
|
||||
if version != 'latest' and version != version.replace('/', '').replace('\\', ''):
|
||||
fail('Version cannot contain a forward slash')
|
||||
|
||||
|
||||
# ======== Determine state of existing helm repo
|
||||
if repo_url != '':
|
||||
local_helm_repo = get_local_repo(repo_name, repo_url)
|
||||
|
||||
if local_helm_repo == None:
|
||||
# Unaware of repo, add it
|
||||
repo_command = 'repo add %s %s' % (shlex.quote(repo_name), shlex.quote(repo_url))
|
||||
# Add authentication for adding the repository if credentials are provided
|
||||
output = str(local(build_helm_command(repo_command, (username, password)), quiet=True)).rstrip('\n')
|
||||
if 'already exists' not in output: # repo was added
|
||||
_set_skip('False')
|
||||
else:
|
||||
# Helm is already aware of the chart, update repo (unfortunately you cannot specify a single repo)
|
||||
if _get_skip() != 'True':
|
||||
repo_command = 'repo update'
|
||||
local(build_helm_command(repo_command), quiet=True)
|
||||
_set_skip('True')
|
||||
|
||||
# ======== Create Namespace
|
||||
if create_namespace and namespace != '' and namespace != 'default':
|
||||
# avoid a namespace not found error
|
||||
namespace_create(namespace) # do this early so it manages to register before we attempt to install into it
|
||||
|
||||
# ======== Initialize
|
||||
# -------- targets
|
||||
pull_target = os.path.join(helm_remote_cache_dir, repo_name, version)
|
||||
chart_target = os.path.join(pull_target, chart)
|
||||
|
||||
cached_chart_exists = os.path.exists(chart_target)
|
||||
|
||||
needs_pull = True
|
||||
|
||||
if cached_chart_exists:
|
||||
# Helm chart structure is concrete, we can trust this YAML file to exist
|
||||
cached_chart_details = read_yaml(os.path.join(chart_target, 'Chart.yaml'))
|
||||
|
||||
# check if our local cached chart matches latest remote
|
||||
remote_chart_details = fetch_chart_details(chart, repo_name, (username, password), version)
|
||||
|
||||
# pull when version mismatch
|
||||
needs_pull = cached_chart_details['version'] != remote_chart_details['version']
|
||||
|
||||
|
||||
if needs_pull:
|
||||
# -------- commands
|
||||
pull_command = 'pull %s/%s --untar --destination %s' % (repo_name, chart, pull_target)
|
||||
|
||||
# ======== Perform Installation
|
||||
if cached_chart_exists:
|
||||
local('rm -rf %s' % chart_target, command_bat='if exist %s ( rd /s /q %s )' % (chart_target, chart_target), quiet=True)
|
||||
|
||||
local(build_helm_command(pull_command, (username, password), version), quiet=True)
|
||||
|
||||
install_crds(chart, chart_target)
|
||||
|
||||
# TODO: since neither `k8s_yaml()` nor `helm()` accept resource_deps,
|
||||
# sometimes the crds haven't yet finished installing before the below tries
|
||||
# to run
|
||||
yaml = helm(chart_target, name=release_name, namespace=namespace, values=values, set=set)
|
||||
|
||||
# The allow_duplicates API is only available in 0.17.1+
|
||||
if allow_duplicates and _version_tuple() >= [0, 17, 1]:
|
||||
k8s_yaml(yaml, allow_duplicates=allow_duplicates)
|
||||
else:
|
||||
k8s_yaml(yaml)
|
||||
|
||||
return yaml
|
||||
|
||||
def _version_tuple():
|
||||
ver_string = str(local('tilt version', quiet=True))
|
||||
versions = ver_string.split(', ')
|
||||
# pull first string and remove the `v` and `-dev`
|
||||
version = versions[0].replace('-dev', '').replace('v', '')
|
||||
return [int(str_num) for str_num in version.split(".")]
|
||||
|
||||
# install CRDs as a separate resource and wait for them to be ready
|
||||
def install_crds(name, directory):
|
||||
name += '-crds'
|
||||
files = str(local(r"grep --include='*.yaml' --include='*.yml' -rEil '\bkind[^\w]+CustomResourceDefinition\s*$' %s || exit 0" % directory, quiet=True)).rstrip('\n')
|
||||
|
||||
if files == '':
|
||||
files = []
|
||||
else:
|
||||
files = files.split("\n")
|
||||
|
||||
# we're applying CRDs directly and not using helm preprocessing
|
||||
# this will cause errors!
|
||||
# since installing CRDs in this function is a nice-to-have, just skip
|
||||
# any that have preprocessing
|
||||
files = [f for f in files if str(read_file(f)).find('{{') == -1]
|
||||
|
||||
if len(files) != 0:
|
||||
local_resource(name+'-install', cmd='kubectl apply -f %s' % " -f ".join(files), deps=files) # we can wait/depend on this, but it won't cause a proper uninstall
|
||||
k8s_yaml(files) # this will cause a proper uninstall, but we can't wait/depend on it
|
||||
|
||||
# TODO: Figure out how to avoid another named resource showing up in the tilt HUD for this waiter
|
||||
local_resource(name+'-ready', resource_deps=[name+'-install'], cmd='kubectl wait --for=condition=Established crd --all') # now we can wait for those crds to finish establishing
|
7
tilt_modules/helm_remote/test/Dockerfile
Normal file
7
tilt_modules/helm_remote/test/Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM alpine
|
||||
|
||||
RUN apk update && apk add expect busybox-extras
|
||||
|
||||
ADD ./verify.exp ./verify.exp
|
||||
|
||||
ENTRYPOINT expect < verify.exp
|
17
tilt_modules/helm_remote/test/Tiltfile
Normal file
17
tilt_modules/helm_remote/test/Tiltfile
Normal file
@ -0,0 +1,17 @@
|
||||
os.putenv('TILT_HELM_REMOTE_CACHE_DIR', os.path.abspath('./.helm'))
|
||||
load('../Tiltfile', 'helm_remote')
|
||||
|
||||
# Note that .helm is in the .tiltignore!
|
||||
helm_remote('memcached', repo_url='https://charts.bitnami.com/bitnami')
|
||||
if not os.path.exists('./.helm/memcached'):
|
||||
fail('memcached failed to load in the right directory')
|
||||
|
||||
# This chart has a bunch of CRDs (including templated CRDs), so we can test the CRD init logic.
|
||||
helm_remote('gloo', repo_url='https://storage.googleapis.com/solo-public-helm',
|
||||
# The gloo chart has duplicate resources, see discussion here:
|
||||
# https://github.com/tilt-dev/tilt/issues/3656
|
||||
allow_duplicates=True)
|
||||
|
||||
docker_build('helm-remote-test-verify', '.')
|
||||
k8s_yaml('job.yaml')
|
||||
k8s_resource('helm-remote-test-verify', resource_deps=['memcached'])
|
12
tilt_modules/helm_remote/test/job.yaml
Normal file
12
tilt_modules/helm_remote/test/job.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: helm-remote-test-verify
|
||||
spec:
|
||||
backoffLimit: 1
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: helm-remote-test-verify
|
||||
image: helm-remote-test-verify
|
||||
restartPolicy: Never
|
7
tilt_modules/helm_remote/test/test.sh
Executable file
7
tilt_modules/helm_remote/test/test.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
set -ex
|
||||
tilt ci
|
||||
tilt down --delete-namespaces
|
12
tilt_modules/helm_remote/test/verify.exp
Normal file
12
tilt_modules/helm_remote/test/verify.exp
Normal file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/expect
|
||||
|
||||
spawn telnet memcached 11211
|
||||
expect {
|
||||
timeout {exit 1}
|
||||
"Connected"
|
||||
}
|
||||
send "stats\r\n"
|
||||
expect {
|
||||
timeout {exit 1}
|
||||
"STAT pid 1"
|
||||
}
|
70
tilt_modules/secret/README.md
Normal file
70
tilt_modules/secret/README.md
Normal file
@ -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.
|
105
tilt_modules/secret/Tiltfile
Normal file
105
tilt_modules/secret/Tiltfile
Normal file
@ -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))
|
1
tilt_modules/secret/test/.pgpass
Normal file
1
tilt_modules/secret/test/.pgpass
Normal file
@ -0,0 +1 @@
|
||||
hostname:5432:database:username:password
|
7
tilt_modules/secret/test/Tiltfile
Normal file
7
tilt_modules/secret/test/Tiltfile
Normal file
@ -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')
|
33
tilt_modules/secret/test/job.yaml
Normal file
33
tilt_modules/secret/test/job.yaml
Normal file
@ -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
|
9
tilt_modules/secret/test/test.sh
Executable file
9
tilt_modules/secret/test/test.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
export SOME_TOKEN=abc123
|
||||
|
||||
set -ex
|
||||
tilt ci
|
||||
tilt down --delete-namespaces
|
Loading…
x
Reference in New Issue
Block a user