Skip to content

Commit

Permalink
Merge branch 'main' into feat-add-last-updated-timestamp-to-mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
pehlicd authored Apr 20, 2024
2 parents e1565ee + 195cc9d commit e60d049
Show file tree
Hide file tree
Showing 13 changed files with 549 additions and 82 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,12 @@ Workflow triggers can either be executed manually when an alert is activated or
          
<img width=32 height=32 src="https://github.com/keephq/keep/blob/main/keep-ui/public/icons/servicenow-icon.png?raw=true"/>
</p>
<h3 align="center">Container Orchestration platforms</h2>
<p align="center">
<img width=32 height=32 src="https://github.com/keephq/keep/blob/main/keep-ui/public/icons/openshift-icon.png?raw=true"/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<img width=32 height=32 src="https://github.com/keephq/keep/blob/main/keep-ui/public/icons/kubernetes-icon.png?raw=true"/>
</p>
## Getting Started
### Overview
Expand Down
2 changes: 2 additions & 0 deletions docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"providers/documentation/datadog-provider",
"providers/documentation/ilert-provider",
"providers/documentation/kibana-provider",
"providers/documentation/kubernetes-provider",
"providers/documentation/discord-provider",
"providers/documentation/elastic-provider",
"providers/documentation/gcpmonitoring-provider",
Expand All @@ -101,6 +102,7 @@
"providers/documentation/mock-provider",
"providers/documentation/mysql-provider",
"providers/documentation/new-relic-provider",
"providers/documentation/openshift-provider",
"providers/documentation/opsgenie-provider",
"providers/documentation/pagerduty-provider",
"providers/documentation/pingdom-provider",
Expand Down
41 changes: 41 additions & 0 deletions docs/providers/documentation/kubernetes-provider.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: "Kubernetes"
description: "Kubernetes provider to perform rollout restart or list pods action."
---

## Inputs

- **action** (required): Determines the which action to perform (`rollout_restart`, `list_pods`).
- **kind** (required): Kind of the object to perform rollout restart action.
- **object_name** (required): Name of the object to perform rollout restart action.
- **namespace** (required): Namespace of the object to perform rollout restart or list pods action.
- **labels** (optional): Labels to filter the pods while performing list pods action and also filters before performing rollout restart.

## Outputs

- **message**: Message for the action performed.

## Authentication Parameters

This provider offers you to authenticate with Openshift using: api_server, token and insecure.

- **api_server** (required): The api server url of your Kubernetes cluster.
- **token** (required): The token of your service account to authenticate with Kubernetes.
- **insecure** (optional): If you want to skip the certificate verification, set this to `True` (default: True).

## Connecting with the Provider

To connect to Kubernetes, follow below steps:

1. Create a service account on Kubernetes.
2. Create role/clusterrole and bind to service account using rolebinding/clusterrolebinding.
3. Get the token of service account.

## Notes

- This provider allows you to interact with Kubernetes to perform rollout restart or pods listing actions.

## Useful Links

- [Access Kubernetes Cluster](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/)

34 changes: 34 additions & 0 deletions docs/providers/documentation/openshift-provider.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
title: "Openshift"
description: "Openshift provider to perform rollout restart action on specific resources."
---

## Inputs

- **kind** (required): Kind of the object which will be run rollout restart action run (`deployments`, `statefulset`, `daemonset`).
- **name** (required): Name of the object which will be run rollout restart action run.

## Outputs

- **message**: Message for the action performed.

## Authentication Parameters

This provider offers you to authenticate with Openshift using: api_server, token and insecure.

- **api_server** (required): The api server url of your Openshift cluster.
- **token** (required): The token of your user to authenticate with Openshift.
- **insecure** (optional): If you want to skip the certificate verification, set this to `True`.

## Connecting with the Provider

To connect to Openshift, follow below steps:

1. Log in to your Openshift cluster and create a new service account with required roles.
2. Get the token of the service account.
3. Use the token to authenticate with Openshift.

## Notes

- This provider allows you to interact with Openshift to perform rollout restart actions.

Binary file added keep-ui/public/icons/kubernetes-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added keep-ui/public/icons/openshift-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
203 changes: 203 additions & 0 deletions keep/providers/kubernetes_provider/kubernetes_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import pydantic
import dataclasses

from kubernetes import client
from kubernetes.client.rest import ApiException
import datetime

from keep.contextmanager.contextmanager import ContextManager
from keep.providers.base.base_provider import BaseProvider
from keep.providers.models.provider_config import ProviderScope, ProviderConfig


@pydantic.dataclasses.dataclass
class KubernetesProviderAuthConfig:
"""Kubernetes authentication configuration."""

api_server: str = dataclasses.field(
default=None,
metadata={
"name": "api_server",
"description": "The kubernetes api server url",
"required": True,
"sensitive": False,
},
)
token: str = dataclasses.field(
default=None,
metadata={
"name": "token",
"description": "Bearer token to access kubernetes",
"required": True,
"sensitive": True,
},
)
insecure: bool = dataclasses.field(
default=True,
metadata={
"name": "insecure",
"description": "Whether to skip tls verification (default: True)",
"required": False,
"sensitive": False,
},
)


class KubernetesProvider(BaseProvider):
"""Perform actions like rollout restart objects or list pods on Kubernetes."""

provider_id: str
PROVIDER_DISPLAY_NAME = "Kubernetes"

