Skip to content

Commit

Permalink
feat: don't allow update/edit provisioned resources (#1916)
Browse files Browse the repository at this point in the history
  • Loading branch information
shahargl authored Sep 15, 2024
1 parent 48d9826 commit 7e9d06d
Show file tree
Hide file tree
Showing 35 changed files with 1,060 additions and 232 deletions.
29 changes: 29 additions & 0 deletions docs/deployment/provision/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
title: "Overview"
---

Keep supports various deployment and provisioning strategies to accommodate different environments and use cases, from development setups to production deployments.

### Provisioning Options

Keep offers two main provisioning options:

1. [**Provider Provisioning**](/deployment/provision/provider) - Set up and manage data providers for Keep.
2. [**Workflow Provisioning**](/deployment/provision/workflow) - Configure and manage workflows within Keep.

Choosing the right provisioning strategy depends on your specific use case, deployment environment, and scalability requirements. You can read more about each provisioning option in their respective sections.

### How To Configure Provisioning

<Tip>
Some provisioning options require additional environment variables. These will be covered in detail on the specific provisioning pages.
</Tip>

Provisioning in Keep is controlled through environment variables and configuration files. The main environment variables for provisioning are:

| Provisioning Type | Environment Variable | Purpose |
|-------------------|----------------------|---------|
| **Provider** | `KEEP_PROVIDERS` | JSON string containing provider configurations |
| **Workflow** | `KEEP_WORKFLOWS_DIRECTORY` | Directory path containing workflow configuration files |

For more details on each provisioning strategy, including setup instructions and implications, refer to the respective sections.
58 changes: 58 additions & 0 deletions docs/deployment/provision/provider.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: "Providers Provisioning"
---

<Tip>For any questions or issues related to provider provisioning, please join our [Slack](https://slack.keephq.dev) community.</Tip>

Provider provisioning in Keep allows you to set up and manage data providers dynamically. This feature enables you to configure various data sources that Keep can interact with, such as monitoring systems, databases, or other services.

### Configuring Providers

To provision providers, set the `KEEP_PROVIDERS` environment variable with a JSON string containing the provider configurations. Here's an example:

```json
{
"keepVictoriaMetrics": {
"type": "victoriametrics",
"authentication": {
"VMAlertHost": "http://localhost",
"VMAlertPort": 1234
}
},
"keepClickhouse1": {
"type": "clickhouse",
"authentication": {
"host": "http://localhost",
"port": 1234,
"username": "keep",
"password": "keep",
"database": "keep-db"
}
}
}
```

Spin up Keep with this `KEEP_PROVIDERS` value:
```json
# ENV
KEEP_PROVIDERS={"keepVictoriaMetrics":{"type":"victoriametrics","authentication":{"VMAlertHost":"http://localhost","VMAlertPort": 1234}},"keepClickhouse1":{"type":"clickhouse","authentication":{"host":"http://localhost","port":"4321","username":"keep","password":"1234","database":"keepdb"}}}
```

### Supported Providers

Keep supports a wide range of provider types. Each provider type has its own specific configuration requirements.
To see the full list of supported providers and their detailed configuration options, please refer to our comprehensive provider documentation.


### Update Provisioned Providers

Provider configurations can be updated dynamically by changing the `KEEP_PROVIDERS` environment variable.

On every restart, Keep reads this environment variable and determines which providers need to be added or removed.

This process allows for flexible management of data sources without requiring manual intervention. By simply updating the `KEEP_PROVIDERS` variable and restarting the application, you can efficiently add new providers, remove existing ones, or modify their configurations.

The high-level provisioning mechanism:
1. Keep reads the `KEEP_PROVIDERS` value.
2. Keep checks if there are any provisioned providers that are no longer in the `KEEP_PROVIDERS` value, and deletes them.
3. Keep installs all providers from the `KEEP_PROVIDERS` value.
36 changes: 36 additions & 0 deletions docs/deployment/provision/workflow.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
title: "Workflow Provisioning"
---

<Tip>For any questions or issues related to workflow provisioning, please join our [Slack](https://slack.keephq.dev) community.</Tip>

Workflow provisioning in Keep allows you to set up and manage workflows dynamically. This feature enables you to configure various automated processes and tasks within your Keep deployment.

### Configuring Workflows

To provision workflows, follow these steps:

1. Set the `KEEP_WORKFLOWS_DIRECTORY` environment variable to the path of your workflow configuration directory.
2. Create workflow configuration files in the specified directory.

Example directory structure:
```
/path/to/workflows/
├── workflow1.yaml
├── workflow2.yaml
└── workflow3.yaml
```
### Update Provisioned Workflows

On every restart, Keep reads the `KEEP_WORKFLOWS_DIRECTORY` environment variable and determines which workflows need to be added, removed, or updated.

This process allows for flexible management of workflows without requiring manual intervention. By simply updating the workflow files in the `KEEP_WORKFLOWS_DIRECTORY` and restarting the application, you can efficiently add new workflows, remove existing ones, or modify their configurations.

The high-level provisioning mechanism:
1. Keep reads the `KEEP_WORKFLOWS_DIRECTORY` value.
2. Keep lists all workflow files under the `KEEP_WORKFLOWS_DIRECTORY` directory.
3. Keep compares the current workflow files with the previously provisioned workflows:
- New workflow files are provisioned.
- Missing workflow files are deprovisioned.
- Updated workflow files are re-provisioned with the new configuration.
4. Keep updates its internal state to reflect the current set of provisioned workflows.
8 changes: 8 additions & 0 deletions docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@
"deployment/authentication/keycloak-auth"
]
},
{
"group": "Provision",
"pages": [
"deployment/provision/overview",
"deployment/provision/provider",
"deployment/provision/workflow"
]
},
"deployment/secret-manager",
"deployment/docker",
"deployment/kubernetes",
Expand Down
51 changes: 41 additions & 10 deletions keep-ui/app/providers/provider-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
Accordion,
AccordionHeader,
AccordionBody,

