koris.deploy package

Submodules

koris.deploy.k8s module

deploy cluster service to kubernetes via the API server

class koris.deploy.k8s.K8S(config, manifest_path=None)[source]

Bases: koris.deploy.k8s.K8SConfigurator, koris.deploy.k8s.K8SScaler

Class allowing various interactions with a Kubernets cluster.

nginx_ingress_ports

get the ingress-nginx service ports as dictionary

class koris.deploy.k8s.K8SConfigurator[source]

Bases: object

apply plugins and post install setup

add_all_masters_to_loadbalancer(cluster_name, n_masters, lb_inst)[source]

Adds all master nodes to the LoadBalancer listener.

If the number of members in the master listener pool of the LoadBalancer is less than expected number of masters this function will add them to the pool as soon as they have node status “Ready”.

Parameters:
  • cluster_name (string) – the name of the cluster
  • n_master (int) – Number of desired master nodes.
  • lb_inst (cloud.openstack.LoadBalancer) – A configured LoadBalancer instance.
apply_addons(koris_config, apply_func=<function create_from_yaml>)[source]

apply all addons to the cluster

Parameters:koris_config (dict) – koris configuration loaded as dict
apply_plugins(plugins)[source]

apply all plugins in the list

ca_cert

Returns the API servers CA.

Returns:The CA encoded as base64.
ca_info

Return a dict with the read ca and the discovery hash

discovery_hash

Calculate and return a discovery_hash.

Based on the cluster CA.

Returns:A discovery hash encoded in Hex.
etcd_cluster_status()[source]

Checks the current etcd cluster state.

This function calls etcdctl inside a pod in order to obtain the current state of the etcd cluster before a new member can be added to it.

Right now, etcdctl offers no convenient way to format the output so the URLs from the masters can be extracted, which is why jq is used here.

Parameters:
  • podname (str) – The name of the pod where the etcdctl command should be sent from. Needs to be inside the kube-system namespace.
  • master_ip (str) –
Returns:

The status of the etcd as a string (e.g.master-1=192.168.1.102,master-2=192.168.1.103)

get_bootstrap_token()[source]

Generate a Bootstrap token

Returns:A string of the form <token id>.<token secret>.
get_random_master()[source]

Returns a name and IP of a random master server in the cluster.

Returns:Tuple of name and IP of a master.
host

Retrieve the host or loadbalancer info

is_ready

Check if the API server is already available.

Returns:True if it’s reachable.
nginx_ingress_ports

get the ingress-nginx service ports as dictionary

validate_context(conn)[source]

Validate that server that we are talking to via K8S API is also the cloud context we are using.

This retrieves the project ID of the Kubernetes LoadBalancer, then checks if it finds the same ID in any LoadBalancer of the currently sourced OpenStack project.

In case the IP is not a Floating IP but only a Virtual IP, both IPs are simply compared.

Parameters:conn (obj) – OpenStack connection object.
Returns:bool
class koris.deploy.k8s.K8SScaler[source]

Bases: object

A Mixin to modify the cluster size

add_master()[source]

add a master to the cluster

add_node()[source]

add a node to the cluster

delete_node(nodename, grace_period=0, ignore_not_found=True)[source]

Delete a node in Kubernetes.

Parameters:
  • nodename (str) – The name of the node to delete.
  • grace_period (int) – Duration in seconds before the node should be delete. Defaults to 0, which means immediately.
  • ignore_not_found (bool) – If set to False, will raise a ValueError if node doesn’t exist.
Raises:

:class:`kubernetes.client.rest.ApiException` in case the API call – fails.

drain_node(nodename, ignore_not_found=True)[source]

Drains a node of pods.

We’re using kubectl drain instead of the eviction API, since it’s quicker and we don’t have to get all the Pods of the Node first.

Will check if the node exists first.

Parameters:
  • nodename (str) – Name of the node to drain
  • ignore_not_found (bool) – If set to False, will raise a ValueError if the node doesn’t exist.
Raises:

RuntimeError if kubectl drain fails.

etcd_members(podname, master_ip)[source]

Retrieves a dictionary with information about the etcd cluster.

This function uses etcdctl member list to retrieve information about the etcd cluster, then parses that response into a dictionary where the keys are the names of the members and the corresponding values hold the rest of the information such as ID, clientURLs and peerURLs.

Returns:A dictionary with information about the etcd cluster.
Raises:ValueError if master_ip is not valid.
node_status(nodename)[source]

Returns the status of a Node.

Parameters:nodename (str) – The name of the node to check.
Returns:
The status of the node as string or None if an error was
encountered.
remove_from_etcd(name, ignore_not_found=True)[source]

Removes a member from etcd.