PROVIDER_SCOPES = [
ProviderScope(
name="connect_to_kubernetes",
description="Check if the provided token can connect to the kubernetes server",
mandatory=True,
alias="Connect to the kubernetes",
)
]

def __init__(
self, context_manager, provider_id: str, config: ProviderConfig
):
super().__init__(context_manager, provider_id, config)
self.authentication_config = None
self.validate_config()

def dispose(self):
"""Dispose the provider."""
pass

def validate_config(self):
"""
Validate the required configuration for the Kubernetes provider.
"""
if self.config.authentication is None:
self.config.authentication = {}
self.authentication_config = KubernetesProviderAuthConfig(**self.config.authentication)

def __create_k8s_client(self):
"""
Create a Kubernetes client.
"""
client_configuration = client.Configuration()

client_configuration.host = self.authentication_config.api_server
client_configuration.verify_ssl = not self.authentication_config.insecure
client_configuration.api_key = {"authorization": "Bearer " + self.authentication_config.token}

return client.ApiClient(client_configuration)

def validate_scopes(self):
"""
Validate that the provided token has the required scopes to use the provider.
"""
self.logger.info("Validating scopes for Kubernetes provider")
try:
self.__create_k8s_client()
self.logger.info("Successfully connected to the Kubernetes server")
scopes = {
"connect_to_kubernetes": True,
}
except Exception as e:
self.logger.error(f"Failed to connect to the Kubernetes server: {str(e)}")
scopes = {
"connect_to_kubernetes": str(e),
}

return scopes

def _notify(self, action: str, kind: str, object_name: str, namespace: str, labels: str, **kwargs):
if labels is None:
labels = []
if action == "rollout_restart":
self.__rollout_restart(kind=kind, name=object_name, namespace=namespace, labels=labels)
elif action == "list_pods":
self.__list_pods(namespace=namespace, labels=labels)
else:
raise NotImplementedError(f"Action {action} is not implemented")

def __rollout_restart(self, kind, name, namespace, labels):
api_client = self.__create_k8s_client()
self.logger.info(f"Performing rollout restart for {kind} {name} using kubernetes provider")
now = datetime.datetime.now(datetime.timezone.utc)
now = str(now.isoformat("T") + "Z")
body = {
'spec': {
'template': {
'metadata': {
'annotations': {
'kubectl.kubernetes.io/restartedAt': now
}
}
}
}
}
apps_v1 = client.AppsV1Api(api_client)
try:
if kind == "deployment":
deployment_list = apps_v1.list_namespaced_deployment(namespace=namespace, label_selector=labels)
if not deployment_list.items:
raise ValueError(f"Deployment with labels {labels} not found in namespace {namespace}")
apps_v1.patch_namespaced_deployment(name=name, namespace=namespace, body=body)
elif kind == "statefulset":
statefulset_list = apps_v1.list_namespaced_stateful_set(namespace=namespace, label_selector=labels)
if not statefulset_list.items:
raise ValueError(f"StatefulSet with labels {labels} not found in namespace {namespace}")
apps_v1.patch_namespaced_stateful_set(name=name, namespace=namespace, body=body)
elif kind == "daemonset":
daemonset_list = apps_v1.list_namespaced_daemon_set(namespace=namespace, label_selector=labels)
if not daemonset_list.items:
raise ValueError(f"DaemonSet with labels {labels} not found in namespace {namespace}")
apps_v1.patch_namespaced_daemon_set(name=name, namespace=namespace, body=body)
else:
raise ValueError(f"Unsupported kind {kind} to perform rollout restart")
except ApiException as e:
self.logger.error(f"Error performing rollout restart for {kind} {name}: {e}")
raise Exception(f"Error performing rollout restart for {kind} {name}: {e}")

self.logger.info(f"Successfully performed rollout restart for {kind} {name}")

def __list_pods(self, namespace, labels):
api_client = self.__create_k8s_client()
core_v1 = client.CoreV1Api(api_client)
if namespace is None:
namespace = "default"
self.logger.info(f"Listing pods in namespace {namespace} with labels {labels}")
try:
core_v1.list_namespaced_pod(namespace=namespace, label_selector=labels)
except ApiException as e:
self.logger.error(f"Error listing pods in namespace {namespace} with labels {labels}: {e}")
raise Exception(f"Error listing pods in namespace {namespace} with labels {labels}: {e}")

self.logger.info(f"Successfully listed pods in namespace {namespace} with labels {labels}")


if __name__ == "__main__":
# Output debug messages
import logging

logging.basicConfig(level=logging.DEBUG, handlers=[logging.StreamHandler()])

# Load environment variables
import os
url = os.environ.get("KUBERNETES_URL")
token = os.environ.get("KUBERNETES_TOKEN")
insecure = os.environ.get("KUBERNETES_INSECURE", "false").lower() == "true"
context_manager = ContextManager(
tenant_id="singletenant",
workflow_id="test",
)
config = ProviderConfig(
authentication={
"api_server": url,
"token": token,
"insecure": insecure,
},
)

kubernetes_provider = KubernetesProvider(context_manager, "kubernetes_keephq", config)

result = kubernetes_provider.notify("rollout_restart", "deployment", "nginx", "default", {"app": "nginx"})
print(result)
Empty file.
Loading

0 comments on commit e60d049

Please sign in to comment.