Badge,
} from "@tremor/react";
import {
ExclamationCircleIcon,
Expand Down Expand Up @@ -520,6 +520,7 @@ const ProviderForm = ({
onChange={(value) => handleInputChange({ target: { name: configKey, value } })}
placeholder={method.placeholder || `Select ${configKey}`}
error={Object.keys(inputErrors).includes(configKey)}
disabled={provider.provisioned}
>
{method.options.map((option) => (
<SelectItem key={option} value={option}>
Expand All @@ -541,6 +542,7 @@ const ProviderForm = ({
color="orange"
size="xs"
onClick={addEntry(configKey)}
disabled={provider.provisioned}
>
Add Entry
</Button>
Expand All @@ -550,6 +552,7 @@ const ProviderForm = ({
value={formValues[configKey] || []}
onChange={(value) => handleDictInputChange(configKey, value)}
error={Object.keys(inputErrors).includes(configKey)}
disabled={provider.provisioned}
/>
</div>
);
Expand All @@ -565,6 +568,7 @@ const ProviderForm = ({
inputFileRef.current.click();
}}
icon={ArrowDownOnSquareIcon}
disabled={provider.provisioned}
>
{selectedFile ? `File Chosen: ${selectedFile}` : `Upload a ${method.name}`}
</Button>
Expand All @@ -581,6 +585,7 @@ const ProviderForm = ({
}
handleInputChange(e);
}}
disabled={provider.provisioned}
/>
</>
);
Expand All @@ -597,6 +602,7 @@ const ProviderForm = ({
autoComplete="off"
error={Object.keys(inputErrors).includes(configKey)}
placeholder={method.placeholder || `Enter ${configKey}`}
disabled={provider.provisioned}
/>
</>
);
Expand Down Expand Up @@ -694,6 +700,13 @@ const ProviderForm = ({
<div>
<div className="flex flex-row">
<Title>Connect to {provider.display_name}</Title>
{/* Display the Provisioned Badge if the provider is provisioned */}
{provider.provisioned && (
<Badge color="orange" className="ml-2">
Provisioned
</Badge>
)}

<Link
href={`http://docs.keephq.dev/providers/documentation/${provider.type}-provider`}
target="_blank"
Expand All @@ -708,6 +721,20 @@ const ProviderForm = ({
</Link>
</div>

{provider.provisioned &&
<div className="w-full mt-4">
<Callout
icon={ExclamationTriangleIcon}
color="orange"
className="w-full"
>
<Text>
Editing provisioned providers is not possible from UI.
</Text>
</Callout>
</div>
}

{provider.provider_description && (
<Subtitle>{provider.provider_description}</Subtitle>
)}
Expand Down Expand Up @@ -885,7 +912,7 @@ const ProviderForm = ({
variant="secondary"
color="orange"
className="mt-2.5"
disabled={!installOrUpdateWebhookEnabled}
disabled={!installOrUpdateWebhookEnabled || provider.provisioned}
tooltip={
!installOrUpdateWebhookEnabled
? "Fix required webhook scopes and refresh scopes to enable"
Expand Down Expand Up @@ -928,16 +955,20 @@ const ProviderForm = ({
</Button>
{installedProvidersMode && Object.keys(provider.config).length > 0 && (
<>
<Button onClick={deleteProvider} color="red" className="mr-2.5">
<Button onClick={deleteProvider} color="orange" className="mr-2.5" disabled={provider.provisioned} variant="secondary">
Delete
</Button>
<Button
loading={isLoading}
onClick={handleUpdateClick}
color="orange"
>
Update
</Button>
<div className="relative">
<Button
loading={isLoading}
onClick={handleUpdateClick}
color="orange"
disabled={provider.provisioned}
variant="secondary"
>
Update
</Button>
</div>
</>
)}
{!installedProvidersMode && Object.keys(provider.config).length > 0 && (
Expand Down
10 changes: 10 additions & 0 deletions keep-ui/app/providers/provider-tile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import "./provider-tile.css";
import moment from "moment";
import ImageWithFallback from "@/components/ImageWithFallback";
import { FaCode } from "react-icons/fa";

interface Props {
provider: Provider;
Expand Down Expand Up @@ -200,6 +201,15 @@ export default function ProviderTile({ provider, onClick }: Props) {
Linked
</Text>
) : null}
{provider.provisioned ? (
<Icon
icon={FaCode}
className="absolute top-[-15px] right-[-15px]"
color="orange"
size="sm"
tooltip="Provisioned"
/>
) : null}
<div className="flex flex-col gap-2">
<div>
<Title className="capitalize" title={provider.details?.name}>
Expand Down
1 change: 1 addition & 0 deletions keep-ui/app/providers/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export interface Provider {
tags: TProviderLabels[];
alertsDistribution?: AlertDistritbuionData[];
alertExample?: { [key: string]: string };
provisioned?: boolean;
}

export type Providers = Provider[];
Expand Down
1 change: 1 addition & 0 deletions keep-ui/app/workflows/models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export type Workflow = {
WorkflowExecution,
"execution_time" | "status" | "started"
>[];
provisioned?: boolean;
};

export type MockProvider = {
Expand Down
30 changes: 20 additions & 10 deletions keep-ui/app/workflows/workflow-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ interface WorkflowMenuProps {
onDownload?: () => void;
onBuilder?: () => void;
isRunButtonDisabled: boolean;
runButtonToolTip?: string;
runButtonToolTip?: string;
provisioned?: boolean;
}


Expand All @@ -24,6 +25,7 @@ export default function WorkflowMenu({
onBuilder,
isRunButtonDisabled,
runButtonToolTip,
provisioned,
}: WorkflowMenuProps) {
const stopPropagation = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
Expand Down Expand Up @@ -115,15 +117,23 @@ export default function WorkflowMenu({
</Menu.Item>
<Menu.Item>
{({ active }) => (
<button
onClick={(e) => { stopPropagation(e); onDelete?.(); }}
className={`${
active ? "bg-slate-200" : "text-gray-900"
} group flex w-full items-center rounded-md px-2 py-2 text-xs`}
>
<TrashIcon className="mr-2 h-4 w-4" aria-hidden="true" />
Delete
</button>
<div className="relative group">
<button
disabled={provisioned}
onClick={(e) => { stopPropagation(e); onDelete?.(); }}
className={`${
active ? 'bg-slate-200' : 'text-gray-900'
} flex w-full items-center rounded-md px-2 py-2 text-xs ${provisioned ? 'cursor-not-allowed opacity-50' : ''}`}
>
<TrashIcon className="mr-2 h-4 w-4" aria-hidden="true" />
Delete
</button>
{provisioned && (
<div className="absolute bottom-full transform -translate-x-1/2 bg-black text-white text-xs rounded px-4 py-1 z-10 opacity-0 group-hover:opacity-100">
Cannot delete a provisioned workflow
</div>
)}
</div>
)}
</Menu.Item>
</div>
Expand Down
Loading

0 comments on commit 7e9d06d

Please sign in to comment.