The ‘master-adder’ operator will be used to perform the queries against etcd. The pod will be created if not found.

Parameters:
  • name (str) – The name of the member to remove.
  • ignore_not_found (bool) – If set to False, will raise a ValueError if member is not part of etcd cluster.
class koris.deploy.k8s.KorisAddon(name, manifest_path='/home/docs/checkouts/readthedocs.org/user_builds/koris/envs/latest/lib/python3.7/site-packages/koris/deploy/manifests')[source]

Bases: object

Naive Addon class. Applies a kubernetes collection of resources from yml.

Parameters:
  • name (str) – the name of the plugin
  • manifest_path (str) – the path where kubernetes resources are saved.
apply(k8s_client, apply_func=<function create_from_yaml>)[source]

Apply a plugin to the cluster. Currently we use the Python client to apply a plugin. This might be limited, so we keep the possibilty to use a kubectl shell wrapper by making this an optional argument.

Parameters:
  • k8s_client – A Kubernet API client
  • apply_func – A callable that can apply a plugin to the cluster
koris.deploy.k8s.add_ingress_listeners(nginx_ingress_ports, lbinst, lb_masters)[source]

Reconfigure the Openstack LoadBalancer - add an HTTP and HTTPS listener for nginx ingress controller

Parameters:
koris.deploy.k8s.get_addons(config)[source]

A prototype for loading addons. There are optional addons, and non-optional addons. Currently, non-optional addons include only the metrics-server.

Parameters:config (dict) – parse yaml with an optional section, list of addons
koris.deploy.k8s.get_token_description()[source]

create a description for the token

koris.deploy.k8s.parse_etcd_response(resp)[source]

Takes a response from etcdctl and parses it for its member info.

The response is to be expected in JSON format as obtained by etcdctl member list -w json. Right now, the IDs in the JSON response are in uint64 format and will be transformed into hex with this function.

Parameters:resp (str) – A JSON response from etcdctl.
Returns:A dict containing member information.
Raises:ValueError if state could not be extracted.
koris.deploy.k8s.rand_string(num)[source]

generate a random string of len num

koris.deploy.dex module

