-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add keystone-shell container #185
Open
timothyb89
wants to merge
11
commits into
master
Choose a base branch
from
keystone-shell
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
20d00d7
Add keystone-shell container
timothyb89 d9d7d56
Merge remote-tracking branch 'origin/master' into keystone-shell
timothyb89 4e5b080
Formatting tweaks and readme fixes
timothyb89 f6e029a
Merge branch 'master' into keystone-shell
kornicameister 978b62a
Merge remote-tracking branch 'origin/master' into keystone-shell
timothyb89 d56080f
Use monasca/python:3 and tiny-kubernetes
timothyb89 3125610
Fix shellcheck errors
timothyb89 f932da2
Fix more shellcheck errors
timothyb89 6e42314
Merge remote-tracking branch 'origin/master' into keystone-shell
timothyb89 c86181b
Fix broken conditional in keystonerc.sh
timothyb89 c5a37dc
Merge remote-tracking branch 'origin/master' into keystone-shell
timothyb89 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
FROM monasca/python:3-20171101-203444 | ||
|
||
# To force a rebuild, pass --build-arg REBUILD="$(DATE)" when running | ||
# `docker build` | ||
ARG REBUILD=1 | ||
|
||
COPY requirements.txt / | ||
|
||
RUN apk add --no-cache ca-certificates tini libffi && \ | ||
apk add --no-cache --virtual build-dep \ | ||
musl-dev linux-headers git make g++ openssl-dev libffi-dev && \ | ||
pip install urllib3 ipaddress ipython ptpython python-openstackclient python-monascaclient && \ | ||
pip install -r /requirements.txt && \ | ||
rm -rf /root/.cache/pip && \ | ||
apk del build-dep | ||
|
||
COPY keystone_shell_vars.py ptpython_init.py start.sh / | ||
COPY keystonerc.sh /root/.keystonerc | ||
|
||
ENV ENV="/root/.keystonerc" | ||
ENTRYPOINT ["sh", "-l", "/start.sh"] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
keystone-shell | ||
============== | ||
|
||
This image allows you to quickly interact with a keystone environment. | ||
|
||
Sources: [keystone-shell][1] · [Dockerfile][2] · [monasca-docker][3] | ||
|
||
Tags | ||
---- | ||
|
||
Images in this repository are tagged as follows: | ||
|
||
* `latest`: refers to the latest stable point release, e.g. `1.0.0` | ||
* `1.0.0`, `1.0`, `1`: standard semver tags | ||
|
||
Basic Usage | ||
----------- | ||
|
||
This is mainly intended for use in Kubernetes environments configured using | ||
[keystone-init][4]. To use, run: | ||
|
||
kubectl run keystone-shell -i -t \ | ||
--rm=true \ | ||
--restart Never \ | ||
--image=monasca/keystone-shell:latest | ||
|
||
(pass `-n <NAMESPACE>` as needed if the desired secrets are located elsewhere) | ||
|
||
Once the container starts, press enter once to start the shell. Basic usage | ||
information will be printed to the terminal. | ||
|
||
As [keystone-init][4] includes all necessary environment variables inside its | ||
secrets, no additional environment variables are needed. However, use without | ||
this utility or in standalone Docker environments will require the necessary | ||
`OS_` variables to be set manually. | ||
|
||
There are three commands available: | ||
- `secret <NAME>`: load keystone `OS_` variables from secret in current | ||
namespace `NAME` | ||
- `shell [NAME]`: start a Python shell (using ptipython, with highlighting | ||
and autocomplete) with pre-connected Keystone and Kubernetes clients - | ||
additional usage information will be printed on startup | ||
- if specified, secret with `NAME` will be loaded first (as with | ||
`secret NAME`) and will override existing environment variables | ||
- `openstack ...`: the standard openstack client | ||
- `monasca ...`: the standard Monasca client (additional configuration | ||
needed, see below) | ||
|
||
Direct Shell | ||
------------ | ||
|
||
You can also start a shell directly: | ||
|
||
kubectl run keystone-shell \ | ||
--rm=true \ | ||
--restart Never \ | ||
--image=monasca/keystone-shell:latest \ | ||
--env="OS_USERNAME=admin" \ | ||
--env="OS_PASSWORD=secretadmin" \ | ||
--env="OS_PROJECT_NAME=admin" \ | ||
--env="OS_PROJECT_DOMAIN_NAME=Default" \ | ||
--env="OS_USER_DOMAIN_NAME=Default" \ | ||
--env="OS_AUTH_URL=http://monasca-keystone:35357/" \ | ||
--env="KEYSTONE_SHELL=true" \ | ||
--attach -i -t | ||
|
||
Note the `KEYSTONE_SHELL` variable. `OS_` variables can also be provided in | ||
lieu of a secret name (allowing the container to be run in docker rather than | ||
Kubernetes). | ||
|
||
|
||
Verifying Keystone Connectivity | ||
------------------------------- | ||
|
||
This container can be used as a one-off method to check if a Keystone instance | ||
is available. To use, try: | ||
|
||
kubectl run keystone-shell \ | ||
--rm=true \ | ||
--restart Never \ | ||
--image=monasca/keystone-shell:latest \ | ||
--env="KEYSTONE_SECRET=keystone-example-user" \ | ||
--attach | ||
|
||
Note the absence of the `-i` and `-t` parameters. Note that `KEYSTONE_SECRET` | ||
must be defined in this scenario. | ||
|
||
If Keystone is available and the credentials work, the return code will be | ||
zero. If Keystone is not available or the credentials are invalid, the return | ||
code will be 1. Log information should be printed to help narrow down the | ||
error, or at least as much as Keystone will be willing to divulge about it. | ||
|
||
Interacting with Monasca | ||
------------------------ | ||
|
||
The keystone-shell container includes a monasca client as well, however it may | ||
require a small amount of manual configuration: | ||
|
||
$ kubectl run keystone-shell -i -t \ | ||
--rm=true \ | ||
--restart Never \ | ||
--env="KEYSTONE_SECRET=keystone-example-user" | ||
--image=monasca/keystone-shell:latest | ||
keystone-shell:/# export MONASCA_API_URL=http://monasca-api:8070/ | ||
keystone-shell:/# export OS_IDENTITY_API_VERSION=3 | ||
keystone-shell:/# monasca ... | ||
|
||
|
||
Note that your particular `monasca-api` may have a different service name. | ||
|
||
|
||
Configuration | ||
------------- | ||
|
||
| Variable | Default | Description | | ||
|--------------------------|---------|----------------------------------------| | ||
| `LOG_LEVEL` | `INFO` | Python logging level | | ||
| `KEYSTONE_TIMEOUT` | `10` | Keystone connection timeout in seconds | | ||
| `KEYSTONE_VERIFY` | `true` | If `false`, don't verify SSL | | ||
| `KEYSTONE_CERT` | unset | Path to mounted alternative CA bundle | | ||
| `KEYSTONE_SECRET` | unset | Secret to auto-load on startup | | ||
| `OS_AUTH_URL` | unset | (**required**) | | ||
| `OS_USERNAME` | unset | keystone username | | ||
| `OS_PASSWORD` | unset | keystone password | | ||
| `OS_USER_DOMAIN_NAME` | unset | keystone user domain name | | ||
| `OS_PROJECT_NAME` | unset | keystone project name | | ||
| `OS_PROJECT_DOMAIN_NAME` | unset | keystone project domain name | | ||
|
||
[1]: https://github.com/monasca/monasca-docker/blob/master/keystone-shell/ | ||
[2]: https://github.com/monasca/monasca-docker/blob/master/keystone-shell/Dockerfile | ||
[3]: https://github.com/monasca/monasca-docker/ | ||
[4]: https://github.com/monasca/monasca-docker/blob/master/keystone-init/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
repository: monasca/keystone-shell | ||
variants: | ||
- tag: latest | ||
aliases: | ||
- :1.0.0 | ||
- :1.0 | ||
- :1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
#!/usr/bin/env python3 | ||
|
||
# (C) Copyright 2017 Hewlett Packard Enterprise Development LP | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
# not use this file except in compliance with the License. You may obtain | ||
# a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
# License for the specific language governing permissions and limitations | ||
# under the License. | ||
|
||
import base64 | ||
import logging | ||
import os | ||
import sys | ||
|
||
from typing import Union | ||
|
||
from keystoneauth1.identity import Password | ||
from keystoneauth1.session import Session | ||
from keystoneclient.discover import Discover | ||
from requests import HTTPError | ||
|
||
from tiny_kubernetes.kubernetes import KubernetesAPIClient, KubernetesAPIResponse | ||
|
||
NAMESPACE_FILE = '/var/run/secrets/kubernetes.io/serviceaccount/namespace' | ||
|
||
LOG_LEVEL = logging.getLevelName(os.environ.get('LOG_LEVEL', 'INFO')) | ||
logging.basicConfig(level=LOG_LEVEL, | ||
handlers=(logging.StreamHandler(sys.stderr),)) | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
KEYSTONE_PASSWORD_ARGS = [ | ||
'auth_url', 'username', 'password', 'user_id', 'user_domain_id', | ||
'user_domain_name', 'project_id', 'project_name', 'project_domain_id', | ||
'project_domain_name', 'tenant_id', 'tenant_name', 'domain_id', | ||
'domain_name', 'trust_id', 'default_domain_id', 'default_domain_name' | ||
] | ||
KEYSTONE_TIMEOUT = int(os.environ.get('KEYSTONE_TIMEOUT', '10')) | ||
KEYSTONE_VERIFY = os.environ.get('KEYSTONE_VERIFY', 'true') == 'true' | ||
KEYSTONE_CERT = os.environ.get('KEYSTONE_CERT', None) | ||
|
||
_kubernetes_client = None | ||
|
||
|
||
def get_current_namespace() -> str: | ||
if 'NAMESPACE' in os.environ: | ||
return os.environ['NAMESPACE'] | ||
|
||
if os.path.exists(NAMESPACE_FILE): | ||
with open(NAMESPACE_FILE, 'r') as f: | ||
return f.read() | ||
|
||
logger.warning('Not running in cluster and $NAMESPACE is not set, ' | ||
'assuming \'default\'!') | ||
return 'default' | ||
|
||
|
||
def get_kubernetes_client() -> KubernetesAPIClient: | ||
global _kubernetes_client | ||
|
||
if _kubernetes_client is None: | ||
_kubernetes_client = KubernetesAPIClient() | ||
_kubernetes_client.load_auto_config() | ||
|
||
return _kubernetes_client | ||
|
||
|
||
def keystone_env_vars(): | ||
ret = {} | ||
for arg in KEYSTONE_PASSWORD_ARGS: | ||
name = 'OS_{}'.format(arg.upper()) | ||
if name in os.environ: | ||
ret[name] = os.environ[name] | ||
|
||
return ret | ||
|
||
|
||
def keystone_args_from_env(): | ||
ret = {} | ||
for arg in KEYSTONE_PASSWORD_ARGS: | ||
ret[arg] = os.environ.get('OS_{}'.format(arg.upper())) | ||
|
||
return ret | ||
|
||
|
||
def get_keystone_client(): | ||
auth = Password(**keystone_args_from_env()) | ||
session = Session(auth=auth, | ||
app_name='keystone-shell', | ||
user_agent='keystone-shell', | ||
timeout=KEYSTONE_TIMEOUT, | ||
verify=KEYSTONE_VERIFY, | ||
cert=KEYSTONE_CERT) | ||
|
||
discover = Discover(session=session) | ||
return discover.create_client() | ||
|
||
|
||
def get_kubernetes_secret(name: str, | ||
namespace: str=None) -> Union[KubernetesAPIResponse, | ||
None]: | ||
""" | ||
:return: loaded secret dict or None if it does not exist | ||
""" | ||
client = get_kubernetes_client() | ||
|
||
if namespace is None: | ||
namespace = get_current_namespace() | ||
|
||
try: | ||
return client.get('/api/v1/namespaces/{}/secrets/{}', namespace, name) | ||
except HTTPError as e: | ||
if e.response.status_code != 404: | ||
raise | ||
|
||
return None | ||
|
||
|
||
def main(): | ||
if len(sys.argv) == 1: | ||
logger.info('no secret name arg provided, will use default environment') | ||
return | ||
|
||
secret_name = sys.argv[1] | ||
|
||
if len(sys.argv) > 2: | ||
secret_namespace = sys.argv[2] | ||
else: | ||
secret_namespace = None | ||
|
||
# purge existing keystone vars from the environment | ||
for var in keystone_env_vars().keys(): | ||
print('unset {};'.format(var)) | ||
|
||
secret = get_kubernetes_secret(secret_name, secret_namespace) | ||
for key, val in secret.data.items(): | ||
val_bytes = base64.b64decode(val) | ||
print('export {}="{}"'.format(key, val_bytes.decode('utf-8'))) | ||
|
||
logger.info('now using account from secret %s', secret_name) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#!/bin/sh | ||
|
||
secret() { | ||
if [ "$#" -ne 1 ]; then | ||
echo "A secret name is required." | ||
echo "Usage: secret <name>" | ||
return 1 | ||
fi | ||
|
||
eval "$(python /keystone_shell_vars.py "$1")" | ||
} | ||
|
||
shell() { | ||
if [ "$#" -eq 1 ]; then | ||
eval "$(python /keystone_shell_vars.py "$1")" | ||
fi | ||
|
||
LD_PRELOAD=/stack-fix.so ptipython -i /ptpython_init.py | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import keystone_shell_vars | ||
|
||
keystone = keystone_shell_vars.get_keystone_client() | ||
ks = keystone | ||
|
||
kubernetes = keystone_shell_vars.get_kubernetes_client() | ||
k8s = kubernetes | ||
|
||
print('--------------------------------------') | ||
print('Pre-defined variables:') | ||
print(' - keystone (ks): keystone client') | ||
print(' - kubernetes (k8s): kubernetes client') | ||
print('--------------------------------------') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
dpath | ||
dotmap | ||
pbr>=1.8 | ||
keystoneauth1 | ||
python-keystoneclient>=3.8.0 # Apache-2.0 | ||
requests | ||
-e git+https://github.com/monasca/tiny-kubernetes.git#egg=tiny-kubernetes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// workaround for musl's small stack size causing python segfaults | ||
// https://github.com/esnme/ultrajson/issues/254#issuecomment-314862445 | ||
|
||
#include <dlfcn.h> | ||
|
||
#include <pthread.h> | ||
|
||
typedef int (*func_t)(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg); | ||
|
||
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg) { | ||
pthread_attr_t local; | ||
int used = 0, ret; | ||
|
||
if (!attr) { | ||
used = 1; | ||
pthread_attr_init(&local); | ||
attr = &local; | ||
} | ||
pthread_attr_setstacksize((void*)attr, 2 * 1024 * 1024); // 2 MB | ||
|
||
func_t orig = (func_t)dlsym(RTLD_NEXT, "pthread_create"); | ||
|
||
ret = orig(thread, attr, start_routine, arg); | ||
|
||
if (used) { | ||
pthread_attr_destroy(&local); | ||
} | ||
|
||
return ret; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this return should have one less indentation level. I know you run it from
except
but this way you will make it default.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that it matters a ton since the code paths are effectively equivalent but my feeling was that (without the conditional to check for a 404) is a bit more clear about the intent.
Ideally it would've been:
... which I think makes the intent more clear - that is,
None
isn't the "default" branch so much as the "not found" branch. Since requests doesn't have exception subclasses for different error codes, it gets a bit uglier when we compare the error code ... I'm not sure how much more readable the end result is, though.