-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
kubernetes: add support to manage k8s cluster(s)
Initial support to create and manage k8s clusters Signed-off-by: Douglas Schilling Landgraf <[email protected]>
- Loading branch information
Showing
9 changed files
with
332 additions
and
2 deletions.
There are no files selected for viewing
Binary file not shown.
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,96 @@ | ||
# Vehicle Signal Specification (VSS) Middleware to create and manage Kubernetes cluster(s) | ||
|
||
## Table of Contents | ||
- [Overview](#overview) | ||
- [Why?](#why) | ||
- [Features](#features) | ||
- [Requirements](#requirements) | ||
- [Kubernetes](#kubernetes) | ||
- [Creating a Kubernetes cluster](#creating-a-kubernetes-cluster) | ||
- [Creating a pod](#creating-a-pod) | ||
- [Deleting a cluster](#deleting-a-cluster) | ||
|
||
## Overview | ||
|
||
The `KubernetesClusterManager` class is part of VSS Middleware to provide an easy-to-use interface for creating, managing, and interacting with Kubernetes clusters using `kind` (Kubernetes in Docker). It leverages the `kubectl` command-line tool to apply, update, and delete resources, making it simple to manage Pods, Namespaces, and other Kubernetes resources. | ||
|
||
## Why? | ||
|
||
The purpose of creating this resource in the Middleware is to enable seamless interaction and resource management within Kubernetes clusters (cloud vendors) directly from edge devices, such as vehicles, as more edge vendors are leveraging cloud connectivity to dynamically spawn and manage cloud-based resources from the edge. | ||
|
||
## Features | ||
|
||
- **Cluster Management**: Create and delete Kubernetes clusters using `kind`. | ||
- **Resource Management**: Apply, update, and delete Kubernetes resources (e.g., Pods, Namespaces) using YAML manifests. | ||
- **Resource Existence Handling**: Automatically check if resources exist before applying or creating them. | ||
- **Customizable**: Supports applying user-defined YAML manifests for flexible resource management. | ||
- **Built-in Error Handling**: Provides robust error handling using exceptions (`RuntimeError`, `FileNotFoundError`) for better reliability and debugging. | ||
|
||
## Requirements | ||
|
||
- Python 3.6+ | ||
- `kind` (Kubernetes in Docker) | ||
- `kubectl` or `oc` (OpenShift CLI) | ||
|
||
## Kubernetes | ||
|
||
### Creating a kubernetes cluster | ||
|
||
```bash | ||
./create_cluster | ||
enabling experimental podman provider | ||
Creating cluster "test-cluster" ... | ||
✓ Ensuring node image (kindest/node:v1.31.2) 🖼 | ||
✓ Preparing nodes 📦 📦 📦 | ||
✓ Writing configuration 📜 | ||
✓ Starting control-plane 🕹️ | ||
✓ Installing CNI 🔌 | ||
✓ Installing StorageClass 💾 | ||
✓ Joining worker nodes 🚜 | ||
Set kubectl context to "kind-test-cluster" | ||
You can now use your cluster with: | ||
|
||
kubectl cluster-info --context kind-test-cluster | ||
|
||
Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂 | ||
Cluster 'test-cluster' created successfully. | ||
Listing nodes in the cluster... | ||
NAME STATUS ROLES AGE VERSION | ||
test-cluster-control-plane NotReady control-plane 15s v1.31.2 | ||
test-cluster-worker NotReady <none> 2s v1.31.2 | ||
test-cluster-worker2 NotReady <none> 2s v1.31.2 | ||
``` | ||
|
||
Code: | ||
```bash | ||
from vss_lib.cloud.k8s import KubernetesClusterManager | ||
|
||
PATH_KIND_CONFIG="/usr/local/lib/python3.12/site-packages/vss_lib/cloud/k8s/kind-config.yaml" | ||
|
||
if __name__ == "__main__": | ||
manager = KubernetesClusterManager(cluster_name='test-cluster', config_file=f"{PATH_KIND_CONFIG}") | ||
manager.create_cluster() | ||
manager.list_nodes() | ||
``` | ||
### Creating a pod | ||
```bash | ||
$ ./create_example_pod | ||
Creating custom namespace... | ||
namespace/custom-namespace created | ||
Creating custom pod in custom namespace... | ||
pod/custom-pod created | ||
|
||
$ kubectl get pods -n custom-namespace | ||
NAME READY STATUS RESTARTS AGE | ||
custom-pod 1/1 Running 0 20s | ||
``` | ||
### Deleting a cluster | ||
```bash | ||
$ ./delete_cluster | ||
enabling experimental podman provider | ||
Deleting cluster "test-cluster" ... | ||
Deleted nodes: ["test-cluster-worker2" "test-cluster-worker" "test-cluster-control-plane"] | ||
``` |
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,24 @@ | ||
#!/usr/bin/env python | ||
# | ||
# 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. | ||
# | ||
# Script to create a Kubernetes cluster using KubernetesClusterManager. | ||
|
||
from vss_lib.cloud.k8s import KubernetesClusterManager | ||
|
||
PATH_KIND_CONFIG = "/usr/local/lib/python3.12/site-packages/vss_lib/cloud/k8s/kind-config.yaml" | ||
|
||
if __name__ == "__main__": | ||
manager = KubernetesClusterManager(cluster_name='test-cluster', config_file=f"{PATH_KIND_CONFIG}") | ||
manager.create_cluster() | ||
manager.list_nodes() |
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,53 @@ | ||
#!/usr/bin/env python | ||
# | ||
# 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. | ||
# | ||
# Script to create a custom namespace and a pod within it using apply_yaml method in KubernetesClusterManager. | ||
from vss_lib.cloud.k8s import KubernetesClusterManager | ||
|
||
if __name__ == "__main__": | ||
manager = KubernetesClusterManager(cluster_name='test-cluster') | ||
|
||
# Define a custom YAML manifest for a Namespace | ||
namespace_yaml = """ | ||
apiVersion: v1 | ||
kind: Namespace | ||
metadata: | ||
name: custom-namespace | ||
""" | ||
|
||
# Apply the Namespace YAML manifest with existence check | ||
print("Creating custom namespace...") | ||
manager.apply_yaml(namespace_yaml, resource_type='namespace', resource_name='custom-namespace') | ||
|
||
# Define a custom YAML manifest for a Pod within the namespace | ||
custom_pod_yaml = """ | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: custom-pod | ||
namespace: custom-namespace | ||
labels: | ||
app: custom-app | ||
spec: | ||
containers: | ||
- name: custom-container | ||
image: busybox | ||
command: ["sleep", "3600"] | ||
ports: | ||
- containerPort: 8080 | ||
""" | ||
|
||
# Apply the custom Pod YAML manifest with existence check | ||
print("Creating custom pod in custom namespace...") | ||
manager.apply_yaml(custom_pod_yaml, resource_type='pod', resource_name='custom-pod', namespace='custom-namespace') |
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,21 @@ | ||
#!/usr/bin/env python | ||
# | ||
# 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. | ||
# | ||
# Script to delete the entire kind Kubernetes cluster using KubernetesClusterManager. | ||
|
||
from vss_lib.cloud.k8s import KubernetesClusterManager | ||
|
||
if __name__ == "__main__": | ||
manager = KubernetesClusterManager(cluster_name='test-cluster') | ||
manager.delete_cluster() |
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
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,130 @@ | ||
""" | ||
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 subprocess | ||
import os | ||
|
||
|
||
class KubernetesClusterManager: | ||
def __init__(self, cluster_name='kind-cluster', config_file='kind-config.yaml', kind_version='0.25.0', kubectl_version='v1.27.0'): | ||
self.cluster_name = cluster_name | ||
self.config_file = config_file | ||
self.kind_version = kind_version | ||
self.kubectl_version = kubectl_version | ||
self._check_tools_installed() | ||
|
||
def _check_tools_installed(self): | ||
"""Check if kind, kubectl, or oc are installed on the system.""" | ||
self._check_command('kind', install_instructions=f""" | ||
You can install 'kind' by running the following commands: | ||
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v{self.kind_version}/kind-$(uname)-amd64 | ||
chmod +x ./kind | ||
sudo mv ./kind /usr/local/bin/kind | ||
""") | ||
|
||
# Check if either kubectl or oc exists | ||
kubectl_or_oc_found = any(self._check_command(cmd, suppress_output=True) for cmd in ['kubectl', 'oc']) | ||
if not kubectl_or_oc_found: | ||
print("Error: Neither 'kubectl' nor 'oc' is installed or found in the system's PATH.") | ||
print(f"You can install 'kubectl' by running the following commands (default version {self.kubectl_version}):") | ||
print(f""" | ||
curl -LO "https://dl.k8s.io/release/{self.kubectl_version}/bin/$(uname -s | tr '[:upper:]' '[:lower:]')/amd64/kubectl" | ||
chmod +x ./kubectl | ||
sudo mv ./kubectl /usr/local/bin/kubectl | ||
""") | ||
print("Alternatively, you can install 'oc' by following instructions at:") | ||
print(" https://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest/") | ||
|
||
exit(1) | ||
|
||
def _check_command(self, cmd, install_instructions=None, suppress_output=False): | ||
"""Check if a command is available on the system.""" | ||
try: | ||
# Check if the command exists in the PATH | ||
subprocess.run(['which', cmd], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||
return True | ||
except (subprocess.CalledProcessError, FileNotFoundError): | ||
if not suppress_output: | ||
print(f"Error: '{cmd}' is not installed or not found in the system's PATH.") | ||
if install_instructions: | ||
print(install_instructions) | ||
return False | ||
|
||
def create_cluster(self): | ||
"""Create a Kubernetes cluster using kind and the provided configuration file.""" | ||
if not os.path.exists(self.config_file): | ||
raise FileNotFoundError(f"Configuration file '{self.config_file}' not found.") | ||
|
||
try: | ||
subprocess.run( | ||
['kind', 'create', 'cluster', '--name', self.cluster_name, '--config', self.config_file], | ||
check=True | ||
) | ||
except subprocess.CalledProcessError as e: | ||
raise RuntimeError(f"Failed to create cluster: {e}") | ||
|
||
def list_nodes(self): | ||
"""List all nodes in the Kubernetes cluster.""" | ||
try: | ||
subprocess.run(['kubectl', 'get', 'nodes'], check=True) | ||
except subprocess.CalledProcessError as e: | ||
raise RuntimeError(f"Failed to list nodes: {e}") | ||
|
||
def delete_cluster(self): | ||
"""Delete the kind Kubernetes cluster.""" | ||
try: | ||
subprocess.run(['kind', 'delete', 'cluster', '--name', self.cluster_name], check=True) | ||
except subprocess.CalledProcessError as e: | ||
raise RuntimeError(f"Failed to delete the cluster: {e}") | ||
|
||
def apply_yaml(self, yaml_manifest, resource_type=None, resource_name=None, namespace=None): | ||
""" | ||
Apply a YAML manifest using kubectl, checking if the resource already exists. | ||
If the resource exists, it prints a message and does not re-apply. | ||
Args: | ||
yaml_manifest (str): The YAML content to apply. | ||
resource_type (str, optional): The type of the resource (e.g., pod, namespace). | ||
resource_name (str, optional): The name of the resource. | ||
namespace (str, optional): The namespace of the resource (if applicable). | ||
""" | ||
if resource_type and resource_name: | ||
# Check if the resource already exists | ||
cmd = ['kubectl', 'get', resource_type, resource_name] | ||
if namespace: | ||
cmd.extend(['-n', namespace]) | ||
|
||
try: | ||
subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||
return | ||
except subprocess.CalledProcessError: | ||
# Resource does not exist, proceed to apply the manifest | ||
pass | ||
|
||
# Apply the manifest | ||
try: | ||
subprocess.run( | ||
['kubectl', 'apply', '-f', '-'], | ||
input=yaml_manifest.encode('utf-8'), | ||
check=True | ||
) | ||
except subprocess.CalledProcessError as e: | ||
raise RuntimeError(f"Failed to apply the YAML manifest: {e}") | ||
|
||
def delete_resource(self, resource_type, resource_name): | ||
"""Delete a resource in the Kubernetes cluster by type and name.""" | ||
try: | ||
subprocess.run(['kubectl', 'delete', resource_type, resource_name], check=True) | ||
except subprocess.CalledProcessError as e: | ||
raise RuntimeError(f"Failed to delete {resource_type} '{resource_name}': {e}") |
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,6 @@ | ||
kind: Cluster | ||
apiVersion: kind.x-k8s.io/v1alpha4 | ||
nodes: | ||
- role: control-plane | ||
- role: worker | ||
- role: worker |
Empty file.