Skip to content

Commit

Permalink
Implement K8S discovery
Browse files Browse the repository at this point in the history
  • Loading branch information
ezh committed Feb 22, 2020
1 parent f59792c commit 825bc6a
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 0 deletions.
99 changes: 99 additions & 0 deletions cloudselect/discovery/kubernetes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright 2020 Alexey Aksenov and individual contributors
# See the LICENSE.txt file at the top-level directory of this distribution.
#
# Licensed under the MIT license
# <LICENSE-MIT or http://opensource.org/licenses/MIT>
# This file may not be copied, modified, or distributed
# except according to those terms.
"""Module collecting instances from Kubernetes namespaces."""
import copy
import logging
import re

from kubernetes import client
from kubernetes import config as config

from cloudselect import Container, Pod

from . import DiscoveryService


class Kubernetes(DiscoveryService):
"""Class implementing discovery service plugin."""

log = None

def __init__(self):
"""Class constructor."""
self.log = logging.getLogger("cloudselect.discovery.kubernetes")

def run(self):
"""Collect Kubernetes pods."""
self.log.debug("Discover Kubernetes pods")
return list(self.instances())

def instances(self):
"""Collect Kubernetes pods."""
for i in self.find():
instance_id = i["metadata"]["uid"]
metadata = self.simplify_metadata(i)
configuration = i["cluster"]["configuration"]
context = i["cluster"]["context"]
ip = i["status"]["pod_ip"]
name = i["metadata"]["name"]
namespace = i["metadata"]["namespace"]
node_ip = i["status"]["host_ip"]

representation = [instance_id, name]
self.enrich_representation(representation, metadata)

instance = Pod(
instance_id,
name,
None,
metadata,
representation,
configuration,
context,
namespace,
ip,
node_ip,
)
yield instance

def find(self):
"""Discover pods in Kubernetes clouds."""
cluster = Container.config.discovery.cluster()
for cluster_id in cluster:
patterns = [re.compile(i) for i in cluster[cluster_id].get("namespace", [])]
config_file = cluster[cluster_id].get("configuration")
context = cluster[cluster_id].get("context")
api_client = config.new_client_from_config(
config_file=config_file, context=context,
)
version_api = client.VersionApi(api_client=api_client)
version = version_api.get_code()
self.log.debug(
"Connected to '%s', control plane version %s",
cluster_id,
version.git_version,
)
core_v1 = client.CoreV1Api(api_client=api_client)
ret = core_v1.list_pod_for_all_namespaces(watch=False)
for pod in ret.items:
instance = copy.deepcopy(pod.to_dict())
if instance["status"]["phase"] == "Running":
instance["cluster"] = {
"configuration": config_file,
"context": context,
}
if patterns:
namespace = instance["metadata"]["namespace"]
matched = next(
(m for m in patterns if m.match(namespace) is not None),
None,
)
if matched is not None:
yield instance
else:
yield instance
74 changes: 74 additions & 0 deletions example/kexectmux
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/bin/sh
#
# Split arguments by --:
# cloudselect arguments -- exec arguments

CLOUDSELECT_ARGS=$#
if [ $CLOUDSELECT_ARGS -eq 0 ]; then
python -m cloudselect
exit
fi
for (( i=1; i <= "$#"; i++ )); do
if [ "${!i}" == "--" ]; then
CLOUDSELECT_ARGS=$(($i-1))
break
fi
done
KEXEC_ARGS=$(($CLOUDSELECT_ARGS+2))

OUTPUT=$(python -m cloudselect "${@:1:$CLOUDSELECT_ARGS}")
if [ $? -ne 0 ];then
exit
fi

CLOUD=$(echo $OUTPUT | jq -r '.instances[] | @base64')
TMUX_LAYOUT=${TMUX_LAYOUT:-tiled}
TMUX_SESSION_NAME="assh-"$(date +%s)
WAIT_FOR_INPUT='read -n 1 -s -r -p "Press any key to continue"'
WINDOW=0

OPTION_AFTER=$(echo $OUTPUT | jq -r '.option.after // ""')
OPTION_BEFORE=$(echo $OUTPUT | jq -r '.option.before // ""')
OPTION_SHELL=$(echo $OUTPUT | jq -r '.option.shell // "bash"')

# Collect shared arguments
for row in $(echo $CLOUD); do
OUTPUT=$(echo ${row} | base64 --decode)
break
done

test "$OPTION_BEFORE" != "null" && eval "$OPTION_BEFORE"
# Iterate over instances
for row in $(echo $CLOUD); do
OUTPUT=$(echo ${row} | base64 --decode)
HOST=$(echo $OUTPUT | jq -r '.host')
CONFIGURATION="--kubeconfig="$(echo $OUTPUT | jq -r '.configuration // ""')
CONTEXT="--context="$(echo $OUTPUT | jq -r '.context // ""')
NAMESPACE=$(echo $OUTPUT | jq -r '.namespace // ""')

test "$CONFIGURATION" == "--kubeconfig=" && CONFIGURATION=""
test "$CONTEXT" == "--context=" && CONTEXT=""

connectString="kubectl $CONFIGURATION $CONTEXT -n $NAMESPACE exec $HOST -i -t $OPTION_SHELL"
commandString="${connectString}; $WAIT_FOR_INPUT"

if [ $# -ge $KEXEC_ARGS ];then
echo "kubectl $CONFIGURATION $CONTEXT -n $NAMESPACE exec $HOST -- ${@:$KEXEC_ARGS}"
kubectl $CONFIGURATION $CONTEXT -n $NAMESPACE exec $HOST -- ${@:$KEXEC_ARGS}
continue
fi

tmux list-windows | grep -q "${TMUX_SESSION_NAME}"
if [ $WINDOW -eq 0 -a $? -ne 0 ]; then
tmux new-window -n "${TMUX_SESSION_NAME}" "$commandString"
echo "$connectString"
else
tmux split-window -t "${TMUX_SESSION_NAME}" "${commandString}" && \
echo "$connectString"
fi
WINDOW=$((WINDOW+1))
done

test $WINDOW -gt 0 && tmux set-window-option -t "${TMUX_SESSION_NAME}" synchronize-panes on
test $WINDOW -gt 0 && tmux select-layout -t "${TMUX_SESSION_NAME}" "${TMUX_LAYOUT}"
test "$OPTION_AFTER" != "null" && eval "$OPTION_AFTER"

0 comments on commit 825bc6a

Please sign in to comment.