The dex module manages a dex (https://github.com/dexidp/dex) installation.

The workflow is the following:

  1. Create certificates for Dex (via DexSSL)

2. In koris.provision.cloud_init.FirstMasterInit deploy the previously created CA to the first Master that is created. Additionally, create the extra arguments for the apiserver as environment variables in the koris.env file.

3. In koris/provision/userdata/bootstraph-k8s-master-ubuntu-16.04.sh, take the variables from koris.env and add their values as extra arguments to kubeadm’s init.tmpl.

4. Take the main LoadBalancer and add two new Listeners to it: one for the Dex service (via create_dex()) and one for an OAuth2 client app (via create_oauth2()) that will be requesting tokens from Dex.

Example

>>> dex_ssl = DexSSL("certs/", "dex.example.org")
>>> dex_ssl.save_certs()
>>> dex_conf = create_dex_conf(config['addons']['dex'], dex_ssl)
>>> master_tasks = master_builder.create_masters_tasks(..., dex=dex_conf)
>>> # Execute master_tasks
>>> dex_listener = dex_conf['ports']['listener']
>>> dex_service = dex_conf['ports']['service']
>>> dex_members = master_ips
>>> dex_task = loop.create_task(create_dex(NEUTRON, lbinst,
...                                        listener_port=dex_listener,
...                                        pool_port=dex_service,
...                                        members=dex_members))
>>> client_listener = dex_conf['client']['ports']['listener']
>>> client_service = dex_conf['client']['ports']['service']
>>> client_members = node_ips
>>> oauth_task = loop.create_task(create_oauth2(NEUTRON, lbinst,
...                                             listener_port=client_listener,
...                                             pool_port=client_service,
...                                             members=client_members))
>>> tasks = [dex_task, oauth_task]
>>> loop.run_until_complete(asyncio.gather(*tasks))
class koris.deploy.dex.DexSSL(cert_dir: str, issuer: str, k8s_ca_path='/etc/ssl/certs/oidc-ca.pem')[source]

Bases: object

Class managing the dex TLS infrastrucutre.

Dex uses a self-signed CA to sign tokens it receives from a OIDC Provider such as Gitlab, GitHub or LDAP. A 3rd Party (such as the Kubernetes apiserver) will then verify the signed token with Dex’s public key. A client certificate, that is signed by the Dex CA, is used to request a token from Dex.

On instantiation, will create the Dex CA and client cert bundles.

Example

>>> dex_ssl = DexSSL("./certs", "dex.example.com")
>>> dex_ssl.save_certs()
Parameters:
  • cert_dir (str) – The directory where the keys and certificates will be saved to.
  • issuer (str) – The issuer of the Dex CA. This needs to be an IP or DNS where Dex is reachable from a host and from inside the kube-apiserver.
  • k8s_ca_path (str) – This is the location on the Master node(s) where the Dex CA will saved under. Needs to be a full path including file name. This will then be passed as an argument to the kube-apiserver so it can use the certificate’s public key to verify an incoming token.
ca_bundle

An SSL Certificate Bundle containing the Dex CA certificate and key pair.

Type:koris.ssl.CertBundle
client_bundle

An SSL Certificate Bundle containing the client certificate and key pair.

Type:koris.ssl.CertBundle
create_certs()[source]

Create a CA and client cert for Dex.

Will first create a CA bundle, then use this to sign a client certificate. The Client cert will have the following Key Usage parameters: Digital Signature, Content Commitment, Key Encipherment.

Will also set the attributes ca_bundle and client_bundle.

Returns:Tuple consisting of root CA bundle and cert bundle
save_certs(client_prefix='dex-client', ca_prefix='dex-ca')[source]

Saves certificate bundles to disc.

This function uses koris.ssl.CertBundle.save() to save the certificate bundles to disc.

Parameters:
  • client_prefix (str) – The prefix for the client certificate.
  • ca_prefix (str) – The prefix for the Dex CA.
class koris.deploy.dex.Listener(lb: koris.cloud.openstack.LoadBalancer, name, port, pool: koris.deploy.dex.Pool, protocol='HTTPS')[source]

Bases: object

A Listener containing a LoadBalancer and Pool.

In future, this should be of the OpenStack scope instead of Dex’s.

When a Listener class gets instantiated, its parameters will be checked for validity. The same is true when functions get executed. Additionally, an instantiated class does not mean the pool is created with OpenStack. Use the create function for that.

A Pool (see above) is attached to a Listener. This Listener listens on a specific port and forwards traffic to the Pool, on a specific port. A Listener is attached to a LoadBalancer which will forward traffic to a specific Listener, depending on the port.

Example

>>> # Create a Pool with Members
>>> members = ["10.0.0.1", 10.0.0.2"]
>>> pool = Pool("test-pool", "HTTPS", 32443, "ROUND_ROBIN", members)
>>> listener = Listener(LB, "test-listener", 443, pool)
>>> # This will create the Pool, add it to the Listener and add it all to a LB
>>> listener.all(NEUTRON)
Parameters:
  • lb (LoadBalancer) – An OpenStack LoadBalancer Object.
  • name (str) – The name of the Listener.
  • port (int) – The port which will be listened on.
  • protocol (str) – The protocol that should be used for listening. Must be part of allowed_protocols.
  • pool (Pool) – A Pool object.
allowed_algirthms

A list of strings for the LoadBalancer algorithms that can be used.

Type:list
allowed_protocols

A list of string for the LoadBalancer protocols that can be used.

Type:list
loadbalancer

An OpenStack LoadBalancer Object

Type:LoadBalancer
id

The Listener ID. Will be assigned after create gets called.

Type:int
listener

A LoadBalancer dictionary as received from the OpenStack API. Will be assigned after create gets called.

Type:dict
all()[source]

Convenience function to create a Listener, then Pool.

Will call create and create_pool.

create()[source]

Creates a new Listener and adds it to the LoadBalancer.

This function will assing the attributes listener and id.

create_pool()[source]

Creates a new Pool with members and healthmon and adds it to the Listener.

Requires create to be called first.

In case the Pool.create() or Pool.all() function hasn’t been called, call Pool.all().

verify()[source]

Verifies if the Listener is configured correctly

class koris.deploy.dex.Pool(name, protocol, port, algorithm, members)[source]

Bases: object

A Pool with Members, Algorithm and Port

In future, this should be of the OpenStack scope instead of Dex’s.

When a Pool class gets instantiated, its parameters will be checked for validity. The same is true when functions get executed. Additionally, an instantiated class does not mean the pool is created with OpenStack. Use the create function for that.

Members are loadbalanced IP Adresses or DNS Names that belong to a Pool. This Pool is then attached to a Listener.

Example

>>> # Create a Pool with Members
>>> members = ["10.0.0.1", 10.0.0.2"]
>>> pool = Pool("test-pool", "HTTPS", 32443, "ROUND_ROBIN", members)
>>> # Assuming we have a created Listener
>>> pool.all(NEUTRON, LB, listener.id)
Parameters:
  • name (str) – The name of the Pool.
  • protocol (str) – The protocol for the Pool. Must be part of allowed_protocols.
  • port (int) – The port for the members.
  • algorithm (str) – The loadbalancing algorithm. Must be part of allowed_algorithms.
  • members (list) – A list of members that the Pool contains.
allowed_algirthms

A list of strings for the LoadBalancer algorithms that can be used.

Type:list
allowed_protocols

A list of string for the LoadBalancer protocols that can be used.

Type:list
id

The pool ID. Will be assigned after create gets called.

Type:int
pool

A LoadBalancer dictionary as received from the OpenStack API. Will be assigned after create gets called.

Type:dict
add_health_monitor(lb: koris.cloud.openstack.LoadBalancer)[source]

Adds a Health monitor to a Pool with default settings

Parameters:lb (LoadBalancer) – An OSLoadBalancer instance.
add_members(lb: koris.cloud.openstack.LoadBalancer)[source]

Adds Members to a Pool.

Parameters:lb (LoadBalancer) – An OSLoadBalancer instance.
all(lb: koris.cloud.openstack.LoadBalancer, listener_id)[source]

Convenience function to create a Pool with Members and Health Monitor.

Will call create, add_members and add_health_monitor.

Parameters:
  • lb (LoadBalancer) – An OSLoadBalancer instance.
  • listener_id (str) – The Listener ID this pool should be added to.
create(lb: koris.cloud.openstack.LoadBalancer, listener_id)[source]

Creates a Pool and adds it to a Listener.

This function will set the attributes pool and id.

Parameters:
  • lb (LoadBalancer) – An OSLoadBalancer instance.
  • listener_id (str) – The Listener ID this pool should be added to.
verify()[source]

Checks if a Pool is configured correctly

exception koris.deploy.dex.ValidationError[source]

Bases: Exception

A custom error if dex is configured inproperly

koris.deploy.dex.create_dex(lb: koris.cloud.openstack.LoadBalancer, name='dex', listener_port=32000, pool_port=32000, protocol='HTTPS', algo='ROUND_ROBIN', members=None)[source]

Convenience function to create a Dex Listener and Pool.

This will take an existing LoadBalancer in OpenStack and adds a new Listener with Pool and members to it, so Dex can be reached inside the cluster.

Will first create a Pool, then a Listener from that pool.

Parameters:
  • lb (LoadBalancer) – The used LoadBalancer.
  • name (str) – The name of the Dex Listener and Pool.
  • listener_port (int) – The port the Listener should listen on.
  • pool_port (int) – The exposed port of the Dex service inside the cluster.
  • protcol (str) – The protocol to use. Should be HTTPS.
  • algo (str) – The loadbalancing algorithm to use.
  • members (list) – A list of members to add to the pool. Should be the Masters.
koris.deploy.dex.create_dex_conf(config, dex_ssl: koris.deploy.dex.DexSSL)[source]

Parse the koris config for dex parameters.

The user needs to validate first if dex is wished to be installed. The following config arguments are optional username_claim (default: email), groups_claim (default: group)

Parameters:
  • config (dict) – The config[‘addons’][‘dex’] part of the config dict.
  • dex_ssl (DexSSL) – a DexSSL instance.
Raises:

ValidationError if mandatory parts are missing or incorrect information – has been provided.

Returns:

A dictionary with the correct config parameters set.

koris.deploy.dex.create_oauth2(lb: koris.cloud.openstack.LoadBalancer, name='oauth2', listener_port=5556, pool_port=32555, protocol='HTTP', algo='ROUND_ROBIN', members=None)[source]

Convenience function to create an OAuth2 Client App Listener and Pool.

Users need to deploy an OAuth2 Client App that talks with Dex to retrieve a token. This function takes an existing LoadBalancer in OpenStack and adds a new Listener with Pool and Members to it. This way, clients and Dex can reach the OAuth2 Client App.

Parameters:
  • lb (LoadBalancer) – The used LoadBalancer.
  • name (str) – The name of the OAuth2 Listener and Pool.
  • listener_port (int) – The port the Listener should listen on.
  • pool_port (int) – The exposed port of the OAuth2 service inside the cluster.
  • protcol (str) – The protocol to use. Should be HTTPS.
  • algo (str) – The loadbalancing algorithm to use.
  • members (list) – A list of members to add to the pool. Should be the Nodes.
koris.deploy.dex.is_ip(ip)[source]

Checks if an IP is a valid IPv4 or IPv6 address.

Parameters:ip (str) – The IP to be checked.
Returns:True, if it’s a valid IPv4 or IPv6 address
koris.deploy.dex.is_port(port)[source]

Checks if a port is valid.

A port needs to be integer and between 0 and 65535.

Parameters:port (int) – The port to bee checked
Returns:True, if port is valid

Module contents