From db6c9ac78c000b6b69b3f16de9e87a5442cbaf39 Mon Sep 17 00:00:00 2001 From: Vasyl Saienko Date: Wed, 18 Sep 2024 10:58:45 +0000 Subject: [PATCH] Add local volume provisioner chart Some applications require perisitant volumes to be stored on the hosts where they running, usually its done via kubernetes PV. One of PV implementations is local-volume-provisioner [0] This patch adds helm chart to deploy LVP. Since LVP creates a volumes for each mountpoint, helm chart provides a script to create mountpoints in the directory, which later exposed to kubernetes as individual volumes. Change-Id: I3f61088ddcbd0a83a729eb940cbf9b2bf1e65894 --- local-volume-provisioner/Chart.yaml | 24 ++ local-volume-provisioner/requirements.yaml | 18 + .../templates/bin/_fakemount.py.tpl | 377 ++++++++++++++++++ .../templates/configmap-bin.yaml | 58 +++ .../templates/configmap-etc.yaml | 33 ++ .../templates/daemonset-lvp.yaml | 212 ++++++++++ .../templates/job-image-repo-sync.yaml | 18 + .../templates/secret-registry.yaml | 17 + .../templates/storageclasses.yaml | 27 ++ local-volume-provisioner/values.yaml | 153 +++++++ .../notes/local-volume-provisioner.yaml | 4 + 11 files changed, 941 insertions(+) create mode 100644 local-volume-provisioner/Chart.yaml create mode 100644 local-volume-provisioner/requirements.yaml create mode 100644 local-volume-provisioner/templates/bin/_fakemount.py.tpl create mode 100644 local-volume-provisioner/templates/configmap-bin.yaml create mode 100644 local-volume-provisioner/templates/configmap-etc.yaml create mode 100644 local-volume-provisioner/templates/daemonset-lvp.yaml create mode 100644 local-volume-provisioner/templates/job-image-repo-sync.yaml create mode 100644 local-volume-provisioner/templates/secret-registry.yaml create mode 100644 local-volume-provisioner/templates/storageclasses.yaml create mode 100644 local-volume-provisioner/values.yaml create mode 100644 releasenotes/notes/local-volume-provisioner.yaml diff --git a/local-volume-provisioner/Chart.yaml b/local-volume-provisioner/Chart.yaml new file mode 100644 index 000000000..a33684e87 --- /dev/null +++ b/local-volume-provisioner/Chart.yaml @@ -0,0 +1,24 @@ +# 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. + +--- +apiVersion: v1 +appVersion: v1.0.0 +description: OpenStack-Helm local-volume-provisioner +name: local-volume-provisioner +version: 0.1.0 +home: https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner +sources: + - https://opendev.org/openstack/openstack-helm +maintainers: + - name: OpenStack-Helm Authors +... diff --git a/local-volume-provisioner/requirements.yaml b/local-volume-provisioner/requirements.yaml new file mode 100644 index 000000000..84f0affae --- /dev/null +++ b/local-volume-provisioner/requirements.yaml @@ -0,0 +1,18 @@ +# 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. + +--- +dependencies: + - name: helm-toolkit + repository: file://../helm-toolkit + version: ">= 0.1.0" +... diff --git a/local-volume-provisioner/templates/bin/_fakemount.py.tpl b/local-volume-provisioner/templates/bin/_fakemount.py.tpl new file mode 100644 index 000000000..e9a937f4e --- /dev/null +++ b/local-volume-provisioner/templates/bin/_fakemount.py.tpl @@ -0,0 +1,377 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 Mirantis, Inc. +# +# 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. +"""Fakemount python module +The module is aimed to crate fake mountpoints (--bind). +Example: + python3 fakemount --config-file '/root/mymount.yml' +Attributes: + config-file - file path to config file that contains fake mounts. +""" +__version__ = "1.0" +import argparse +import logging +import os +import re +import subprocess +import sys +from collections import defaultdict +import yaml +logging.basicConfig(stream=sys.stdout, level=logging.INFO) +LOG = logging.getLogger(__name__) +MOUNT_BIN = "/bin/mount" +###Fork https://github.com/b10011/pyfstab/ ##################################### +# Latest commit 828540d +class InvalidEntry(Exception): + """ + Raised when a string cannot be generated because of the Entry is invalid. + """ +class InvalidFstabLine(Exception): + """ + Raised when a line is invalid in fstab. This doesn't just mean that the + Entry will be invalid but also that the system can not process the fstab + file fully either. + """ +class Entry: + """ + Handles parsing and formatting fstab line entries. + :var device: + (str or None) - + Fstab device (1st parameter in the fstab entry) + :var dir: + (str or None) - + Fstab device (2nd parameter in the fstab entry) + :var type: + (str or None) - + Fstab device (3rd parameter in the fstab entry) + :var options: + (str or None) - + Fstab device (4th parameter in the fstab entry) + :var dump: + (int or None) - + Fstab device (5th parameter in the fstab entry) + :var fsck: + (int or None) - + Fstab device (6th parameter in the fstab entry) + :var valid: + (bool) - + Whether the Entry is valid or not. Can be checked with "if entry:". + """ + def __init__( + self, + _device=None, + _dir=None, + _type=None, + _options=None, + _dump=None, + _fsck=None, + ): + """ + :param _device: Fstab device (1st parameter in the fstab entry) + :type _device: str + :param _dir: Fstab device (2nd parameter in the fstab entry) + :type _dir: str + :param _type: Fstab device (3rd parameter in the fstab entry) + :type _type: str + :param _options: Fstab device (4th parameter in the fstab entry) + :type _options: str + :param _dump: Fstab device (5th parameter in the fstab entry) + :type _dump: int + :param _fsck: Fstab device (6th parameter in the fstab entry) + :type _fsck: int + """ + self.device = _device + self.dir = _dir + self.type = _type + self.options = _options + self.dump = _dump + self.fsck = _fsck + self.valid = True + self.valid &= self.device is not None + self.valid &= self.dir is not None + self.valid &= self.type is not None + self.valid &= self.options is not None + self.valid &= self.dump is not None + self.valid &= self.fsck is not None + def read_string(self, line): + """ + Parses an entry from a string + :param line: Fstab entry line. + :type line: str + :return: self + :rtype: Entry + :raises InvalidEntry: If the data in the string cannot be parsed. + """ + line = line.strip() + if line and not line[0] == "#": + parts = re.split(r"\s+", line) + if len(parts) == 6: + [_device, _dir, _type, _options, _dump, _fsck] = parts + _dump = int(_dump) + _fsck = int(_fsck) + self.device = _device + self.dir = _dir + self.type = _type + self.options = _options + self.dump = _dump + self.fsck = _fsck + self.valid = True + return self + else: + raise InvalidFstabLine() + self.device = None + self.dir = None + self.type = None + self.options = None + self.dump = None + self.fsck = None + self.valid = False + raise InvalidEntry("Entry cannot be parsed") + def write_string(self): + """ + Formats the Entry into fstab entry line. + :return: Fstab entry line. + :rtype: str + :raises InvalidEntry: + A string cannot be generated because the entry is invalid. + """ + if self: + return "{} {} {} {} {} {}".format( + self.device, + self.dir, + self.type, + self.options, + self.dump, + self.fsck, + ) + else: + raise InvalidEntry("Entry cannot be formatted") + def __bool__(self): + return self.valid + def __str__(self): + return self.write_string() + def __repr__(self): + try: + return "".format(str(self)) + except InvalidEntry: + return "" +class Fstab: + """ + Handles reading, parsing, formatting and writing of fstab files. + :var entries: + (list[Entry]) - + List of entries. + When writing to a file, entries are listed from this list. + :var entries_by_device: + (dict[str, list[Entry]]) - + Fstab entries by device. + :var entry_by_dir: + (dict[str, Entry]) - + Fstab entry by directory. + :var entries_by_type: + (dict[str, list[Entry]]) - + Fstab entries by type. + """ + def __init__(self): + self.entries = [] + # A single device can have multiple mountpoints + self.entries_by_device = defaultdict(list) + # If multiple devices have same mountpoint, only the last entry in the + # fstab file is taken into consideration + self.entry_by_dir = dict() + # And the most obvious one, many entries can have mountpoints of same + # type + self.entries_by_type = defaultdict(list) + def read_string(self, data, only_valid=False): + """ + Parses entries from a data string + :param data: Contents of the fstab file + :type data: str + :param only_valid: + Skip the entries that do not actually mount. For example, if device + A is mounted to directory X and later device B is mounted to + directory X, the A mount to X is undone by the system. + :type only_valid: bool + :return: self + :rtype: Fstab + """ + for line in reversed(data.splitlines()): + try: + entry = Entry().read_string(line) + if entry and ( + not only_valid or entry.dir not in self.entry_by_dir + ): + self.entries.insert(0, entry) + self.entries_by_device[entry.device].insert(0, entry) + self.entry_by_dir[entry.dir] = entry + self.entries_by_type[entry.type].insert(0, entry) + except InvalidEntry: + pass + return self + def write_string(self): + """ + Formats entries into a string. + :return: Formatted fstab file. + :rtype: str + :raises InvalidEntry: + A string cannot be generated because one of the entries is invalid. + """ + return "\n".join(str(entry) for entry in self.entries) + def read_file(self, handle, only_valid=False): + """ + Parses entries from a file + :param handle: File handle + :type handle: file + :param only_valid: + Skip the entries that do not actually mount. For example, if device + A is mounted to directory X and later device B is mounted to + directory X, the A mount to X is undone by the system. + :type only_valid: bool + :return: self + :rtype: Fstab + """ + self.read_string(handle.read(), only_valid) + return self + def write_file(self, handle): + """ + Parses entries in data string + :param path: File handle + :type path: file + :return: self + :rtype: Fstab + """ + handle.write(str(self)) + return self + def __bool__(self): + return len(self.entries) > 0 + def __str__(self): + return self.write_string() + def __repr__(self): + res = "