Skip to content

Commit

Permalink
Add support for deploying k3s
Browse files Browse the repository at this point in the history
This adds `sesdev create k3s`, which by default will deploy a
master with four workers on openSUSE Tumbleweed.  It will also
deploy on SLE 15 SP3 if you specify `--os=sles-15-sp3`.

k3s is installed by running `curl https://get.k3s.io`.  Helm
is also installed on the master, again using `curl`.  If you
don't want the latest stable k3s, you can install a specific
version with the `--k3s-version` option.  If you want more or
less workers, use `--roles='[master],[worker],[...]`.

Signed-off-by: Tim Serong <tserong@suse.com>
  • Loading branch information
tserong committed Mar 3, 2023
1 parent fd134f4 commit 1d858c9
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 10 deletions.
26 changes: 26 additions & 0 deletions sesdev/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ def _gen_settings_dict(
username=None,
msgr2_secure_mode=None,
msgr2_prefer_secure=None,
k3s_version=None,
):

settings_dict = {}
Expand All @@ -456,6 +457,8 @@ def _gen_settings_dict(
roles_string = Constant.ROLES_SINGLE_NODE['nautilus']
elif version == 'caasp4':
roles_string = Constant.ROLES_SINGLE_NODE['caasp4']
elif version == 'k3s':
roles_string = Constant.ROLES_SINGLE_NODE['k3s']
else:
raise VersionNotKnown(version)
settings_dict['roles'] = _parse_roles(roles_string)
Expand Down Expand Up @@ -696,6 +699,9 @@ def _gen_settings_dict(
raise OptionNotSupportedInVersion('--rgw-ssl', version)
settings_dict['rgw_ssl'] = rgw_ssl

if k3s_version is not None:
settings_dict['k3s_version'] = k3s_version

return settings_dict


Expand Down Expand Up @@ -768,6 +774,10 @@ def _create_command(deployment_id, settings_dict):
click.echo()
click.echo(" $ sesdev tunnel {} suma".format(deployment_id))
click.echo()
elif dep.settings.version == 'k3s':
# Nothing extra to print
# TODO: really?
pass
else:
click.echo("Or, access the Ceph Dashboard with:")
click.echo()
Expand Down Expand Up @@ -913,6 +923,22 @@ def caasp4(deployment_id, **kwargs):
_create_command(deployment_id, settings_dict)


@create.command()
@click.argument('deployment_id', required=False)
@common_create_options
@libvirt_options
@click.option("--k3s-version", default=None,
help='k3s version to install (defaults to latest stable)')
def k3s(deployment_id, **kwargs):
"""
Creates a k3s cluster using Tumbleweed
"""
_prep_kwargs(kwargs)
settings_dict = _gen_settings_dict('k3s', **kwargs)
deployment_id = _maybe_gen_dep_id('k3s', deployment_id, settings_dict)
_create_command(deployment_id, settings_dict)


@create.command()
@click.argument('deployment_id', required=False)
@common_create_options
Expand Down
9 changes: 9 additions & 0 deletions seslib/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ class Constant():

ROLES_DEFAULT = {
"caasp4": [["master"], ["worker"], ["worker"], ["loadbalancer"]],
"k3s": [["master"], ["worker"], ["worker"], ["worker"], ["worker"]],
"makecheck": [["makecheck"]],
"nautilus": [["master", "client", "prometheus", "grafana"],
["storage", "mon", "mgr", "rgw", "igw"],
Expand All @@ -381,6 +382,7 @@ class Constant():

ROLES_DEFAULT_BY_VERSION = {
'caasp4': ROLES_DEFAULT["caasp4"],
'k3s': ROLES_DEFAULT["k3s"],
'makecheck': ROLES_DEFAULT["makecheck"],
'nautilus': ROLES_DEFAULT["nautilus"],
'octopus': ROLES_DEFAULT["octopus"],
Expand Down Expand Up @@ -415,6 +417,7 @@ class Constant():

ROLES_SINGLE_NODE = {
"caasp4": "[ master ]",
"k3s": "[ master ]",
"nautilus": "[ master, storage, mon, mgr, prometheus, grafana, mds, igw, rgw, "
"nfs ]",
"octopus": "[ master, admin, bootstrap, storage, mon, mgr, mds, igw, rgw, "
Expand Down Expand Up @@ -500,6 +503,10 @@ class Constant():
'http://download.nue.suse.com/ibs/SUSE/Updates/SUSE-CAASP/4.5/x86_64/update/'
],
},
'k3s': {
'sles-15-sp3': [],
'tumbleweed': [],
},
'makecheck': {
'sles-15-sp1': [
'http://download.nue.suse.com/ibs/Devel:/Storage:/6.0/images/repo/'
Expand Down Expand Up @@ -535,6 +542,7 @@ class Constant():
'octopus': 'cephadm',
'pacific': 'cephadm',
'caasp4': None,
'k3s': None,
'makecheck': None,
}

Expand All @@ -546,6 +554,7 @@ class Constant():
'octopus': 'leap-15.2',
'pacific': 'leap-15.3',
'caasp4': 'sles-15-sp2',
'k3s': 'tumbleweed',
'makecheck': 'tumbleweed',
}

Expand Down
14 changes: 7 additions & 7 deletions seslib/deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def __populate_os(self):

def __populate_deployment_tool(self):
if not self.settings.deployment_tool \
and self.settings.version not in ['caasp4', 'makecheck']:
and self.settings.version not in ['caasp4', 'k3s', 'makecheck']:
self.settings.deployment_tool = \
Constant.VERSION_PREFERRED_DEPLOYMENT_TOOL[self.settings.version]

Expand Down Expand Up @@ -414,7 +414,7 @@ def __generate_nodes(self):
if 'master' in node_roles:
self.master = node

if self.settings.version == 'caasp4':
if self.settings.version in ['caasp4', 'k3s']:
single_node = self.settings.single_node or len(self.settings.roles) == 1
if 'master' in node_roles or 'worker' in node_roles:
node.cpus = max(node.cpus, 2)
Expand Down Expand Up @@ -609,8 +609,7 @@ def _generate_vagrantfile(self):
'deepsea_git_branch': self.settings.deepsea_git_branch,
'version': self.settings.version,
'provision': self.settings.provision,
'deploy_salt': bool(self.settings.version != 'makecheck' and
self.settings.version != 'caasp4' and
'deploy_salt': bool(self.settings.version not in ['makecheck', 'caasp4', 'k3s'] and
not self.suma and
not self.settings.os.startswith('ubuntu')),
'stop_before_stage': self.settings.stop_before_stage,
Expand Down Expand Up @@ -695,6 +694,7 @@ def _generate_vagrantfile(self):
'rgw_ssl': self.settings.rgw_ssl,
'internal_media_repo': self.internal_media_repo,
'developer_tools_repos': self.developer_tools_repos,
'k3s_version': self.settings.k3s_version
}

scripts = {}
Expand Down Expand Up @@ -1163,14 +1163,14 @@ def vet_configuration(self):
# --product makes sense only with SES
if not self.settings.devel_repo and not self.settings.version.startswith('ses'):
raise ProductOptionOnlyOnSES(self.settings.version)
# caasp4 is special
if self.settings.version in ['caasp4']:
# caasp4 and k3s are special
if self.settings.version in ['caasp4', 'k3s']:
each_machine_has_one_and_only_one_role = True
for node_roles in self.settings.roles:
if len(node_roles) > 1:
each_machine_has_one_and_only_one_role = False
if each_machine_has_one_and_only_one_role:
Log.debug('caasp4 cluster: each machine has one and only one role. Good.')
Log.debug(f'{self.settings.version} cluster: each machine has one and only one role. Good.')
else:
raise MultipleRolesPerMachineNotAllowedInCaaSP()
else:
Expand Down
4 changes: 2 additions & 2 deletions seslib/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ def __init__(self):
class MultipleRolesPerMachineNotAllowedInCaaSP(SesDevException):
def __init__(self):
super().__init__(
"Multiple roles per machine detected. This is not allowed in CaaSP "
"clusters. For a single-node cluster, use the --single-node option "
"Multiple roles per machine detected. This is not allowed in CaaSP or "
"k3s clusters. For a single-node cluster, use the --single-node option "
"or --roles=\"[master]\" (in this special case, the master node "
"will function also as a worker node)"
)
Expand Down
5 changes: 5 additions & 0 deletions seslib/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@
'help': 'Deprecated container image path for Ceph daemons.',
'default': '',
},
'k3s_version': {
'type': str,
'help': 'k3s version to install (defaults to latest stable)',
'default': '',
},
'ceph_image_path': {
'type': str,
'help': 'Container image path for Ceph daemons',
Expand Down
115 changes: 115 additions & 0 deletions seslib/templates/k3s/provision.sh.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
set -ex

zypper --non-interactive install curl

# need this to pick up kubectl, helm etc. which are installed to /usr/local/bin
export PATH=$PATH:/usr/local/bin

{% if k3s_version %}
export INSTALL_K3S_VERSION={{ k3s_version }}
{% endif %}

{% if node == master %}

# Lifted from seslib/templates/caasp/master.sh.j2
function wait_for_master_ready {
set +ex
echo "Waiting for master to be ready"
timeout_seconds="900"
remaining_seconds="$timeout_seconds"
interval_seconds="10"
while true ; do
set -x
ACTUAL_NUMBER_OF_MASTERS="$(kubectl get nodes 2>/dev/null | egrep -c "master\s+Ready")"
set +x
echo "masters in cluster (actual/expected): $ACTUAL_NUMBER_OF_MASTERS/1 (${remaining_seconds} seconds to timeout)"
remaining_seconds="$(( remaining_seconds - interval_seconds ))"
[ "$ACTUAL_NUMBER_OF_MASTERS" = "1" ] && break
if [ "$remaining_seconds" -le "0" ] ; then
echo "It seems unlikely that a master will ever appear. Bailing out!"
set -e
false
fi
sleep "$interval_seconds"
done
set -ex
}

# Also lifted from seslib/templates/caasp/master.sh.j2, except for the
# ACTUAL_NUMBER_O_F_WORKERS= line, which checked for "worker[0-9]+\s+Ready",
# but that won't do because the nodes are named "node[0-9]", not "worker[0-9]"
function wait_for_workers_ready {
set +ex
echo "Waiting for {{ worker_nodes }} workers to be ready"
timeout_seconds="900"
remaining_seconds="$timeout_seconds"
interval_seconds="10"
while true ; do
set -x
ACTUAL_NUMBER_OF_WORKERS="$(kubectl get nodes 2>/dev/null | egrep -c "node[0-9]+\s+Ready")"
set +x
echo "workers in cluster (actual/expected): $ACTUAL_NUMBER_OF_WORKERS/{{ worker_nodes }} (${remaining_seconds} seconds to timeout)"
remaining_seconds="$(( remaining_seconds - interval_seconds ))"
[ "$ACTUAL_NUMBER_OF_WORKERS" = "{{ worker_nodes }}" ] && break
if [ "$remaining_seconds" -le "0" ] ; then
echo "It seems unlikely that {{ worker_nodes }} workers will ever appear. Bailing out!"
set -e
false
fi
sleep "$interval_seconds"
done
set -ex
}

curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE="644" sh -

# Unfortunately we can't use `kubectl wait --for=condition=Ready node/{{ master.name }} --timeout 15m`
# because this runs before the master node even exists, and so we get
# 'Error from server (NotFound): nodes "master" not found'
wait_for_master_ready

wait_for_workers_ready

# Helm seems generally useful, let's install it
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version

# This sets KUBECONFIG for everyone to the global k3s config.
# Without this, helm will try to talk to http://localhost:8080/version
# by default, which of course won't work.
echo "export KUBECONFIG=/etc/rancher/k3s/k3s.yaml" >> /etc/profile.local

{% else %} {# node == master #}

function get_k3s_token {
set +ex
echo "Waiting for K3S token from master"
timeout_seconds="900"
remaining_seconds="$timeout_seconds"
interval_seconds="30"
while true ; do
remaining_seconds="$(( remaining_seconds - interval_seconds ))"
if scp master:/var/lib/rancher/k3s/server/node-token /tmp/k3s_token 2>/dev/null ; then
# Got the node token, probably (when exactly does that token get created
# on the master? could we conceivably have a weird race where we get an
# empty token file here?)
break
fi
if [ "$remaining_seconds" -le "0" ] ; then
echo "It seems unlikely that a master will ever appear. Bailing out!"
set -e
false
fi
echo "waiting for k3s token (${remaining_seconds} seconds to timeout)"
sleep "$interval_seconds"
done
set -ex
}

get_k3s_token
export K3S_TOKEN=$(cat /tmp/k3s_token)
rm /tmp/k3s_token

curl -sfL https://get.k3s.io | K3S_URL=https://{{ master.fqdn }}:6443 sh -

{% endif %}
4 changes: 4 additions & 0 deletions seslib/templates/provision.sh.j2
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ fi
{% elif version == 'caasp4' %}
{% include "caasp/provision.sh.j2" %}

# k3s
{% elif version == 'k3s' %}
{% include "k3s/provision.sh.j2" %}

# make check
{% elif version == 'makecheck' %}
{% include "makecheck/provision.sh.j2" %}
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ seslib =
templates/salt/deepsea/*.j2
templates/salt/suma/*.j2
templates/cephadm/*.j2
templates/k3s/*.j2

[options.entry_points]
console_scripts =
Expand Down
2 changes: 1 addition & 1 deletion tests/test_parse_config_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ def test_parse_config_yaml(isfile, exists):
['mon', 'mgr', 'storage'],
['mon', 'mgr', 'storage'],
]
assert len(settings['other_roles'].keys()) == 8
assert len(settings['other_roles'].keys()) == 9

0 comments on commit 1d858c9

Please sign in to comment.