diff --git a/mission-control-chart b/mission-control-chart index 3710fad5..2a91080d 160000 --- a/mission-control-chart +++ b/mission-control-chart @@ -1 +1 @@ -Subproject commit 3710fad5bb8408aaf37129f0272d7aa4da1f3c77 +Subproject commit 2a91080def4f14679f38331c6b0fe05f610380cd diff --git a/mission-control/blog/state-based-alerting/index.md b/mission-control/blog/state-based-alerting/index.md index 57aca153..417a3028 100644 --- a/mission-control/blog/state-based-alerting/index.md +++ b/mission-control/blog/state-based-alerting/index.md @@ -1,37 +1,34 @@ --- draft: true --- -# State-Based Alerting: Understanding Why Kubernetes Deployments Fail +# State-Based Alerting: Understanding Why Kubernetes Deployments Fail ## Application vs Infrastructure -Application and infrastucture normally have very different failure scenarios, Application errors are normally due to bugs (that produce exceptions) or performance related problems. When there are problems it becomes immiedatly obvious - page fails to load or starts timing out. Infrastructure health is more often related to configuration errors, drift, permissions and unhealthy dependencies problems can lay latent and be influenced by drift and dependences. +Application and infrastucture normally have very different failure scenarios, Application errors are normally due to bugs (that produce exceptions) or performance related problems. When there are problems it becomes immiedatly obvious - page fails to load or starts timing out. Infrastructure health is more often related to configuration errors, drift, permissions and unhealthy dependencies problems can lay latent and be influenced by drift and dependences. -Common application health methodologies include **USE** (**u**tilization, **s**aturation,**e**rrors) and **RED** (**r**quests, **e**rrors, **d**uration) that primarily use metrics (and log/trace derived metrics) that define thresholds for known health states. It is fairly straightforard to define healthy, unhealthy and warning states. These methodoligies struggle with unknown states i.e. we are not receiving any traffic so we don't if there are any errors. Synthetic testing helps to surface problems by creating artificial transactions +Common application health methodologies include **USE** (**u**tilization, **s**aturation,**e**rrors) and **RED** (**r**quests, **e**rrors, **d**uration) that primarily use metrics (and log/trace derived metrics) that define thresholds for known health states. It is fairly straightforard to define healthy, unhealthy and warning states. These methodoligies struggle with unknown states i.e. we are not receiving any traffic so we don't if there are any errors. Synthetic testing helps to surface problems by creating artificial transactions ## Metric (Thresholds and Anomalies) - ## State Based ## Synthetic Testing - Infrastructure errors tend be more state oreinted ## Alerting Types and Examples There are various types of alerting methods, and choosing the right one can be challenging. -| Alerting Type | Example(s) | Use Cases | -| :---- | :---- | :---- | -| **Metrics (Threshold)** | \- CPU \> 90% for 5 minutes.
\- Available disk space \< 10GB. | Best for USE (**u**tilization, **s**aturation,**e**rrors) and known errors. | -| **Anomaly Detection** | \- Website traffic drops 50% compared to the same time last week.
\- Login attempts spike far beyond the normal range. | Useful for detecting unusual patterns and behavior that deviate from historical norms. Suitable for security monitoring and business metrics. | -| **Log-Based** | \- More than 10 "HTTP 500" errors in web server logs within 1 minute.
\- Any log containing `OutOfMemoryError`. | Ideal for error detection, security events, and application-specific issues that are captured in logs. Good for detailed troubleshooting context. | -| **State Based** | \- Kubernetes Node condition `Ready` \= False for 10 minutes.
\- Pod status is `CrashLoopBackOff`.
\- Deployment condition `Progressing = False` with reason: `ProgressDeadlineExceeded`. | Suitable for infrastructure and platform monitoring where resources have defined states. Good for Kubernetes and cloud resource health monitoring. | -| **Synthetic** | \- Simulated user login journey fails from an external testing location.
\- Critical API endpoint response time exceeds 2 seconds from an external check.
\- Website homepage fails to load correctly from an external probe. | Best for end-to-end monitoring and user experience validation. Ideal for critical business flows and external service dependency checks. | - +| Alerting Type | Example(s) | Use Cases | +| :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Metrics (Threshold)** | \- CPU \> 90% for 5 minutes.
\- Available disk space \< 10GB. | Best for USE (**u**tilization, **s**aturation,**e**rrors) and known errors. | +| **Anomaly Detection** | \- Website traffic drops 50% compared to the same time last week.
\- Login attempts spike far beyond the normal range. | Useful for detecting unusual patterns and behavior that deviate from historical norms. Suitable for security monitoring and business metrics. | +| **Log-Based** | \- More than 10 "HTTP 500" errors in web server logs within 1 minute.
\- Any log containing `OutOfMemoryError`. | Ideal for error detection, security events, and application-specific issues that are captured in logs. Good for detailed troubleshooting context. | +| **State Based** | \- Kubernetes Node condition `Ready` \= False for 10 minutes.
\- Pod status is `CrashLoopBackOff`.
\- Deployment condition `Progressing = False` with reason: `ProgressDeadlineExceeded`. | Suitable for infrastructure and platform monitoring where resources have defined states. Good for Kubernetes and cloud resource health monitoring. | +| **Synthetic** | \- Simulated user login journey fails from an external testing location.
\- Critical API endpoint response time exceeds 2 seconds from an external check.
\- Website homepage fails to load correctly from an external probe. | Best for end-to-end monitoring and user experience validation. Ideal for critical business flows and external service dependency checks. | This article compares Metric vs State Based alerts needed by platform teams managing infrastructure and deployments. @@ -51,7 +48,7 @@ labels: severity: warning annotations: summary: Kubernetes pod crash looping (instance {{ $labels.instance }}) - description: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is crash looping" + description: 'Pod {{ $labels.namespace }}/{{ $labels.pod }} is crash looping' ``` And would produce an alert similar to this: @@ -60,22 +57,19 @@ And would produce an alert similar to this: There are some drawbacks with this approach: -* **Limited Details** - The alert tells you **_what_** happened, but not **_why_**. -* **Limited Context** - You know the name of the pod and namespace, but not much else. If you want to restrict alerts to only pods labelled `env: production`, `kube-state-metrics` needs to be updated to whitelist the label. -* **Cardinality Challenges** - Whitelisting is required, as without it, you risk a cardinality explosion. Ingesting large amounts of metrics can be expensive and inefficient. -* **Configuration Overhead** - Each failure scenario requires configuration, first with the extraction of metrics and then by creating and fine-tuning alerts. +- **Limited Details** - The alert tells you **_what_** happened, but not **_why_**. +- **Limited Context** - You know the name of the pod and namespace, but not much else. If you want to restrict alerts to only pods labelled `env: production`, `kube-state-metrics` needs to be updated to whitelist the label. +- **Cardinality Challenges** - Whitelisting is required, as without it, you risk a cardinality explosion. Ingesting large amounts of metrics can be expensive and inefficient. +- **Configuration Overhead** - Each failure scenario requires configuration, first with the extraction of metrics and then by creating and fine-tuning alerts. These challenges are due to how TSDBs handle textual vs numerical data - the details and context you need is all in the text. - ## State-Based Alerting - The first step to "configuration-less" alerts is some standardization on what it means for something to be unhealthy. This is still an unsolved problem outside of Pod Probes. Kubernetes has taken some early steps with Conditions - which is an interface for reporting the state of a resource as either `unhealthy` or `healthy`. If you run the following command to get the YAML definition of a Pod: - apiVersion: v1 kind: Pod @@ -198,8 +192,6 @@ If you run the following command to get the YAML definition of a Pod:  startTime: "2025-03-26T10:17:16Z" - - While standards exist for exposing metrics, there's no equivalent standard for exposing the thresholds or conditions that trigger alerts. This leads to fragmentation and complexity in monitoring setups. [is-healthy](https://github.com/flanksource/is-healthy) is a tool designed to assess and report the health status of Kubernetes and other cloud resources (such as AWS) without the limitations of metric-based approaches. @@ -213,28 +205,27 @@ ready: false health: unhealthy status: ImagePullBackOff message: Back-off pulling image "nginx:invalid" -lastUpdated: "2025-03-26T10:17:18Z" +lastUpdated: '2025-03-26T10:17:18Z' ``` - This example output shows: -* **ready**: Whether the resource is reconciling or provisioning. Note: `ready` indicates if the resource's desired state matches its actual state, which is different from its health. A pod in a failure state can be `ready` if its state is stable (not changing). -* **health**: One of `healthy`, `unhealthy`, `warning`, `unknown`. This indicates the overall health assessment. -* **status**: A text description of the state of the resource, for example, `Running` or `ImagePullBackOff`. -* **message**: A reason providing more detail for the current status. -* **lastUpdated**: The timestamp when the resource was lastUpdated or reconciled. + +- **ready**: Whether the resource is reconciling or provisioning. Note: `ready` indicates if the resource's desired state matches its actual state, which is different from its health. A pod in a failure state can be `ready` if its state is stable (not changing). +- **health**: One of `healthy`, `unhealthy`, `warning`, `unknown`. This indicates the overall health assessment. +- **status**: A text description of the state of the resource, for example, `Running` or `ImagePullBackOff`. +- **message**: A reason providing more detail for the current status. +- **lastUpdated**: The timestamp when the resource was lastUpdated or reconciled. This is example isn't really thay useful, as it needs to be run continously, [canary-checker](https://canarychecker.io/) is a kubernetes health-check platform with support for 30+ check types, The [`kubernetes`](https://canarychecker.io/reference/kubernetes) check uses the `is-healthy` library: ```yaml title=kubernetes.yaml file=./canary.yaml + ``` This can be run locally: - - ## Step-by-Step Guide to State-Based Alerting for Deployments ### Understanding Deployment States @@ -298,8 +289,6 @@ And then checking on the status: updatedReplicas: 1 - - ### Setting Up State-Based Alerting with Mission Control Mission Control can monitor these states and alert when they indicate problems. Let's create a check to monitor deployment rollout status. @@ -315,38 +304,40 @@ spec: interval: 30 kubernetes: - name: check-deployment-rollout - description: "Monitor deployment rollout state" + description: 'Monitor deployment rollout state' resource: apiVersion: apps/v1 kind: Deployment name: nginx-deployment namespace: default results: - - name: Available - selector: $.status.conditions[?(@.type=="Available")].status - condition: Equal - error: "False" - - name: Progressing - selector: $.status.conditions[?(@.type=="Progressing")].status - condition: Equal - error: "False" - - name: ProgressingReason - selector: $.status.conditions[?(@.type=="Progressing")].reason - condition: Equal - error: "ProgressDeadlineExceeded" - - name: ErrorMessage - selector: $.status.conditions[?(@.type=="Progressing")].message - display: true + - name: Available + selector: $.status.conditions[?(@.type=="Available")].status + condition: Equal + error: 'False' + - name: Progressing + selector: $.status.conditions[?(@.type=="Progressing")].status + condition: Equal + error: 'False' + - name: ProgressingReason + selector: $.status.conditions[?(@.type=="Progressing")].reason + condition: Equal + error: 'ProgressDeadlineExceeded' + - name: ErrorMessage + selector: $.status.conditions[?(@.type=="Progressing")].message + display: true ``` + This Canary check: + 1. Runs every 30 seconds (`interval: 30`). 2. Targets the `Deployment` named `nginx-deployment` in the `default` namespace. 3. Defines results based on JSONPath selectors applied to the Deployment's status: - - Checks if the `Available` condition status is `False`. - - Checks if the `Progressing` condition status is `False`. - - Checks if the `Progressing` condition reason is `ProgressDeadlineExceeded`. - - Captures the `Progressing` condition message for display (`display: true`). - An alert is triggered if any condition marked with `error:` is met. + - Checks if the `Available` condition status is `False`. + - Checks if the `Progressing` condition status is `False`. + - Checks if the `Progressing` condition reason is `ProgressDeadlineExceeded`. + - Captures the `Progressing` condition message for display (`display: true`). + An alert is triggered if any condition marked with `error:` is met. Use `kubectl` to apply the Canary resource definition to your cluster: @@ -384,16 +375,17 @@ spec: app: failing-app spec: containers: - - name: container - image: nginx:latest - resources: - limits: - memory: "10Mi" # Intentionally too small - requests: - memory: "10Mi" - ports: - - containerPort: 80 + - name: container + image: nginx:latest + resources: + limits: + memory: '10Mi' # Intentionally too small + requests: + memory: '10Mi' + ports: + - containerPort: 80 ``` + This Deployment requests 3 replicas but sets a very low memory limit (`10Mi`), which is likely to cause Pods to be terminated with Out Of Memory (OOM) errors. Use `kubectl` to apply the failing Deployment definition to your cluster: @@ -413,18 +405,17 @@ Now, compare how different monitoring approaches handle this failure. With Prometheus, a common alert rule for deployment issues checks for generation mismatches: ```yaml title=KubernetesDeploymentGenerationMismatch - - alert: KubernetesDeploymentGenerationMismatch - expr: kube_deployment_status_observed_generation != kube_deployment_metadata_generation - for: 10m - labels: - severity: critical - annotations: - summary: Kubernetes Deployment generation mismatch (instance {{ $labels.instance }}) - description: "Deployment {{ $labels.namespace }}/{{ $labels.deployment }} has failed but has not been rolled back.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" +- alert: KubernetesDeploymentGenerationMismatch + expr: kube_deployment_status_observed_generation != kube_deployment_metadata_generation + for: 10m + labels: + severity: critical + annotations: + summary: Kubernetes Deployment generation mismatch (instance {{ $labels.instance }}) + description: "Deployment {{ $labels.namespace }}/{{ $labels.deployment }} has failed but has not been rolled back.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" ``` -This alert fires when there is a mismatch between the observed and expected generation numbers of a Kubernetes Deployment. The generation number increments whenever the Deployment spec changes. A mismatch indicates that the latest configuration change has not been successfully rolled out by the controller. While useful, it doesn't explain *why* the rollout failed. See [KubernetesDeploymentGenerationMismatch](https://samber.github.io/awesome-prometheus-alerts/rules#rule-kubernetes-1-24) for more details on this type of alert. - +This alert fires when there is a mismatch between the observed and expected generation numbers of a Kubernetes Deployment. The generation number increments whenever the Deployment spec changes. A mismatch indicates that the latest configuration change has not been successfully rolled out by the controller. While useful, it doesn't explain _why_ the rollout failed. See [KubernetesDeploymentGenerationMismatch](https://samber.github.io/awesome-prometheus-alerts/rules#rule-kubernetes-1-24) for more details on this type of alert. #### Mission Control State-Based Alert @@ -456,7 +447,9 @@ The output might resemble the following: } ] ``` + This output shows two conditions: + 1. `Available` is `False` because the deployment does not have the minimum required replicas ready (`MinimumReplicasUnavailable`). 2. `Progressing` is `False` because the rollout timed out (`ProgressDeadlineExceeded`). The message provides specific details about the failure, potentially including reasons like OOM killing if the system surfaces that information here. @@ -467,11 +460,13 @@ Mission Control captures this state and provides an alert with the error message ### When State-Based Alerting Works Best (and When It Doesn't) State-based alerting excels when: + - Resources self-report meaningful status - Problems have descriptive error messages - You need context for troubleshooting It's less effective when: + - Resources don't update status fields - You need to alert on trends over time - Complex conditions require correlation between multiple resources @@ -487,6 +482,7 @@ State changes can trigger multiple alerts. To avoid this: ### Combining with Metric-Based Monitoring The best approach is often a combination: + - Use state-based alerts for detailed diagnostics - Use metric-based alerts for performance issues and trends - Create correlation between the two for complete visibility @@ -498,6 +494,8 @@ State-based alerting transforms monitoring from "something is wrong" to "this is The ability to extract human-readable error messages directly from Kubernetes resources provides context that metrics alone cannot. As systems become more complex, this context becomes critical for effective incident management. For Kubernetes operators, combining state-based alerting with traditional metrics creates a complete view of your system's health and gives you the power to resolve issues faster. + ``` - [KubernetesDeploymentGenerationMismatch](https://samber.github.io/awesome-prometheus-alerts/rules#rule-kubernetes-1-24) +``` diff --git a/mission-control/docs/guide/config-db/concepts/relationships.md b/mission-control/docs/guide/config-db/concepts/relationships.md index 2c9805ef..c1519a24 100644 --- a/mission-control/docs/guide/config-db/concepts/relationships.md +++ b/mission-control/docs/guide/config-db/concepts/relationships.md @@ -53,31 +53,31 @@ You can see changes on the incoming relationships (and their parents) by choosin ## Relationship -| Field | Description | Scheme | Required | -| -------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | -------- | +| Field | Description | Scheme | Required | +| -------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | -------- | | `filter` | Which config items to form relationships with | CEL with [`ScrapeResult`](/docs/reference/config-db/scrape-result) | `true` | -| `id` | The ID or Alias (External ID) of the config to link to | [Lookup](#lookup) | | -| `name` | | [Lookup](#lookup) | | -| `type` | Config Type | [Lookup](#lookup) | | +| `id` | The ID or Alias (External ID) of the config to link to | [Lookup](#lookup) | | +| `name` | | [Lookup](#lookup) | | +| `type` | Config Type | [Lookup](#lookup) | | | `agent` | agent of the config to link to | [Lookup](#lookup) that returns an [Agent](/docs/reference/types#agent) | | -| `labels` | Labels of the config to link to | `map[string]`[Lookup](#lookup) | | +| `labels` | Labels of the config to link to | `map[string]`[Lookup](#lookup) | | ### Lookup RelationshipLookup offers different ways to specify a lookup value -| Field | Description | Scheme | Variables | -| ------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------- | +| Field | Description | Scheme | Variables | +| ------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | | `expr` | An expression that returns a value to be used | CEL with [`ScrapeResult`](/docs/reference/config-db/scrape-result) | [`ScrapeResult`](/docs/reference/config-db/scrape-result) | -| `value` | A static value to use in the lookup | `string` | | -| `label` | Get the value to use from an existing label | `label name` | | +| `value` | A static value to use in the lookup | `string` | | +| `label` | Get the value to use from an existing label | `label name` | | ## Dynamic Linking Sometimes the logic for when to a form a relationship is complex, a CEL expression can be used to return a list of selectors dynamically. -| Field | Description | Scheme | Variables | -| ------ | -------------------------------------------------------------------------------------- | ------------------------------------- | ---------------------------------------------------- | +| Field | Description | Scheme | Variables | +| ------ | ------------------------------------------------------------------------------------------- | ------------------------------------- | --------------------------------------------------------- | | `expr` | An expression that returns a list of [ResourceSelectors](/docs/reference/resource-selector) | CEL | [`ScrapeResult`](/docs/reference/config-db/scrape-result) | ```yaml title=link-pvc-to-pod.yaml diff --git a/mission-control/docs/guide/config-db/concepts/retention.md b/mission-control/docs/guide/config-db/concepts/retention.md index d123396e..a06ae8be 100644 --- a/mission-control/docs/guide/config-db/concepts/retention.md +++ b/mission-control/docs/guide/config-db/concepts/retention.md @@ -9,17 +9,17 @@ After scraping we can choose to retain results on the basis of name, age, count The retention rules are applied for each unique catalog item. If `changes` is specified with type `X` and count `20`, last 20 changes of `X` type would be kept for each catalog item -| Field | Description | Scheme | -| -------------- | ------------------------------------------------------------------ | ------------------------------------- | -| `types` | Specify retention rules for config items | [`[]ConfigItem`](#config-items) | -| `changes` | Specify retention rules for changes | [`[]Change`](#changes) | +| Field | Description | Scheme | +| -------------- | ------------------------------------------------------------------ | ------------------------------------------ | +| `types` | Specify retention rules for config items | [`[]ConfigItem`](#config-items) | +| `changes` | Specify retention rules for changes | [`[]Change`](#changes) | | `staleItemAge` | Config items that were last scraped after this age will be deleted | [Duration](/docs/reference/types#duration) | ## Config Items -| Field | Description | Scheme | -| ------------ | ----------------------------------------------------------------- | ------------------------------------- | -| `name` | Specify retention rules for changes | `string` | +| Field | Description | Scheme | +| ------------ | ----------------------------------------------------------------- | ------------------------------------------ | +| `name` | Specify retention rules for changes | `string` | | `createdAge` | Age after a config item is created it will be deleted | [Duration](/docs/reference/types#duration) | | `updatedAge` | Age after a config item last updated, that it will be deleted | [Duration](/docs/reference/types#duration) | | `deletedAge` | Age after a config item is soft deleted, will it be hard deleted. | [Duration](/docs/reference/types#duration) | @@ -63,11 +63,11 @@ spec: Changes can quickly accumulate and grow large over time. While it's important to retain some changes, others can be discarded after a period. -| Field | Description | Scheme | Required | -| ------- | ------------------------------------------------------- | ------------------------------------- | -------- | -| `name` | Name of the change type | `string` | `true` | +| Field | Description | Scheme | Required | +| ------- | ------------------------------------------------------- | ------------------------------------------ | -------- | +| `name` | Name of the change type | `string` | `true` | | `age` | Maximum age of the change type to retain (`12h`, `30d`) | [Duration](/docs/reference/types#duration) | | -| `count` | Maximum count to retain the change type | `int` | | +| `count` | Maximum count to retain the change type | `int` | | ```yaml title="kubernetes-scraper.yaml" apiVersion: configs.flanksource.com/v1 diff --git a/mission-control/docs/guide/config-db/concepts/transform.md b/mission-control/docs/guide/config-db/concepts/transform.md index 2fe62c27..299cf370 100644 --- a/mission-control/docs/guide/config-db/concepts/transform.md +++ b/mission-control/docs/guide/config-db/concepts/transform.md @@ -11,14 +11,14 @@ Transformations allow you to modify scraped config items before they are saved, - Masking sensitive data - Excluding duplicate changes or changes with a high rate -| Field | Description | Scheme | -| --------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ | -| `transform.exclude` | Remove fields from a scraped `config` | [[]Exclude](#field-exclusions) | -| `transform.mask` | Replace sensitive fields with a hash to enable change detection on secrets | [[]Mask](#masking) | +| Field | Description | Scheme | +| --------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| `transform.exclude` | Remove fields from a scraped `config` | [[]Exclude](#field-exclusions) | +| `transform.mask` | Replace sensitive fields with a hash to enable change detection on secrets | [[]Mask](#masking) | | `transform.changes.exclude` | Ignore changes | [[]CEL](#exclusions) with [Change Context](/docs/reference/config-db/changes) | -| `transform.changes.mapping` | Categorize changes | [Mapping](#mapping) | +| `transform.changes.mapping` | Categorize changes | [Mapping](#mapping) | | `transform.expr` | | [CEL](/docs/reference/scripting/cel) | -| `transform.relationship` | Create relationships between items | [Relationships](./relationships) | +| `transform.relationship` | Create relationships between items | [Relationships](./relationships) | ## Config Items @@ -76,11 +76,11 @@ spec: - fixtures/data/single-config.json ``` -| Field | Description | Scheme | -| ---------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| Field | Description | Scheme | +| ---------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | `selector` | Filter which config items to apply masks on | `CEL` with [ScrapeResult](/docs/reference/config-db/scrape-result) context | -| `jsonpath` | Values to mask | `JSONPath` | -| `value` | The replacement value of matched elements | `md5` or any static string e.g. `***` | +| `jsonpath` | Values to mask | `JSONPath` | +| `value` | The replacement value of matched elements | `md5` or any static string e.g. `***` | :::info Masks are applied in the order they are specified in the configuration file. @@ -138,19 +138,19 @@ spec: //highlight-end ``` -| Field | Description | Scheme | -| --------- | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- | +| Field | Description | Scheme | +| --------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | | `filter` | Selects changes to apply the mapping | CEL with [Change Context](/docs/reference/config-db/changes) | -| `action` | What action to take on the change, if `delete` then the corresponding config item is marked as deleted | `delete` or `ignore` | -| `type` | New change type | `string` | +| `action` | What action to take on the change, if `delete` then the corresponding config item is marked as deleted | `delete` or `ignore` | +| `type` | New change type | `string` | | `summary` | New summary of the change | Go Template with [Change Context](/docs/reference/config-db/changes) | ## Scripting Scripting modifies the scraped configuration using CEL before saving it to the database. This process is beneficial for data normalization, default value population, and sensitive field masking. -| Field | Description | Scheme | Context | -| ------ | ----------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| Field | Description | Scheme | Context | +| ------ | ----------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | | `expr` | Transform a config item | CEL that returns [[]ScrapeResult](/docs/reference/config-db/scrape-result) | `config` `JSON`
`result` [Scrape Result](/docs/reference/config-db/scrape-result) | ```yaml title="file-scraper.yaml" diff --git a/mission-control/docs/guide/config-db/scrapers/azure.md b/mission-control/docs/guide/config-db/scrapers/azure.md index 404351a5..bbf2ca6d 100644 --- a/mission-control/docs/guide/config-db/scrapers/azure.md +++ b/mission-control/docs/guide/config-db/scrapers/azure.md @@ -17,27 +17,27 @@ The Azure scrapers scrapes your azure account to fetch all the resources & save ## Scraper -| Field | Description | Scheme | Required | -| ----------- | ---------------------------------------------------------------------------- | -------------------------------------------------- | -------- | -| `logLevel` | Specify the level of logging. | `string` | | -| `schedule` | Specify the interval to scrape in cron format. Defaults to every 60 minutes. | `string` | | +| Field | Description | Scheme | Required | +| ----------- | ---------------------------------------------------------------------------- | ------------------------------------------------------- | -------- | +| `logLevel` | Specify the level of logging. | `string` | | +| `schedule` | Specify the interval to scrape in cron format. Defaults to every 60 minutes. | `string` | | | `retention` | Settings for retaining changes, analysis and scraped items | [`Retention`](/docs/guide/config-db/concepts/retention) | | -| `azure` | Azure scrape config | [`[]Azure`](#azure) | | +| `azure` | Azure scrape config | [`[]Azure`](#azure) | | ### Azure -| Field | Description | Scheme | Required | -| ---------------- | -------------------------------------------------------------------------- | ----------------------------------------------------- | -------- | -| `connection` | Specify the connection that provides the clientID, clientSecret & tenantID | `string` | | -| `subscriptionID` | Azure subscription ID | `string` | | -| `organisation` | Azure organisation ID | `string` | | -| `tenantID` | Azure tenant ID | `string` | | -| `clientID` | Microsoft Entra ID app client id | _EnvVar_ | | -| `clientSecret` | Microsoft Entra ID app client secret | _EnvVar_ | | -| `exclusions` | Specifies the Azure projects to scrape | [`Exclusion`](#exclusion) | | +| Field | Description | Scheme | Required | +| ---------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------- | -------- | +| `connection` | Specify the connection that provides the clientID, clientSecret & tenantID | `string` | | +| `subscriptionID` | Azure subscription ID | `string` | | +| `organisation` | Azure organisation ID | `string` | | +| `tenantID` | Azure tenant ID | `string` | | +| `clientID` | Microsoft Entra ID app client id | _EnvVar_ | | +| `clientSecret` | Microsoft Entra ID app client secret | _EnvVar_ | | +| `exclusions` | Specifies the Azure projects to scrape | [`Exclusion`](#exclusion) | | | `properties` | Custom templatable properties for the scraped config items. | [`[]ConfigProperty`](/docs/reference/config-db/properties) | | | `transform` | Field to transform result | [`Transform`](/docs/guide/config-db/concepts/transform) | | -| `tags` | Set custom tags on the scraped config items | `map[string]string` | | +| `tags` | Set custom tags on the scraped config items | `map[string]string` | | :::note Either the `connection` name or the credentials (`clientID`, `clientSecret` & `tenantID`) are required diff --git a/mission-control/docs/guide/playbooks/_api.md b/mission-control/docs/guide/playbooks/_api.md index 6708403e..3322089f 100644 --- a/mission-control/docs/guide/playbooks/_api.md +++ b/mission-control/docs/guide/playbooks/_api.md @@ -55,10 +55,10 @@ POST `/playbook/list?check_id={check_id}` ### Query Parameters -| Parameter | Description | Type | -| -------------- | ------------------------ | -------- | -| `config_id` | ID of the config item | `string` | -| `check_id` | ID of the Check | `string` | +| Parameter | Description | Type | +| ----------- | --------------------- | -------- | +| `config_id` | ID of the config item | `string` | +| `check_id` | ID of the Check | `string` | > Provide only 1 parameter @@ -123,18 +123,18 @@ GET /playbook/run/{id} ### Response -| Field | Type | Description | -| -------------- | ------------------- | ----------------------------------------------------- | -| `id` | `string` | The ID of the run. | -| `playbook_id` | `string` | The ID of the playbook. | -| `status` | `string` | The status of the run. | -| `created_at` | `string` | The time the run is scheduled to start (_in RFC3339_) | -| `start_time` | `string` | The time the run started (_in RFC3339_) | -| `end_time` | `string` | The time the run ended (_in RFC3339_) | -| `created_by` | `string` | The ID of the user that created the run. | -| `config_id` | `string` | The ID of the config item that ran the playbook. | -| `parameters` | `map[string]string` | The parameters to pass to the playbook. | -| `agent_id` | `string` | The ID of the agent that ran the playbook. | +| Field | Type | Description | +| ------------- | ------------------- | ----------------------------------------------------- | +| `id` | `string` | The ID of the run. | +| `playbook_id` | `string` | The ID of the playbook. | +| `status` | `string` | The status of the run. | +| `created_at` | `string` | The time the run is scheduled to start (_in RFC3339_) | +| `start_time` | `string` | The time the run started (_in RFC3339_) | +| `end_time` | `string` | The time the run ended (_in RFC3339_) | +| `created_by` | `string` | The ID of the user that created the run. | +| `config_id` | `string` | The ID of the config item that ran the playbook. | +| `parameters` | `map[string]string` | The parameters to pass to the playbook. | +| `agent_id` | `string` | The ID of the agent that ran the playbook. | ## Accessing Artifacts diff --git a/mission-control/docs/guide/playbooks/actions/index.md b/mission-control/docs/guide/playbooks/actions/index.md index f9dbef1a..4cfdaa36 100644 --- a/mission-control/docs/guide/playbooks/actions/index.md +++ b/mission-control/docs/guide/playbooks/actions/index.md @@ -7,21 +7,21 @@ sidebar_position: 3 Actions are the fundamental tasks executed by a playbook. A playbook can comprise multiple actions, which are executed sequentially. If any action encounters an error and fails, the execution of the playbook is halted. -| Field | Description | Scheme | Required | -| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------- | -------- | -| `name` | Name of action. | `string` | `true` | +| Field | Description | Scheme | Required | +| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | -------- | +| `name` | Name of action. | `string` | `true` | | `runsOn` | Specify the [runners](/docs/guide/playbooks/concepts/runners) that can run this action. One will be chosen on random. When empty, the playbook will run on the main instance itself | `[]string` | | -| `templatesOn` | Specify where the templating of the action spec should occur | `host` or `agent` | | -| `delay` | A delay before running the action e.g. `8h` | `Duration` or [`Expression`](../concepts/expression) | | -| `filter` | Whether to run the step or not | [`Expression`](../concepts/expression) | | -| `timeout` | Timeout on this action. | `Duration` | | -| `azureDevopsPipeline` | | [AzureDevops](./azure_devops_pipeline) | | -| `exec` | Specify exec of action. | [Exec](./exec) | | -| `gitops` | Specify gitops of action. | [Gitops](./gitops) | | -| `http` | Specify http of action. | [Http](./http) | | -| `sql` | Specify sql of action. | [Sql`](./sql) | | -| `pod` | Specify pod of action. | [Pod](./pod) | | -| `notification` | Specify notification of action. | [Notification](./notification) | | +| `templatesOn` | Specify where the templating of the action spec should occur | `host` or `agent` | | +| `delay` | A delay before running the action e.g. `8h` | `Duration` or [`Expression`](../concepts/expression) | | +| `filter` | Whether to run the step or not | [`Expression`](../concepts/expression) | | +| `timeout` | Timeout on this action. | `Duration` | | +| `azureDevopsPipeline` | | [AzureDevops](./azure_devops_pipeline) | | +| `exec` | Specify exec of action. | [Exec](./exec) | | +| `gitops` | Specify gitops of action. | [Gitops](./gitops) | | +| `http` | Specify http of action. | [Http](./http) | | +| `sql` | Specify sql of action. | [Sql`](./sql) | | +| `pod` | Specify pod of action. | [Pod](./pod) | | +| `notification` | Specify notification of action. | [Notification](./notification) | | :::note Specify one or more actions; but at least one. @@ -77,10 +77,10 @@ getAction('action_name').result.stdout.JSON().count < 5 Templates receive a context variable that contain details about the config or component it is running for. In addition, it also contains the optional `params` variable which contains the parameters passed to the playbook. -| Field | Description | Schema | -| ------------- | ---------------------------------------- | --------------------------------------------- | -| `.config` | Config passed to the playbook | [`ConfigItem`](/docs/reference/config-db) | -| `.check` | Canary Check passed to the playbook | [`Check`](/docs/reference/canary-checker/check) | -| `.params` | User provided parameters to the playbook | `map[string]string` | -| `.user.name` | Name of the user who invoked the action | `string` | -| `.user.email` | Email of the user who invoked the action | `string` | +| Field | Description | Schema | +| ------------- | ---------------------------------------- | ----------------------------------------------- | +| `.config` | Config passed to the playbook | [`ConfigItem`](/docs/reference/config-db) | +| `.check` | Canary Check passed to the playbook | [`Check`](/docs/reference/canary-checker/check) | +| `.params` | User provided parameters to the playbook | `map[string]string` | +| `.user.name` | Name of the user who invoked the action | `string` | +| `.user.email` | Email of the user who invoked the action | `string` | diff --git a/mission-control/docs/guide/views/concepts/cache-control.md b/mission-control/docs/guide/views/concepts/cache-control.md index 3edc7ba6..0f0499c6 100644 --- a/mission-control/docs/guide/views/concepts/cache-control.md +++ b/mission-control/docs/guide/views/concepts/cache-control.md @@ -1,31 +1,23 @@ --- title: Cache Control -sidebar_position: 4 sidebar_custom_props: - icon: material-symbols:cached + icon: mdi:cached --- -Views implement a caching system that stores query results in dedicated database tables for fast retrieval and extra filtering. -You can control cache behavior through both view configuration and HTTP headers. +Views implement intelligent caching to minimize unnecessary data refreshes while keeping data reasonably fresh. Cache control settings help you balance data freshness with performance. -## How View Caching Works +## Overview -Views work like materialized views in traditional databases: +When a view is requested, the system follows this process: -1. User requests view data -2. System looks for cached data in the view's database table -3. If data exists and hasn't expired, return immediately - - **Cache Miss/Expired**: Execute view queries and wait up to `refreshTimeout` -4. **Timeout Handling**: If refresh exceeds timeout: - - Return stale data if available - - Return error if no cached data exists -5. **Hard Timeout Limit**: Refresh operations have a hard limit of 1 minute regardless of configuration +1. **Check cache validity** - Is the cached data still fresh? +2. **Return or refresh** - If fresh, return cached data; if stale, trigger a refresh +3. **Handle refresh timeout** - If refresh takes too long, return stale data instead +4. **Store results** - New data is cached for subsequent requests -## Cache Configuration +## Configuration -### View-Level Settings - -Configure cache behavior in your view specification: +Cache is configured in the view's `spec` section: ```yaml apiVersion: mission-control.flanksource.com/v1 @@ -34,21 +26,236 @@ metadata: name: my-view spec: cache: - maxAge: '30m' # Maximum cache age before refresh (default: 15m) - minAge: '30s' # Minimum age clients can request (default: 10s) - refreshTimeout: '10s' # How long to wait for refresh (default: 5s) - queries: - # ... your queries + # Maximum age before cache is considered stale (default: 15m) + maxAge: 15m + + # Minimum age a user can request refresh (default: 10s) + # Prevents over-refreshing from rapid user requests + minAge: 10s + + # Timeout for refresh operations (default: 5s) + # If refresh takes longer, stale data is returned instead + refreshTimeout: 5s +``` + +## Cache Parameters + +### maxAge + +The maximum age of cached data before it's considered stale and needs refresh. + +- **Default**: `15m` +- **Format**: Go duration (e.g., `5m`, `1h`, `30s`) +- **Effect**: Determines how fresh your data is + - Shorter duration: More frequent refreshes, fresher data, higher load + - Longer duration: Less frequent refreshes, potentially stale data, lower load + +**Example**: + +```yaml +cache: + maxAge: 5m # Refresh at least every 5 minutes +``` + +### minAge + +The minimum age cached data must reach before a user can request a manual refresh. This prevents over-refreshing from rapid user clicks. + +- **Default**: `10s` +- **Format**: Go duration +- **Effect**: Throttles refresh requests from users + - Users cannot force a refresh faster than this interval + - Helps prevent accidental load spikes from aggressive clicking + +**Example**: + +```yaml +cache: + minAge: 30s # Users can only refresh every 30 seconds +``` + +### refreshTimeout + +How long to wait for a refresh operation to complete. If the refresh takes longer, stale data is returned instead of waiting. + +- **Default**: `5s` +- **Format**: Go duration +- **Effect**: Ensures response times don't degrade when refresh is slow + - Shorter timeout: Faster responses, but may return stale data + - Longer timeout: Fresher data, but slower responses if refresh is slow + +**Example**: + +```yaml +cache: + refreshTimeout: 2s # Return stale data if refresh takes > 2 seconds +``` + +## Per-Variable Caching + +Each unique combination of template variables creates a separate cache entry. This allows different filters to maintain independent cache lifespans. + +**Example**: A view with a "cluster" variable will maintain separate cached data for: + +- cluster=us-east-1 +- cluster=us-west-2 +- cluster=eu-central-1 + +Each cluster's data is cached independently, allowing users to switch between clusters without invalidating other cached results. + +## Cache Behavior Examples + +### Example 1: Fresh Data Request + +``` +User requests view + ↓ +Cache check: Data is 5 minutes old, maxAge=15m + ↓ +Cache is valid → Return cached data immediately + ↓ +Response time: ~10ms +``` + +### Example 2: Stale Data with Quick Refresh + +``` +User requests view + ↓ +Cache check: Data is 20 minutes old, maxAge=15m + ↓ +Cache is stale → Trigger refresh with 2s timeout + ↓ +Refresh completes in 1.5 seconds + ↓ +Return fresh data → Cache updated + ↓ +Response time: ~1.5s +``` + +### Example 3: Stale Data with Slow Refresh + +``` +User requests view + ↓ +Cache check: Data is 20 minutes old, maxAge=15m + ↓ +Cache is stale → Trigger refresh with 2s timeout + ↓ +Refresh takes 4 seconds (slower than timeout) + ↓ +Timeout triggers after 2 seconds + ↓ +Return stale cached data instead of waiting + ↓ +Response time: ~2s + ↓ +Refresh continues in background, updates cache +``` + +### Example 4: Manual Refresh Throttling + +``` +User clicks refresh button + ↓ +Check minAge: Last refresh was 2 seconds ago, minAge=10s + ↓ +Too soon → Ignore refresh request + ↓ +User must wait 8 more seconds before refresh is allowed ``` -### Default Values +## Recommended Settings + +### High-Frequency Monitoring (Every Minute) + +Use shorter cache times for real-time dashboards: + +```yaml +cache: + maxAge: 1m + minAge: 10s + refreshTimeout: 5s +``` + +### Typical Dashboard (Every 5-15 Minutes) + +Standard balance of freshness and performance: + +```yaml +cache: + maxAge: 15m + minAge: 30s + refreshTimeout: 5s +``` + +### Low-Frequency Reference Data (Hourly) + +For relatively static data: + +```yaml +cache: + maxAge: 1h + minAge: 1m + refreshTimeout: 10s +``` + +### Real-Time Critical (Seconds) + +For mission-critical live data: + +```yaml +cache: + maxAge: 30s + minAge: 5s + refreshTimeout: 2s +``` + +## Performance Impact + +Cache settings directly affect system performance: + +| Setting | ↓ Latency | ↓ Load | ↓ Freshness | +| ---------------------- | --------- | ------ | ----------- | +| Shorter maxAge | ✗ | ✗ | ✓ | +| Longer maxAge | ✓ | ✓ | ✗ | +| Shorter refreshTimeout | ✓ | ✗ | ✗ | +| Longer refreshTimeout | ✗ | ✓ | ✓ | +| Higher minAge | ✓ | ✓ | ✗ | +| Lower minAge | ✗ | ✗ | ✓ | + +## Default Configuration + +If you don't specify cache settings, Mission Control uses these defaults: + +```yaml +cache: + maxAge: 15m # Refresh at least every 15 minutes + minAge: 10s # Throttle user refreshes to every 10 seconds + refreshTimeout: 5s # Return stale data if refresh takes > 5 seconds +``` + +These defaults work well for most use cases and provide a good balance between data freshness and system performance. + +## Cache Deduplication + +Mission Control uses **singleflight deduplication** to prevent multiple concurrent refresh requests for the same view and variable combination. This means: + +- If 10 users request the same view at the same time +- Only one database query is executed +- All 10 users receive the same cached result +- Significantly reduces database load + +## Cache Storage + +View cache data is stored in: + +- **Primary storage**: PostgreSQL database (persistent) +- **Format**: `view__` table per view +- **Tracking**: Request fingerprint (SHA256 hash of variable map) identifies unique variable combinations -| Setting | Default | Description | -| ---------------- | ---------- | ------------------------------------------ | -| `maxAge` | 15 minutes | Cache expires after this duration | -| `minAge` | 10 seconds | Minimum cache age that clients can request | -| `refreshTimeout` | 5 seconds | Timeout for background refresh operations | +This allows: -:::info Hard Timeout Limit -Regardless of the configured `refreshTimeout`, view refresh operations have a hard limit of **1 minute**. This prevents long-running queries from blocking the system. -::: +- Data to survive service restarts +- PostgREST API access to view data +- Historical tracking of different variable combinations diff --git a/mission-control/docs/guide/views/concepts/panels.md b/mission-control/docs/guide/views/concepts/panels.md deleted file mode 100644 index c4d11d78..00000000 --- a/mission-control/docs/guide/views/concepts/panels.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Panels -sidebar_position: 3 -sidebar_custom_props: - icon: material-symbols:dashboard ---- - -Views support different panel types for visualizing aggregated data. -Panels create interactive visualizations from your data sources and you can configure them with various display options. - -## Panel Types - -Views support the following panel types: - -- `piechart` - Pie chart visualizations for distribution analysis -- `table` - Tabular data presentations -- `number` - Single metric displays with units -- `gauge` - Gauge visualizations with thresholds - -## Basic Panel Structure - -All panels share a common structure. Here's an example from the gauge panel fixture: - -```yaml title="gauge.yaml" file=/modules/mission-control/fixtures/views/panels/gauge.yaml - -``` - -## Panel Configuration - -### Pie Chart - -Pie charts show data distribution across categories: - - - -```yaml title="piechart.yaml" file=/modules/mission-control/fixtures/views/panels/piechart.yaml - -``` - -#### Pie Chart Properties - -- `showLabels`: Display labels on pie slices -- `colors`: Custom color mapping for categories - -### Gauge - -Gauges display metrics with threshold-based color coding: - -```yaml title="gauge.yaml" file=/modules/mission-control/fixtures/views/panels/gauge.yaml - -``` - -#### Gauge Properties - -- `min`: Minimum value for the gauge -- `max`: Maximum value for the gauge -- `thresholds`: Array of threshold objects with `value` and `color` - -#### Supported Threshold Colors - -- `green` - Success/healthy state -- `yellow` - Warning state -- `red` - Critical/error state -- `blue` - Info state -- Custom hex colors (e.g., `#28C19B`) - -### Number - -Number panels display single metrics with units: - - - -```yaml title="number.yaml" file=/modules/mission-control/fixtures/views/panels/number.yaml - -``` - -#### Number Properties - -- `unit`: Display unit (e.g., "seconds", "MB", "%") -- `precision`: Number of decimal places - -### Table - -Tables display aggregated data in rows and columns: - - - -```yaml title="table.yaml" file=/modules/mission-control/fixtures/views/panels/table.yaml - -``` - -## Panel Queries - -Panels use SQL queries to aggregate data from the view's named queries. Each panel executes its SQL query against the data sources defined in the view's `queries` section. - -### Query Structure - -Panels reference query names as tables in SQL. Here's how the gauge example works: - -```yaml title="gauge.yaml" file=/modules/mission-control/fixtures/views/panels/gauge.yaml - -``` diff --git a/mission-control/docs/guide/views/concepts/queries.md b/mission-control/docs/guide/views/concepts/queries.md deleted file mode 100644 index 3b69f1d7..00000000 --- a/mission-control/docs/guide/views/concepts/queries.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: Queries -sidebar_position: 1 -sidebar_custom_props: - icon: material-symbols:query-stats ---- - -Views use a powerful query system to aggregate and transform data from multiple sources. You can query config items, changes, and metrics to populate your views. - -## Query Types - -### Config Queries - -Query config items from your catalog: - -```yaml title="deployments.yaml" file=/modules/mission-control/fixtures/views/deployments.yaml {54-60} - -``` - -### Change Queries - -Query configuration changes and audit data: - -```yaml title="backups.yaml" file=/modules/mission-control/fixtures/views/backups.yaml {29-33} - -``` - -### Metric Queries - -Query time-series data from Prometheus and other sources. Metric queries are not yet implemented in the current version, but the structure would be: - -```yaml title="resource-usage.yaml" file=/modules/mission-control/fixtures/views/panels/resource-usage.yaml {7-15} - -``` - -## Multi-Source Queries - -Views can combine data from different sources and use SQL queries in panels to aggregate and analyze the data: - -```yaml title="pipelines.yaml" file=/modules/mission-control/fixtures/views/pipelines.yaml - -``` - -This example shows: - -- **Multiple query sources**: Combining workflow runs from changes -- **Panel queries**: Using SQL to aggregate repository data -- **CEL expressions**: Complex duration calculations in mappings -- **Data aggregation**: `COUNT` (count) and `AVG` (average) functions in panel queries diff --git a/mission-control/docs/guide/views/concepts/tables.md b/mission-control/docs/guide/views/concepts/tables.md deleted file mode 100644 index cb5afbd6..00000000 --- a/mission-control/docs/guide/views/concepts/tables.md +++ /dev/null @@ -1,301 +0,0 @@ ---- -title: Tables -sidebar_position: 2 -sidebar_custom_props: - icon: material-symbols:table-chart ---- - -A view can generate a single table. The table supports a rich set of column types that decide how data persists, displays, and formats in your dashboards. -Each column type provides specific formatting and visualization capabilities. - -## Columns - -Columns define the structure and data types of your view table. Each column has a name and type that determines how the data stores, displays, and formats in your dashboards. - -:::info -Column names must be valid SQL identifiers as they map directly to table columns in the underlying database. -::: - -### Column Properties - -Each column definition supports several properties: - -- `name`: Column name (must be a valid SQL identifier) -- `type`: Data type that determines formatting and visualization -- `primaryKey`: Whether this column is part of the primary key (default: false) -- `description`: Human-readable description of the column -- `hidden`: Whether to hide the column from the UI (default: false) -- `for`: Reference to another column this column provides data for (helper columns) -- `gauge`: Configuration for gauge visualization (only for `gauge` type) - -### Column Types - -#### `string` - -Text data with no extra formatting. - -```yaml -columns: - - name: application_name - type: string - description: The name of the application -``` - -#### `number` - -Numeric values with automatic formatting. - -```yaml -columns: - - name: replica_count - type: number -``` - -#### `boolean` - -True/false values displayed as checkmarks or status indicators. - -```yaml -columns: - - name: is_active - type: boolean -``` - -#### `datetime` - -Timestamp values with automatic date/time formatting. - -```yaml -columns: - - name: last_deployed - type: datetime -``` - -#### `duration` - -Time duration values with human-readable formatting (e.g., "2h 30m"). - -```yaml -columns: - - name: uptime - type: duration -``` - -#### `health` - -Health status indicators with color-coded visual representation. - -```yaml -columns: - - name: component_health - type: health -``` - -#### `status` - -General status information with visual indicators. - -```yaml -columns: - - name: deployment_status - type: status -``` - -#### `decimal` - -Decimal numbers with configurable precision. - -```yaml -columns: - - name: cpu_usage - type: decimal -``` - -#### `bytes` - -Byte values with automatic unit conversion (B, KB, MB, GB). - -```yaml -columns: - - name: memory_usage - type: bytes -``` - -#### `millicore` - -CPU `millicore` values with proper formatting. - -```yaml -columns: - - name: cpu_request - type: millicore -``` - -#### `gauge` - -Rich gauge visualizations with thresholds and color coding. - -```yaml -columns: - - name: disk_usage - type: gauge - gauge: - min: 0 - max: 100 - thresholds: - - value: 70 - color: 'yellow' - - value: 85 - color: 'orange' - - value: 95 - color: 'red' -``` - -:::note Gauge Configuration Required -When using `type: gauge`, the `gauge` configuration block is **required**. -::: - -##### Gauge Properties - -- `min`: Minimum value for the gauge -- `max`: Maximum value for the gauge -- `thresholds`: Array of threshold objects with `value` and `color` - -#### `url` - -URL values that render as clickable links. - -```yaml -columns: - - name: dashboard_link - type: url -``` - -### Helper Columns - -Helper columns are extra columns that give data for other columns without rendering directly in the UI. They use the `for` field to reference the column they support. - -The most common use case is providing URLs for other columns. When a column has a helper column of type `url`, the main column becomes clickable and links to the URL provided by the helper column. - -```yaml -columns: - - name: component_name - type: string - primaryKey: true - - name: component_url - type: url - for: component_name # This URL will make component_name clickable -``` - -In this example: - -- `component_name` displays as a regular string column -- `component_url` provides the URL data but doesn't render as a separate column -- When rendered, `component_name` becomes a clickable link using the URL from `component_url` - -### Hidden Columns - -You can mark columns as `hidden: true` to prevent them from appearing as separate columns in the table while still being available for internal operations. Primary keys like `UUIDs` are good candidates to hide since they're needed for data operations but not useful for display. - -```yaml -columns: - - name: component_name - type: string - - name: component_id - type: string - primaryKey: true - hidden: true - description: Internal UUID for the component -``` - -### Primary Keys - -Each view table must specify at least one column as the primary key. Primary keys support internal purposes including data `deduplication` and table operations. You can have multiple primary keys to create composite primary keys. - -#### Single Primary Key - -```yaml -columns: - - name: pod_name - type: string - primaryKey: true - - name: namespace - type: string - - name: status - type: string -``` - -#### Composite Primary Keys - -When you need multiple columns to uniquely identify a row: - -```yaml -columns: - - name: pod_name - type: string - primaryKey: true # First part of composite key - - name: namespace - type: string - primaryKey: true # Second part of composite key - - name: status - type: string -``` - -## Data Mapping - -Data mapping defines how data from your query results gets transformed and assigned to specific table columns. - -:::info -Mapping is **optional** - if not specified, Mission Control automatically looks for column names directly in the query results. -::: - -### How Data Mapping Works - -When Mission Control executes your queries, it returns raw data objects. For each column, the system: - -1. **Checks for explicit mapping** → If a CEL expression defines the column -2. **Falls back to direct lookup** → Looks for the column name directly in the query results -3. **Defaults to null** → If neither mapping nor direct column name exists - -The mapping section uses [CEL (Common Expression Language)](/docs/reference/scripting/cel) expressions where the query result data is available as `row`. - -### Mapping Behavior - -#### Automatic Column Mapping - -If you specify no mapping, columns are automatically populated from query results: - -```yaml -columns: - - name: name - type: string - primaryKey: true - - name: status - type: string - - name: created_at - type: datetime -# No mapping section needed - columns will be populated from query results automatically -``` - -#### Explicit Mapping - -When you need data transformation or the column names don't match query results: - -```yaml -mapping: - name: row.component_name # Map from different field name - status: row.health # Map from different field name - created_at: row.created_at # Direct mapping (same as automatic) -``` - -### CEL Expression Context - -In mapping expressions, query result data is available as `row`: - -```yaml -mapping: - database: row.name # Access top-level fields - date: row.created_at # Direct field access - status: row.details.status # Navigate nested objects - source: row.source # Simple field mapping -``` diff --git a/mission-control/docs/guide/views/concepts/templating.md b/mission-control/docs/guide/views/concepts/templating.md new file mode 100644 index 00000000..016c168b --- /dev/null +++ b/mission-control/docs/guide/views/concepts/templating.md @@ -0,0 +1,466 @@ +--- +title: Variables +sidebar_custom_props: + icon: mdi:variable +--- + +Variables make views interactive by allowing users to dynamically control which data is fetched. Variables are defined in the `templating` section and their values are substituted into queries using the `$(var.)` syntax. + +## Overview + +Variables in Mission Control allow you to: + +- Create interactive dropdowns users can adjust +- Populate options from static lists or dynamic sources +- Build dependent variables (e.g., filter namespaces by cluster) +- Use variable values in queries to control what data is fetched +- Maintain separate cache entries per variable combination + +## Variables vs Column Filters + +Mission Control has two ways to filter view data. Both are server-side, but they operate at different stages: + +``` +┌─────────────────────────────────────┐ +│ Sources │ +│ (configs, prometheus, checks, etc) │ +└──────────────────┬──────────────────┘ + │ + ▼ + ┌──────────────────┐ + │ Execute Queries │ ◀── Variables filter here + └────────┬─────────┘ (only matching data is fetched) + │ + ▼ + ┌────────────────────┐ + │ In-memory SQLite │ + │ (joins/merge) │ + └──────────┬─────────┘ + │ + ▼ +┌─────────────────────────────────────┐ +│ PostgreSQL Table │ +│ (view__) │ +└──────────────────┬──────────────────┘ + │ + ▼ + ┌────────────────┐ + │ PostgREST API │ ◀── Column Filters add WHERE clauses here + └───────┬────────┘ + │ + ▼ + ┌──────────┐ + │ UI │ + └──────────┘ +``` + +| | Variables | Column Filters | +| -------------------------- | ---------------------------------------------------------- | -------------------------------------------------------- | +| **Defined in** | `spec.templating` | `spec.columns[].filter` | +| **When filtering happens** | At source query time | At display time (PostgreSQL query) | +| **Effect** | Only matching data is fetched and stored in the view table | All data is in PostgreSQL; UI queries with WHERE clauses | +| **Use case** | Scoping large datasets (e.g., select a cluster/namespace) | Narrowing down within already-materialized results | +| **Cache impact** | Each variable combination has its own cache | No cache impact | + +**Example**: If you have 10,000 pods across 5 clusters: + +- **Variable** with `cluster` selection → Only pods from that cluster are fetched from the source and stored (~2,000 rows in PostgreSQL) +- **Column filter** on `cluster` → All 10,000 pods are stored in PostgreSQL; user's filter selection adds a WHERE clause to the query + +Use **variables** when you need to scope down large datasets at the source. Use **column filters** when you want users to explore within already-reasonable result sets. + +## Basic Variable Structure + +```yaml +templating: + # Simple static values variable + - key: environment + label: 'Environment' + values: ['dev', 'staging', 'prod'] + default: 'dev' + + # Dynamic values from config items + - key: cluster + label: 'Cluster' + valueFrom: + config: + types: ['Kubernetes::Cluster'] + default: 'us-east-1' +``` + +## Variable Properties + +### Required Properties + +| Property | Type | Description | +| -------- | ------ | ----------------------------------------------------------------- | +| `key` | string | Unique identifier for the variable (used in template expressions) | +| `label` | string | Display name shown to users | + +### Optional Properties + +| Property | Type | Description | +| ----------- | ------ | -------------------------------------------------------------------- | +| `default` | string | Default value when view loads | +| `values` | array | Static list of options (mutually exclusive with `valueFrom`) | +| `valueFrom` | object | Dynamic options from config items (mutually exclusive with `values`) | +| `dependsOn` | array | List of variable keys this depends on | + +## Variable Types + +### Static Values Variable + +Use predefined options: + +```yaml +- key: loglevel + label: 'Log Level' + values: ['debug', 'info', 'warning', 'error', 'fatal'] + default: 'info' +``` + +**Use cases**: Environments, log levels, predefined status filters, feature flags + +### Dynamic Values Variable + +Load options from configuration items: + +```yaml +- key: cluster + label: 'Kubernetes Cluster' + valueFrom: + config: + types: ['Kubernetes::Cluster'] # Search for Cluster config items + tagSelector: 'environment=prod' # Optional: filter configs + limit: 100 # Optional: limit results +``` + +**Use cases**: Select from actual resources, allow any config type as filter, dynamic lists + +The variable will be populated with config item names as options. Users can select one value. + +### Dependent Variables + +Create variables that depend on other variables: + +```yaml +templating: + # First variable (no dependencies) + - key: cluster + label: 'Cluster' + valueFrom: + config: + types: ['Kubernetes::Cluster'] + + # Dependent variable (filters based on cluster) + - key: namespace + label: 'Namespace' + valueFrom: + config: + types: ['Kubernetes::Namespace'] + tagSelector: 'cluster=$(var.cluster)' # Reference parent variable + dependsOn: ['cluster'] + default: 'default' + + # Another dependent variable + - key: pod + label: 'Pod' + valueFrom: + config: + types: ['Kubernetes::Pod'] + # Can depend on multiple variables + tagSelector: 'cluster=$(var.cluster),namespace=$(var.namespace)' + dependsOn: ['cluster', 'namespace'] +``` + +**How it works**: + +1. Cluster variable loads first (no dependencies) +2. User selects a cluster +3. Namespace variable's options are dynamically filtered by the selected cluster +4. User selects a namespace +5. Pod variable's options are filtered by both cluster AND namespace + +## Using Variables in Queries + +Variables are available in query definitions using the `$(var.)` syntax: + +### In Config Queries + +```yaml +queries: + pods: + configs: + types: ['Kubernetes::Pod'] + tagSelector: 'cluster=$(var.cluster),namespace=$(var.namespace)' + search: '$(var.pod_search)' +``` + +### In Prometheus Queries + +```yaml +queries: + metrics: + prometheus: + query: | + up{cluster="$(var.cluster)",namespace="$(var.namespace)"} +``` + +### In Custom Merge Queries + +```yaml +merge: | + SELECT * FROM configs + WHERE cluster = '$(var.cluster)' + AND namespace = '$(var.namespace)' +``` + +## Using Variables in Mappings + +Variables can also be used in column mapping expressions: + +```yaml +mapping: + cluster_display: "'$(var.cluster)'" + pod_name: 'row.name' +``` + +## Variable Dependency Resolution + +When views have multiple variables, Mission Control resolves them using topological sorting: + +1. **Level 0**: Variables with no dependencies load first +2. **Level 1**: Variables depending on Level 0 variables load next +3. **Level N**: Variables are resolved in dependency order + +**Circular dependency detection**: Mission Control validates that no circular dependencies exist and rejects invalid configurations. + +**Example resolution order**: + +``` +cluster (Level 0 - no deps) + ↓ +namespace (Level 1 - depends on cluster) +pod (Level 1 - depends on cluster) + ↓ +container (Level 2 - depends on pod) +``` + +## Variable Scope and Caching + +Each unique combination of variable values creates a separate cache entry: + +``` +cluster=us-east-1, namespace=default + → separate cache entry +cluster=us-east-1, namespace=kube-system + → different cache entry +cluster=eu-central-1, namespace=default + → another cache entry +``` + +This means: + +- Users switching between variable combinations get independent cached results +- No interference between different filter selections +- Each combination maintains its own [cache control](./cache-control.md) lifecycle + +## Examples + +### Example 1: Simple Static Variables + +```yaml +apiVersion: mission-control.flanksource.com/v1 +kind: View +metadata: + name: environment-filter +spec: + templating: + - key: env + label: 'Environment' + values: ['development', 'staging', 'production'] + default: 'development' + + - key: sort_by + label: 'Sort By' + values: ['name', 'status', 'age'] + default: 'name' + + queries: + items: + configs: + types: ['Application'] + tagSelector: 'environment=$(var.env)' + + columns: + - name: item_name + type: string + primaryKey: true + + mapping: + item_name: 'row.name' +``` + +### Example 2: Dynamic Cluster and Namespace Selection + +```yaml +apiVersion: mission-control.flanksource.com/v1 +kind: View +metadata: + name: pod-explorer +spec: + display: + title: 'Pod Explorer' + + templating: + # Level 0: No dependencies + - key: cluster + label: 'Cluster' + valueFrom: + config: + types: ['Kubernetes::Cluster'] + default: 'production' + + # Level 1: Depends on cluster + - key: namespace + label: 'Namespace' + valueFrom: + config: + types: ['Kubernetes::Namespace'] + tagSelector: 'cluster=$(var.cluster)' + dependsOn: ['cluster'] + default: 'default' + + columns: + - name: pod_name + type: string + primaryKey: true + - name: status + type: status + + queries: + pods: + configs: + types: ['Kubernetes::Pod'] + tagSelector: 'cluster=$(var.cluster),namespace=$(var.namespace)' + + mapping: + pod_name: 'row.name' + status: 'row.status' +``` + +### Example 3: Multiple Data Sources with Variables + +```yaml +apiVersion: mission-control.flanksource.com/v1 +kind: View +metadata: + name: pod-metrics +spec: + templating: + - key: cluster + label: 'Cluster' + values: ['us-east-1', 'us-west-2', 'eu-central-1'] + default: 'us-east-1' + + - key: metric_type + label: 'Metric Type' + values: ['cpu', 'memory', 'network'] + default: 'cpu' + + columns: + - name: pod + type: string + primaryKey: true + - name: value + type: gauge + + queries: + pods: + configs: + types: ['Kubernetes::Pod'] + tagSelector: 'cluster=$(var.cluster)' + + metrics: + prometheus: + query: | + $(var.metric_type)_usage{cluster="$(var.cluster)"} + columns: + pod: string + value: decimal + + merge: | + SELECT p.name as pod, m.value as value + FROM pods p + LEFT JOIN metrics m ON p.name = m.pod + + mapping: + pod: 'row.pod' + value: 'row.value' +``` + +## Best Practices + +1. **Use meaningful keys** - Choose variable keys that describe what they filter + + ```yaml + key: environment # ✓ Clear + key: env_filter # ✓ Also clear + key: x # ✗ Unclear + ``` + +2. **Provide helpful labels** - Give users context about what to select + + ```yaml + label: "Production Environment" # ✓ Descriptive + label: "Prod" # ✗ Too vague + ``` + +3. **Set sensible defaults** - Most users select default, so choose a good one + + ```yaml + default: "production" # ✓ Safe, commonly used + default: "dev" # ? May not be what most users need + ``` + +4. **Order variables logically** - Put base filters first, dependent filters later + + ```yaml + # ✓ Good order + - cluster # User selects cluster first + - namespace # Then narrows to namespace + - pod # Then finally a specific pod + + # ✗ Poor order + - pod # User can't narrow down pods first + - namespace + - cluster + ``` + +5. **Limit dynamic variable options** - Add reasonable limits to prevent huge lists + ```yaml + valueFrom: + config: + types: ['Kubernetes::Pod'] + limit: 100 # Don't load 10,000 pods + ``` + +## Variable Templating Syntax + +Variables use simple `$(var.key)` syntax for substitution: + +```yaml +# In tagSelector +tagSelector: "cluster=$(var.cluster),env=$(var.env)" + +# In search fields +search: "$(var.pod_search)" + +# In PromQL queries +query: 'up{cluster="$(var.cluster)"}' + +# In merge/custom SQL +WHERE cluster = '$(var.cluster)' +``` + +Note: String substitution happens before query execution, so values are injected as-is. For dynamic SQL, consider using parameterized queries if dealing with user input. diff --git a/mission-control/docs/guide/views/index.md b/mission-control/docs/guide/views/index.md index febadf12..7fcfc3e9 100644 --- a/mission-control/docs/guide/views/index.md +++ b/mission-control/docs/guide/views/index.md @@ -8,20 +8,19 @@ sidebar_custom_props: import View from "@site/docs/reference/views/\_view.mdx" -Views are a SQL-powered feature that allows you to create custom, dynamic dashboards from your Mission Control data. They collect data from multiple sources into an in-memory SQLite database, enabling you to run any SQL query for filtering, joining, and aggregating your observability data. +Views are dynamic, data-driven dashboards in Mission Control that aggregate and visualize data from multiple sources. +They collect data from multiple sources into an in-memory SQLite database, enabling you to run any SQL query for filtering, joining, and aggregating your observability data. -This view creates a dashboard showing Helm releases with their current status, health, and deployment information. +Views serve as a powerful data aggregation and visualization layer in Mission Control. They: -## Overview - -Views solve the challenge of creating custom dashboards and reports by providing: - -- **Dynamic Data Aggregation**: Combine data from multiple sources using SQL -- **Rich Visualizations**: Display data with gauges, charts, status indicators, and more -- **Structured Schema**: Define column types and constraints for consistent data presentation -- **Cached Results**: Views work like materialized views, caching query results in dedicated database tables for fast retrieval +- **Aggregate data** from multiple sources including configuration items, changes, and Prometheus metrics +- **Display data** as interactive tables and visualization panels (gauges, pie charts, ...) +- **Support templating** with variables that users can adjust to dynamically filter and update the view +- **Cache results** intelligently to balance data freshness with performance +- **Transform data** using expressions to map raw query results to meaningful display columns +- **Embed views** inside config item pages or assemble multiple views on one page with sections ## Key Features @@ -32,6 +31,8 @@ Views can query data from multiple sources: - **Config Items**: Infrastructure components and configurations via the `configs` selector - **Changes**: Configuration change tracking and audit data via the `changes` selector - **Metrics**: Time-series data from Prometheus using PromQL queries +- **SQL**: Direct SQL queries via stored connections (PostgreSQL, MySQL, SQL Server) +- **Other Views**: Reuse cached tables from existing views with `viewTableSelector` ### Output Types @@ -86,6 +87,22 @@ panels: This SQL-first approach gives you unlimited flexibility to slice, filter, and aggregate your data. +### Compose Pages with Sections + +Use `spec.sections` to render multiple views together. Mission Control aggregates their variables and shows a single filter bar for all sections. + +```yaml title="workload-overview.yaml" file=/modules/mission-control/fixtures/views/workload-overview.yaml {6-24} + +``` + +### Attach Views to Config Tabs + +`display.plugins` lets you place a view as a tab on matching config items. Variables can be templated from the config to scope the view automatically. + +```yaml title="namespace.yaml" file=/modules/mission-control/fixtures/views/namespace.yaml {66-76} + +``` + ## API Reference diff --git a/mission-control/docs/guide/views/panels/bargauge.md b/mission-control/docs/guide/views/panels/bargauge.md new file mode 100644 index 00000000..d6988c27 --- /dev/null +++ b/mission-control/docs/guide/views/panels/bargauge.md @@ -0,0 +1,41 @@ +--- +title: Bar Gauge +sidebar_custom_props: + icon: mdi:chart-bar +--- + +The `bargauge` panel displays multiple values as horizontal bars with configurable thresholds. Useful for comparing related metrics like CPU real usage vs requests vs limits. + +## Properties + +| Property | Type | Description | +| ------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| `min` | string | Minimum value (can be a number or CEL expression) | +| `max` | string | Maximum value (can be a number or CEL expression) | +| `precision` | int | Decimal places to display | +| `unit` | string | Unit label (e.g., "%", "bytes") | +| `group` | string | Name of a group. When multiple bargauges have the same group, they are placed together. This is useful when each bar needs different properties. | +| `format` | string | Display format: `percentage` or `multiplier` | +| `thresholds` | array | Color thresholds based on percentage | + +## Expected Columns + +| Column | Type | Description | +| ---------------------------------- | ------ | ------------------------------------- | +| `value` | number | Numeric value for each bar (required) | +| `name` or first non-`value` column | string | Bar label (required) | + +## Thresholds + +Thresholds define colors at percentage breakpoints: + +| Property | Type | Description | +| --------- | ------ | ------------------------------------------ | +| `percent` | int | Percentage at which this threshold applies | +| `color` | string | Color name or hex code (e.g., "#10b981") | + +## Example + +```yaml title="bargauge.yaml" file=/modules/mission-control/fixtures/views/panels/bargauge.yaml + +``` diff --git a/mission-control/docs/guide/views/panels/duration.md b/mission-control/docs/guide/views/panels/duration.md new file mode 100644 index 00000000..1d6b1010 --- /dev/null +++ b/mission-control/docs/guide/views/panels/duration.md @@ -0,0 +1,20 @@ +--- +title: Duration +sidebar_custom_props: + icon: mdi:timer-outline +--- + +The `duration` panel displays time duration values in a human-readable format (e.g., "2h 30m"). It has no additional configuration properties. + +## Expected Columns + +| Column | Type | Description | +| ------- | ------ | --------------------------------------------------- | +| `value` | number | Duration in **nanoseconds** (required) | +| `label` | string | Optional per-row label (defaults to the panel name) | + +## Example + +```yaml title="pipelines.yaml" file=/modules/mission-control/fixtures/views/pipelines.yaml + +``` diff --git a/mission-control/docs/guide/views/panels/gauge.md b/mission-control/docs/guide/views/panels/gauge.md new file mode 100644 index 00000000..44ca2723 --- /dev/null +++ b/mission-control/docs/guide/views/panels/gauge.md @@ -0,0 +1,39 @@ +--- +title: Gauge +sidebar_custom_props: + icon: mdi:gauge +--- + +The `gauge` panel displays a single numeric value as a visual gauge with configurable min/max bounds and color thresholds. + +## Properties + +| Property | Type | Description | +| ------------ | ------ | ------------------------------------------------- | +| `min` | string | Minimum value (can be a number or CEL expression) | +| `max` | string | Maximum value (can be a number or CEL expression) | +| `precision` | int | Decimal places to display | +| `unit` | string | Unit label (e.g., "%", "pods") | +| `thresholds` | array | Color thresholds based on percentage | + +## Expected Columns + +| Column | Type | Description | +| ------- | ------ | ---------------------------------------------------- | +| `value` | number | Numeric value to display (required) | +| `label` | string | Optional label; the panel title is used when omitted | + +## Thresholds + +Thresholds define colors at percentage breakpoints (0-100%): + +| Property | Type | Description | +| --------- | ------ | ------------------------------------------------- | +| `percent` | int | Percentage at which this threshold applies | +| `color` | string | Color name or hex code (e.g., "green", "#10b981") | + +## Example + +```yaml title="gauge.yaml" file=/modules/mission-control/fixtures/views/panels/gauge.yaml + +``` diff --git a/mission-control/docs/guide/views/panels/index.mdx b/mission-control/docs/guide/views/panels/index.mdx new file mode 100644 index 00000000..04316d4f --- /dev/null +++ b/mission-control/docs/guide/views/panels/index.mdx @@ -0,0 +1,38 @@ +--- +sidebar_position: 4 +title: Panels +sidebar_custom_props: + icon: mdi:chart-box +--- + +Panels display aggregated data visualizations above the main table. Each panel runs a SQL query against the view's query results and renders the data as a chart, gauge, or summary. + +## Panel Definition + +Panels are defined in `spec.panels`: + +```yaml +panels: + - name: Pod Count # Panel name + description: Total pods # Optional description + type: gauge # Panel type + query: SELECT COUNT(*) AS value FROM pods +``` + +## Common Properties + +All panel types support these properties: + +| Property | Type | Description | +|----------|------|-------------| +| `name` | string | Panel name (required) | +| `description` | string | Optional description | +| `type` | string | Panel type (required) | +| `query` | string | SQL query against view data (required) | +| `gauge`, `number`, `piechart`, `bargauge`, `timeseries` | object | Type-specific options | + +## Panel Types + +import DocCardList from '@theme/DocCardList'; + + diff --git a/mission-control/docs/guide/views/panels/number.md b/mission-control/docs/guide/views/panels/number.md new file mode 100644 index 00000000..ed086780 --- /dev/null +++ b/mission-control/docs/guide/views/panels/number.md @@ -0,0 +1,33 @@ +--- +title: Number +sidebar_custom_props: + icon: mdi:numeric +--- + +The `number` panel displays a single numeric value with optional unit formatting. + +## Properties + +| Property | Type | Description | +| ----------- | ------ | --------------------------------------------- | +| `unit` | string | Unit label (e.g., "bytes", "seconds", "pods") | +| `precision` | int | Decimal places to display | + +## Expected Columns + +| Column | Type | Description | +| ------------------ | ------ | --------------------------------------------------- | +| `value` or `count` | number | Numeric value to display (required) | +| `label` | string | Optional per-row label (defaults to the panel name) | + +## Example + +```yaml title="number.yaml" file=/modules/mission-control/fixtures/views/panels/number.yaml + +``` + +## With Unit and Precision + +```yaml title="resource-usage.yaml" file=/modules/mission-control/fixtures/views/panels/resource-usage.yaml + +``` diff --git a/mission-control/docs/guide/views/panels/piechart.md b/mission-control/docs/guide/views/panels/piechart.md new file mode 100644 index 00000000..055f2260 --- /dev/null +++ b/mission-control/docs/guide/views/panels/piechart.md @@ -0,0 +1,31 @@ +--- +title: Piechart +sidebar_custom_props: + icon: mdi:chart-pie +--- + +The `piechart` panel displays data distribution as a pie chart with customizable colors and labels. + +## Properties + +| Property | Type | Description | +| ------------ | ---- | --------------------------------------------- | +| `showLabels` | bool | Whether to display value labels on pie slices | +| `colors` | map | Custom color mapping by label name | + +## Expected Columns + +| Column | Type | Description | +| ------------------------ | ------ | -------------------------------------- | +| `count` | number | Size of each slice (required) | +| First non-`count` column | string | Slice label (e.g., `health`, `status`) | + +## Colors + +The `colors` property maps label values to hex color codes. If not specified, colors are automatically assigned based on semantic meaning (e.g., "healthy" gets green, "error" gets red). + +## Example + +```yaml title="piechart.yaml" file=/modules/mission-control/fixtures/views/panels/piechart.yaml + +``` diff --git a/mission-control/docs/guide/views/panels/table.md b/mission-control/docs/guide/views/panels/table.md new file mode 100644 index 00000000..ea6491fc --- /dev/null +++ b/mission-control/docs/guide/views/panels/table.md @@ -0,0 +1,20 @@ +--- +title: Table +sidebar_custom_props: + icon: mdi:table +--- + +The `table` panel displays query results as a simple key-value list. It has no additional configuration. + +## Expected Columns + +| Column | Type | Description | +| ------------------------ | ------------- | --------------------------------------- | +| `value` | string/number | Value displayed on the right (required) | +| First non-`value` column | string | Label displayed on the left (required) | + +## Example + +```yaml title="table.yaml" file=/modules/mission-control/fixtures/views/panels/table.yaml + +``` diff --git a/mission-control/docs/guide/views/panels/text.md b/mission-control/docs/guide/views/panels/text.md new file mode 100644 index 00000000..c91c7716 --- /dev/null +++ b/mission-control/docs/guide/views/panels/text.md @@ -0,0 +1,19 @@ +--- +title: Text +sidebar_custom_props: + icon: mdi:text +--- + +The `text` panel renders free-form text rows. Use it for short notes, callouts, or contextual guidance above the table. + +## Expected Columns + +| Column | Type | Description | +| ------- | ------ | -------------------------- | +| `value` | string | Text to display (required) | + +## Example + +```yaml title="text.yaml" file=/modules/mission-control/fixtures/views/panels/text.yaml + +``` diff --git a/mission-control/docs/guide/views/queries/changes.md b/mission-control/docs/guide/views/queries/changes.md new file mode 100644 index 00000000..4110657d --- /dev/null +++ b/mission-control/docs/guide/views/queries/changes.md @@ -0,0 +1,50 @@ +--- +title: Change Queries +sidebar_custom_props: + icon: diff +--- + +Change queries retrieve the audit trail of modifications to configuration items. + +## Query Properties + +| Property | Type | Description | +| ------------- | ------- | ----------------------------------------------------------------- | +| `types` | array | Config types to get changes for | +| `search` | string | Free text search (supports `change_type=` and `@order=` prefixes) | +| `tagSelector` | string | Filter by tags of the affected config items | +| `severity` | string | Filter by severity (`info`, `warning`, `critical`) | +| `agent` | string | Filter by specific agent | +| `limit` | integer | Maximum changes to return | + +## Example + +```yaml title="pipelines.yaml" file=/modules/mission-control/fixtures/views/pipelines.yaml {57-63} + +``` + +## Auto-Mapped Columns + +Change queries provide these columns: + +- `id` - Change identifier +- `config_id`, `config_name`, `config_type` - Affected resource +- `change_type` - Type of change (e.g., `Created`, `Updated`, `Deleted`) +- `severity` - Change severity +- `summary` - Human-readable summary +- `details` - Full change details (JSON) +- `created_at` - When the change was detected +- `tags`, `labels` - Tags/labels of the affected resource + +## Search Syntax + +```yaml +# Filter by change type +search: change_type=BackupSuccessful + +# Ordering +search: "@order=-created_at" + +# Combine +search: "change_type=GitHubActionRun* @order=-created_at" +``` diff --git a/mission-control/docs/guide/views/queries/configs.md b/mission-control/docs/guide/views/queries/configs.md new file mode 100644 index 00000000..93c84ba8 --- /dev/null +++ b/mission-control/docs/guide/views/queries/configs.md @@ -0,0 +1,38 @@ +--- +title: Config Queries +sidebar_custom_props: + icon: config +--- + +Config queries fetch configuration items from the Flanksource catalog. + +## Query Properties + +| Property | Type | Description | +| --------------- | ------- | ------------------------------------------------------- | +| `types` | array | Config types to query (e.g., `["Kubernetes::Pod"]`) | +| `search` | string | Free text search (supports `@order=` for sorting) | +| `status` | string | Filter by status | +| `health` | string | Filter by health status | +| `tagSelector` | string | Filter by tags (e.g., `environment=prod,team=platform`) | +| `labelSelector` | string | Filter by Kubernetes-style labels | +| `agent` | string | Filter by agent ID (use `all` for all agents) | +| `limit` | integer | Maximum results to return | + +## Example + +```yaml title="cronjobs.yaml" file=/modules/mission-control/fixtures/views/cronjobs.yaml {32-37} + +``` + +## Auto-Mapped Columns + +Config queries provide these columns: + +- `id`, `name`, `type`, `namespace` - Core identifiers +- `status`, `health` - Current state +- `created_at`, `updated_at` - Timestamps +- `config` - Raw configuration (JSON) +- `properties`, `tags`, `labels` - Metadata +- `cost_total_1d`, `cost_total_7d`, `cost_total_30d` - Cost data +- `parent_id`, `parent_name`, `parent_type` - Relationships diff --git a/mission-control/docs/guide/views/queries/index.mdx b/mission-control/docs/guide/views/queries/index.mdx new file mode 100644 index 00000000..554fff6e --- /dev/null +++ b/mission-control/docs/guide/views/queries/index.mdx @@ -0,0 +1,11 @@ +--- +sidebar_position: 2 +title: Queries +sidebar_custom_props: + icon: sql +--- + + +import DocCardList from '@theme/DocCardList'; + + diff --git a/mission-control/docs/guide/views/queries/prometheus.md b/mission-control/docs/guide/views/queries/prometheus.md new file mode 100644 index 00000000..a7efaf81 --- /dev/null +++ b/mission-control/docs/guide/views/queries/prometheus.md @@ -0,0 +1,45 @@ +--- +title: Prometheus Queries +sidebar_custom_props: + icon: prometheus +--- + +Prometheus queries fetch metrics directly from Prometheus using PromQL. + +## Query Properties + +| Property | Type | Description | +| ------------ | ------ | -------------------------------------------------------------------- | +| `connection` | string | Prometheus connection reference (e.g., `connection://mc/prometheus`) | +| `query` | string | PromQL query expression | +| `columns` | object | Define column types for results (recommended) | + +## Example + +```yaml title="pods.yaml" file=/modules/mission-control/fixtures/views/pods.yaml {75-103} + +``` + +## Column Definitions + +Define column types to control display and handle empty results: + +```yaml +queries: + memory: + columns: + namespace: string + pod: string + value: decimal + prometheus: + connection: connection://mc/prometheus + query: | + sum by (namespace, pod) (container_memory_working_set_bytes{...}) +``` + +## Auto-Mapped Columns + +Prometheus queries return: + +- **Labels** from the metric as string columns (e.g., `namespace`, `pod`, `instance`) +- **value** as a numeric column containing the metric value diff --git a/mission-control/docs/guide/views/queries/sql.md b/mission-control/docs/guide/views/queries/sql.md new file mode 100644 index 00000000..ba8ecd68 --- /dev/null +++ b/mission-control/docs/guide/views/queries/sql.md @@ -0,0 +1,33 @@ +--- +title: SQL Queries +sidebar_custom_props: + icon: mdi:database +--- + +Use `sql` to run queries against external databases (PostgreSQL, MySQL, or SQL Server) and feed the results into a view. SQL queries share the same templating and mapping capabilities as other data sources. + +## Configuration + +| Field | Description | +| ------------ | ------------------------------------------------------------------------------ | +| `connection` | Name or URL of a stored SQL connection (recommended) | +| `type` | Connection type when not using `connection` (`postgres`, `mysql`, `sqlserver`) | +| `url` | Database DSN/URL (supports env var references) | +| `username` | Database username (supports env var references) | +| `password` | Database password (supports env var references) | +| `query` | SQL statement to execute | + +Mission Control hydrates the connection details before executing the query, so secrets can stay in stored connections or environment variables. + +## Example + +```yaml title="failing-health-checks.yaml" file=/modules/mission-control/fixtures/views/failing-health-checks.yaml {33-59} + +``` + +## Tips + +- Prefer `connection` to reuse managed credentials instead of embedding URLs and passwords. +- Declare query `columns` and `mapping` to control types when the upstream query could return empty results. +- Use templating to parameterize queries (`$(var.cluster)`) just like other view data sources. +- Keep SQL focused on selection/aggregation; use `merge` to combine SQL results with other data sources. diff --git a/mission-control/docs/guide/views/queries/view-table-selector.md b/mission-control/docs/guide/views/queries/view-table-selector.md new file mode 100644 index 00000000..a743240c --- /dev/null +++ b/mission-control/docs/guide/views/queries/view-table-selector.md @@ -0,0 +1,39 @@ +--- +title: View Table Selector Queries +sidebar_custom_props: + icon: view-details +--- + +View table selectors reuse cached data from other views instead of hitting the original data sources again. They are ideal for composing dashboards, drilldowns, or summaries on top of existing views. + +## Overview + +- Query the materialized Postgres tables generated for other views +- Avoid re-running expensive source queries +- Compose multiple views together or build drill-downs +- Keep formatting and column types consistent with the source view + +## Query Properties + +| Property | Type | Description | +| --------------- | ------ | -------------------------------------------------------------------------- | +| `name` | string | Name of the view to query | +| `namespace` | string | Namespace of the view (default: `default`) | +| `labelSelector` | string | Optional label selector to match multiple views (for composition by label) | + +The selector returns **all rows** from the matched view tables. Shape or filter the data in your `merge` SQL or panel queries. + +## Example: Summarize an Existing View + +Aggregate the cached `deployments` view to show healthy vs unhealthy releases per namespace: + +```yaml title="deployments-summary.yaml" file=/modules/mission-control/fixtures/views/deployments-summary.yaml {10-37} + +``` + +## Tips + +- Ensure the source view has cached data; the selector reads the Postgres table created for that view. +- Column types are inherited from the source view, so filters and formatting stay consistent. +- Use `labelSelector` when you want to stitch together multiple related views without hard-coding names. +- Apply filtering and limits in `merge` SQL or panel queries to keep selectors simple. diff --git a/mission-control/docs/guide/views/table/columns/badge.md b/mission-control/docs/guide/views/table/columns/badge.md new file mode 100644 index 00000000..700adf22 --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/badge.md @@ -0,0 +1,13 @@ +--- +title: Badge +sidebar_custom_props: + icon: mdi:label +--- + +The `badge` column type displays text values as styled badge/tag elements. Useful for categories, labels, and versions. + +## Example + +```yaml title="backups.yaml" file=/modules/mission-control/fixtures/views/backups.yaml {30-32} + +``` diff --git a/mission-control/docs/guide/views/table/columns/boolean.md b/mission-control/docs/guide/views/table/columns/boolean.md new file mode 100644 index 00000000..6866bc1d --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/boolean.md @@ -0,0 +1,15 @@ +--- +title: Boolean +sidebar_custom_props: + icon: mdi:check-circle-outline +--- + +The `boolean` column type renders `true`/`false` values as friendly yes/no text in tables and cards. Use it for readiness, feature flags, or any binary state. + +## Example + +```yaml title="boolean-example.yaml" file=/modules/mission-control/fixtures/views/boolean-example.yaml {24-26} + +``` + +If you enable `filter.type: multiselect` on a boolean column, Mission Control builds the options from the cached table so users can quickly include or exclude rows by state without triggering a view refresh. diff --git a/mission-control/docs/guide/views/table/columns/bytes.md b/mission-control/docs/guide/views/table/columns/bytes.md new file mode 100644 index 00000000..bcc25467 --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/bytes.md @@ -0,0 +1,21 @@ +--- +title: Bytes +sidebar_custom_props: + icon: mdi:memory +--- + +The `bytes` column type displays byte sizes with automatic formatting (B, KB, MB, GB, TB). + +## Auto-Formatting + +| Input Value | Display | +| ----------- | ------- | +| 512 | 512 B | +| 1048576 | 1 MB | +| 1073741824 | 1 GB | + +## Example + +```yaml title="pods.yaml" file=/modules/mission-control/fixtures/views/pods.yaml {41-44} + +``` diff --git a/mission-control/docs/guide/views/table/columns/config-item.md b/mission-control/docs/guide/views/table/columns/config-item.md new file mode 100644 index 00000000..808b6483 --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/config-item.md @@ -0,0 +1,15 @@ +--- +title: Config Item +sidebar_custom_props: + icon: config +--- + +The `config_item` column type displays clickable links to configuration item detail pages with a proper icon. + +This column requires an `id` field in row. + +## Example + +```yaml title="cronjobs.yaml" file=/modules/mission-control/fixtures/views/cronjobs.yaml {16-17} + +``` diff --git a/mission-control/docs/guide/views/table/columns/datetime.md b/mission-control/docs/guide/views/table/columns/datetime.md new file mode 100644 index 00000000..dcce45ae --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/datetime.md @@ -0,0 +1,13 @@ +--- +title: DateTime +sidebar_custom_props: + icon: mdi:calendar-clock +--- + +The `datetime` column type displays timestamps with human-readable formatting. + +## Example + +```yaml title="cronjobs.yaml" file=/modules/mission-control/fixtures/views/cronjobs.yaml {32-33} + +``` diff --git a/mission-control/docs/guide/views/table/columns/duration.md b/mission-control/docs/guide/views/table/columns/duration.md new file mode 100644 index 00000000..f1982063 --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/duration.md @@ -0,0 +1,13 @@ +--- +title: Duration +sidebar_custom_props: + icon: mdi:timer-outline +--- + +The `duration` column type displays time spans in human-readable format (e.g., "2h 30m", "1d"). The value is expected in nanoseconds. + +## Example + +```yaml title="pipelines.yaml" file=/modules/mission-control/fixtures/views/pipelines.yaml {49-51} + +``` diff --git a/mission-control/docs/guide/views/table/columns/gauge.md b/mission-control/docs/guide/views/table/columns/gauge.md new file mode 100644 index 00000000..b838d036 --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/gauge.md @@ -0,0 +1,22 @@ +--- +title: Gauge +sidebar_custom_props: + icon: mdi:gauge +--- + +The `gauge` column type displays numeric values as visual gauges with configurable thresholds and colors. + +## Gauge Configuration + +| Property | Type | Description | +| ------------ | ------ | -------------------------------------------------- | +| `max` | string | Maximum value (can be CEL like `row.memory_limit`) | +| `min` | string | Minimum value | +| `precision` | int | Decimal places | +| `thresholds` | array | Color thresholds by percent | + +## Example + +```yaml title="namespace.yaml" file=/modules/mission-control/fixtures/views/namespace.yaml {25-38} + +``` diff --git a/mission-control/docs/guide/views/table/columns/health.md b/mission-control/docs/guide/views/table/columns/health.md new file mode 100644 index 00000000..f4a60503 --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/health.md @@ -0,0 +1,15 @@ +--- +title: Health +sidebar_custom_props: + icon: health +--- + +The `health` column type displays health status with color-coded indicators. Supports three states: `healthy` (green), `warning` (yellow), and `unhealthy`/`critical` (red). + +## Example + +```yaml title="ingress.yaml" file=/modules/mission-control/fixtures/views/ingress.yaml {41-47} + +``` + +Use `card.useForAccent: true` to color the card based on health. diff --git a/mission-control/docs/guide/views/table/columns/index.mdx b/mission-control/docs/guide/views/table/columns/index.mdx new file mode 100644 index 00000000..3ce712da --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/index.mdx @@ -0,0 +1,62 @@ +--- +title: Columns +sidebar_custom_props: + icon: mdi:table-column +--- + +Tables define the structure and display of view data. Each column has a type that controls how values are rendered and filtered. + +## Column Definition + +Columns are defined in `spec.columns`: + +```yaml +columns: + - name: pod_name # Column identifier (used in mapping) + type: string # Column type + primaryKey: true # At least one column must be a primary key + description: "Pod name" +``` + +## Example + +This example demonstrates multiple column types including gauge, status, health, datetime, and card positioning: + +```yaml title="namespace.yaml" file=/modules/mission-control/fixtures/views/namespace.yaml {7-65} + +``` + +## Common Properties + +All column types support these properties: + +| Property | Type | Description | +|----------|------|-------------| +| `name` | string | Column identifier (required) | +| `type` | string | Column type (required) | +| `primaryKey` | bool | Include in composite primary key | +| `description` | string | Help text for the column | +| `hidden` | bool | Hide from table display | +| `filter` | object | Enable filtering (`type: multiselect`), labels columns support include/exclude per key | +| `icon` | string | Icon for the column (supports CEL like `row.health`) | +| `url` | object | Link to configs, views, or custom URLs (see URL column type) | +| `unit` | string | Unit label for gauges, numbers, and durations | +| `configItem` | object | Options for `config_item` columns (e.g., custom `idField`) | +| `card` | object | Card layout position (`position`, `useForAccent`) | + +## Card Positioning + +Columns can be positioned in card layouts using `card.position`: + +- `title` - Card title area +- `subtitle` - Subtitle area +- `deck` - Header area after subtitle +- `body` - Main content area +- `footer` - Footer area +- `headerRight` - Right side of header + +## Column Types + +import DocCardList from '@theme/DocCardList'; + + diff --git a/mission-control/docs/guide/views/table/columns/labels.md b/mission-control/docs/guide/views/table/columns/labels.md new file mode 100644 index 00000000..efc09b41 --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/labels.md @@ -0,0 +1,19 @@ +--- +title: Labels +sidebar_custom_props: + icon: mdi:label +--- + +The `labels` column type renders key/value labels and supports include/exclude filtering by tag. + +## Example + +```yaml title="pods.yaml" file=/modules/mission-control/fixtures/views/pods.yaml {49-50} + +``` + +## Behavior + +- Values must be a JSON object (e.g., `{team: "platform", env: "prod"}`). +- Enable `filter.type: multiselect` to allow users to include or exclude specific label values. +- UI filters encode selections per label key, so users can combine multiple label filters without losing context. diff --git a/mission-control/docs/guide/views/table/columns/millicore.md b/mission-control/docs/guide/views/table/columns/millicore.md new file mode 100644 index 00000000..d90cb95e --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/millicore.md @@ -0,0 +1,21 @@ +--- +title: Millicore +sidebar_custom_props: + icon: mdi:cpu-64-bit +--- + +The `millicore` column type displays CPU resources in millicores (1000m = 1 CPU core). + +## Conversion + +| Value | Display | Meaning | +| ----- | ------- | --------------- | +| 100 | 100m | 10% of a core | +| 500 | 500m | 50% of a core | +| 1000 | 1000m | 1 full CPU core | + +## Example + +```yaml title="pods.yaml" file=/modules/mission-control/fixtures/views/pods.yaml {45-48} + +``` diff --git a/mission-control/docs/guide/views/table/columns/number.md b/mission-control/docs/guide/views/table/columns/number.md new file mode 100644 index 00000000..52ff2dcc --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/number.md @@ -0,0 +1,20 @@ +--- +title: Number +sidebar_custom_props: + icon: mdi:numeric +--- + +The `number` column type displays numeric values (integers and decimals). + +## Properties + +| Property | Type | Description | +| ----------- | ------- | ------------------------------------ | +| `precision` | integer | Decimal places to display | +| `unit` | string | Display unit (e.g., "pods", "req/s") | + +## Example + +```yaml title="namespace.yaml" file=/modules/mission-control/fixtures/views/notification-send-history.yaml {27-30} + +``` diff --git a/mission-control/docs/guide/views/table/columns/status.md b/mission-control/docs/guide/views/table/columns/status.md new file mode 100644 index 00000000..77eeee80 --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/status.md @@ -0,0 +1,13 @@ +--- +title: Status +sidebar_custom_props: + icon: mdi:list-status +--- + +The `status` column type displays status values with appropriate styling. Common values include `Running`, `Pending`, `Failed`, `Succeeded`, etc. + +## Example + +```yaml title="cronjobs.yaml" file=/modules/mission-control/fixtures/views/cronjobs.yaml {20-24} + +``` diff --git a/mission-control/docs/guide/views/table/columns/string.md b/mission-control/docs/guide/views/table/columns/string.md new file mode 100644 index 00000000..a628fcbb --- /dev/null +++ b/mission-control/docs/guide/views/table/columns/string.md @@ -0,0 +1,13 @@ +--- +title: String +sidebar_custom_props: + icon: mdi:format-text +--- + +The `string` column type displays text values. + +## Example + +```yaml title="cronjobs.yaml" file=/modules/mission-control/fixtures/views/cronjobs.yaml {18-19} + +``` diff --git a/mission-control/docs/guide/views/table/index.md b/mission-control/docs/guide/views/table/index.md new file mode 100644 index 00000000..4632ddd4 --- /dev/null +++ b/mission-control/docs/guide/views/table/index.md @@ -0,0 +1,47 @@ +--- +slug: /guide/table +title: Table +sidebar_position: 6 +sidebar_custom_props: + icon: view-details +--- + +Tables are the primary way to explore view data in Mission Control. They render rows from your queries, apply mappings, and drive filters, cards, and links into configs or other views. + +## When to Use + +- You need sortable, filterable rows for configs, changes, or metrics. +- You want to mix multiple data sources into one grid via SQL `merge`. +- You want card layouts (titles, subtitles, gauges) backed by the same table data. + +## Anatomy of a Table View + +- **columns**: Define the schema and rendering (type, filters, card placement, URLs). +- **queries**: Pull data from configs, changes, Prometheus, SQL, or other views. +- **merge**: Optional SQL to join multiple queries into one result set. +- **mapping**: Optional CEL to reshape query fields into your columns. +- **templating**: Variables used inside queries and filters for user-scoped data. + +## Example: Namespace Table + +This view shows how card positioning, gauges, and filters are driven by columns: + +```yaml title="namespace.yaml" file=/modules/mission-control/fixtures/views/namespace.yaml {7-66} + +``` + +### Joining Multiple Data Sources + +Use `merge` to combine configs with Prometheus metrics into one table: + +```yaml title="namespace.yaml" file=/modules/mission-control/fixtures/views/namespace.yaml {85-114} + +``` + +## Best Practices + +- Always set `primaryKey` on at least one column (composite keys allowed). +- Prefer `mapping` to normalize field names and units (e.g., bytes, millicores). +- Keep `merge` focused on shaping/joins; leave presentation to columns. +- Enable `filter.type: multiselect` on columns users need to slice without refresh. +- Use templated variables for scoping (e.g., cluster/namespace) instead of hard-coding tags. diff --git a/mission-control/docs/integrations/flux/catalog.md b/mission-control/docs/integrations/flux/catalog.md index f85a9d13..97aa4276 100644 --- a/mission-control/docs/integrations/flux/catalog.md +++ b/mission-control/docs/integrations/flux/catalog.md @@ -29,7 +29,6 @@ Mission Control provides built-in support for relating Flux resources to each ot - Enable ClickOps workflows backed by git. diff --git a/mission-control/docs/reference/canary-checker/check.md b/mission-control/docs/reference/canary-checker/check.md index 095a3db8..3d840469 100644 --- a/mission-control/docs/reference/canary-checker/check.md +++ b/mission-control/docs/reference/canary-checker/check.md @@ -2,25 +2,25 @@ title: Check --- -| Field | Description | Scheme | -| ---------------------- | ----------------------------------------------------------- | ------------------------------- | -| `id` | ID of the check | `uuid` | -| `canary_id` | ID of the canary | `uuid` | -| `agent_id` | ID of the agent | `uuid` | -| `type` | The type of the check | `string` | -| `name` | The name of the check | `string` | -| `namespace` | The namespace of the check | `string` | -| `labels` | The labels of the check | `map[string]string` | -| `description` | The description of the check | `string` | -| `status` | The status of the check _(healthy/unhealthy)_ | `string` | -| `owner` | The owner of the check | `string` | -| `severity` | The severity of the check _(info/low/medium/high/critical)_ | `string` | +| Field | Description | Scheme | +| ---------------------- | ----------------------------------------------------------- | ------------------------------------ | +| `id` | ID of the check | `uuid` | +| `canary_id` | ID of the canary | `uuid` | +| `agent_id` | ID of the agent | `uuid` | +| `type` | The type of the check | `string` | +| `name` | The name of the check | `string` | +| `namespace` | The namespace of the check | `string` | +| `labels` | The labels of the check | `map[string]string` | +| `description` | The description of the check | `string` | +| `status` | The status of the check _(healthy/unhealthy)_ | `string` | +| `owner` | The owner of the check | `string` | +| `severity` | The severity of the check _(info/low/medium/high/critical)_ | `string` | | `icon` | The icon of the check | [`Icon`](/docs/reference/types#icon) | -| `transformed` | The transformed of the check | `bool` | -| `last_runtime` | The last runtime of the check | `*time` | -| `next_runtime` | The next runtime of the check | `*time` | -| `last_transition_time` | The last transition time of the check | `*time` | -| `created_at` | The created at of the check | `time` | -| `updated_at` | The updated at of the check | `time` | -| `deleted_at` | The deleted at of the check | `*time` | -| `silenced_at` | The silenced at of the check | `*time` | +| `transformed` | The transformed of the check | `bool` | +| `last_runtime` | The last runtime of the check | `*time` | +| `next_runtime` | The next runtime of the check | `*time` | +| `last_transition_time` | The last transition time of the check | `*time` | +| `created_at` | The created at of the check | `time` | +| `updated_at` | The updated at of the check | `time` | +| `deleted_at` | The deleted at of the check | `*time` | +| `silenced_at` | The silenced at of the check | `*time` | diff --git a/mission-control/docs/reference/config-db/properties.md b/mission-control/docs/reference/config-db/properties.md index dc6a0d97..f72d3976 100644 --- a/mission-control/docs/reference/config-db/properties.md +++ b/mission-control/docs/reference/config-db/properties.md @@ -3,30 +3,30 @@ title: Property description: Property fields for config items --- -| Field | Description | Scheme | Required | -| ---------- | ---------------------------------------------------------------------------- | ------------------------------- | ---------- | -| `name` | Set name for component property. | `string` | | -| `value` | Mutually exclusive with `text` | `int64` | | -| `text` | Mutually exclusive with `value` | `string` | | -| `type` | Specify type of component property, one of `currency`, `number`, `url` | `string` | | -| `unit` | Unit for component property e.g. milliseconds, bytes, millicores, epoch etc. | `string` | | -| `color` | Set color for component property. | `string` | | -| `headline` | Toggle headline for component property. | `bool` | | +| Field | Description | Scheme | Required | +| ---------- | ---------------------------------------------------------------------------- | ------------------------------------ | ---------- | +| `name` | Set name for component property. | `string` | | +| `value` | Mutually exclusive with `text` | `int64` | | +| `text` | Mutually exclusive with `value` | `string` | | +| `type` | Specify type of component property, one of `currency`, `number`, `url` | `string` | | +| `unit` | Unit for component property e.g. milliseconds, bytes, millicores, epoch etc. | `string` | | +| `color` | Set color for component property. | `string` | | +| `headline` | Toggle headline for component property. | `bool` | | | `icon` | Specify icon for component. | [`Icon`](/docs/reference/types#icon) | | -| `label` | Specify label for component property. | `string` | | -| `links` | Set links pertaining to component. | [`[]Link`](#link) | | -| `max` | Set maximum value for components to display. | `int64` | `optional` | -| `min` | Set minimum value for components to display. | `int64` | | -| `order` | Set integer value order for component property. | `int` | | -| `tooltip` | Set tooltip outlining information pertaining to the component. | `string` | | +| `label` | Specify label for component property. | `string` | | +| `links` | Set links pertaining to component. | [`[]Link`](#link) | | +| `max` | Set maximum value for components to display. | `int64` | `optional` | +| `min` | Set minimum value for components to display. | `int64` | | +| `order` | Set integer value order for component property. | `int` | | +| `tooltip` | Set tooltip outlining information pertaining to the component. | `string` | | ## Link -| Field | Description | Schema | Required | -| --------- | ------------------------ | ------------------------------- | -------- | -| `type` | The type of the link. | `string` | | -| `url` | The url of the link. | `string` | | -| `tooltip` | The tooltip of the link. | `string` | | +| Field | Description | Schema | Required | +| --------- | ------------------------ | ------------------------------------ | -------- | +| `type` | The type of the link. | `string` | | +| `url` | The url of the link. | `string` | | +| `tooltip` | The tooltip of the link. | `string` | | | `icon` | The icon of the link. | [`Icon`](/docs/reference/types#icon) | | -| `text` | The text of the link. | `string` | | -| `label` | The label of the link. | `string` | | +| `text` | The text of the link. | `string` | | +| `label` | The label of the link. | `string` | | diff --git a/mission-control/docs/reference/config-db/scrape-result.md b/mission-control/docs/reference/config-db/scrape-result.md index 49dd3aee..7f2f78c1 100644 --- a/mission-control/docs/reference/config-db/scrape-result.md +++ b/mission-control/docs/reference/config-db/scrape-result.md @@ -3,21 +3,21 @@ title: Scrape Result description: Fields available during scrape transform --- -| Field | Description | Scheme | -| -------------- | ------------------------------------------------------ | ------------------------------- | -| `id` | Designated id (or the external id) of this config item | `string` | -| `config_class` | Class of the config item | `string` | -| `config_type` | Type of the config item | `string` | -| `status` | Status extracted from the config itself | `string` | -| `name` | Name of the config item | `string` | -| `namespace` | Namespace of the config item | `string` | -| `description` | Description of the config item | `string` | -| `aliases` | Aliases associated with the config item | `[]string` | -| `source` | Source of the config item | `string` | -| `config` | Configuration details | `interface{}` | -| `format` | Format of the config item | `string` | +| Field | Description | Scheme | +| -------------- | ------------------------------------------------------ | ------------------------------------ | +| `id` | Designated id (or the external id) of this config item | `string` | +| `config_class` | Class of the config item | `string` | +| `config_type` | Type of the config item | `string` | +| `status` | Status extracted from the config itself | `string` | +| `name` | Name of the config item | `string` | +| `namespace` | Namespace of the config item | `string` | +| `description` | Description of the config item | `string` | +| `aliases` | Aliases associated with the config item | `[]string` | +| `source` | Source of the config item | `string` | +| `config` | Configuration details | `interface{}` | +| `format` | Format of the config item | `string` | | `icon` | Icon associated with the config item | [`Icon`](/docs/reference/types#icon) | -| `tags` | Tags associated with the config item | `JSONStringMap` | -| `analysis` | Analysis result of the config item | `*AnalysisResult` | -| `action` | Action related to the config item | `string` | -| `properties` | Properties associated with the config item | `types.Properties` | +| `tags` | Tags associated with the config item | `JSONStringMap` | +| `analysis` | Analysis result of the config item | `*AnalysisResult` | +| `action` | Action related to the config item | `string` | +| `properties` | Properties associated with the config item | `types.Properties` | diff --git a/mission-control/docs/reference/playbooks/events.md b/mission-control/docs/reference/playbooks/events.md index d8ba8046..b44de5c4 100644 --- a/mission-control/docs/reference/playbooks/events.md +++ b/mission-control/docs/reference/playbooks/events.md @@ -8,11 +8,11 @@ Multiple playbooks could be listening to the same event and likewise a playbook Filters can give you fine-grained control over the events that can trigger the playbook. -| Field | Description | Scheme | Required | -| -------- | ------------------------------------------------------------------------------------- | ------------------------------- | -------- | -| `event` | Event to listen for. | `string` | `true` | +| Field | Description | Scheme | Required | +| -------- | ------------------------------------------------------------------------------------- | ------------------------------------ | -------- | +| `event` | Event to listen for. | `string` | `true` | | `filter` | Filter events to trigger on | [CEL](/docs/reference/scripting/cel) | `true` | -| `labels` | Labels specifies the key-value pairs that the associated event's resource must match. | `map[string]string` | `false` | +| `labels` | Labels specifies the key-value pairs that the associated event's resource must match. | `map[string]string` | `false` | ## Canary diff --git a/mission-control/docs/reference/playbooks/index.md b/mission-control/docs/reference/playbooks/index.md index 6537648c..cba9e2c0 100644 --- a/mission-control/docs/reference/playbooks/index.md +++ b/mission-control/docs/reference/playbooks/index.md @@ -5,24 +5,24 @@ sidebar_custom_props: icon: playbook --- -| Field | Description | Scheme | -| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | -| **`description`** | A short description | `string` | +| Field | Description | Scheme | +| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | +| **`description`** | A short description | `string` | | `icon` | Icon for the playbook | [`Icon`](/docs/reference/types#icon) | -| `timeout` | Maximum duration to let the playbook run before cancellation. Valid time units are "s", "m", "h", "d", "w", "y". Defaults to 30 minutes. | `string` | -| `on.canary` | Run a playbook when a health check fails or passes | [`EventTrigger`](./events#canary) | -| `on.component` | Run a playbook when a part becomes heathy/unhealthy | [`EventTrigger`](./events#component) | -| `on.config` | Run a playbook when someone creates/updates/deletes a config item or changes its state | [`EventTrigger`](./events#config) | -| `on.webhook` | Run a playbook when someone calls a webhook | [`Webhook`](./webhooks) | -| `runsOn` | Which [runner](/docs/guide/playbooks/concepts/runners) (agent) to run the playbook on | [`[]Agent`](/docs/reference/types#agent) | -| `templatesOn` | Where the templating of actions occurs
For `host` the templating occurs on the mission control instance before sending to the agent
For `agent` the templating occurs on the agent/runner where there might be secrets not accessible by the primary instance. | `host` or `agent` | +| `timeout` | Maximum duration to let the playbook run before cancellation. Valid time units are "s", "m", "h", "d", "w", "y". Defaults to 30 minutes. | `string` | +| `on.canary` | Run a playbook when a health check fails or passes | [`EventTrigger`](./events#canary) | +| `on.component` | Run a playbook when a part becomes heathy/unhealthy | [`EventTrigger`](./events#component) | +| `on.config` | Run a playbook when someone creates/updates/deletes a config item or changes its state | [`EventTrigger`](./events#config) | +| `on.webhook` | Run a playbook when someone calls a webhook | [`Webhook`](./webhooks) | +| `runsOn` | Which [runner](/docs/guide/playbooks/concepts/runners) (agent) to run the playbook on | [`[]Agent`](/docs/reference/types#agent) | +| `templatesOn` | Where the templating of actions occurs
For `host` the templating occurs on the mission control instance before sending to the agent
For `agent` the templating occurs on the agent/runner where there might be secrets not accessible by the primary instance. | `host` or `agent` | | `checks` | Which health checks this playbook can run on | [`[]ResourceSelector`](/docs/reference/resource-selector) | | `configs` | Which config items this playbook can run on | [`[]ResourceSelector`](/docs/reference/resource-selector) | | `components` | Which parts this playbook can run on | [`[]ResourceSelector`](/docs/reference/resource-selector) | | `env` | Variables to lookup, available as `env` map in templating/filters | [[]EnvVar](/docs/reference/env-var) | -| `parameters` | Variables that users need to enter. Do not use parameters for sensitive values. | [`[]Parameter`](./parameters) | -| `actions` | Individual actions or steps to perform | [`[]Action`](#actions) | -| `approval` | Optional approvals required before a playbook runs | [`Approval`](#approvals) | +| `parameters` | Variables that users need to enter. Do not use parameters for sensitive values. | [`[]Parameter`](./parameters) | +| `actions` | Individual actions or steps to perform | [`[]Action`](#actions) | +| `approval` | Optional approvals required before a playbook runs | [`Approval`](#approvals) | ## Run @@ -73,22 +73,22 @@ scheme: 'string', ## Actions -| Field | Description | Scheme | Required | -| --------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | -------- | -| **`name`** | Step Name | `string` | `true` | -| `runsOn` | Which [runner](/docs/guide/playbooks/concepts/runners) (agent) to run the action on | [`[]Agent`](/docs/reference/types#agent) | | -| `templatesOn` | Where templating (and secret management) of actions occurs | `host` or `agent` | | -| `delay` | A delay before running the action e.g. `8h` | [`Duration`](/docs/reference/types#duration) or [CEL](/docs/reference/scripting/cel) with [Playbook Context](./context) | | -| `filter` | Conditionally run an action | [CEL](/docs/reference/scripting/cel) with [Playbook Context](./context) | | -| `timeout` | Timeout on this action. | [`Duration`](/docs/reference/types#duration) | | -| `azureDevopsPipeline` | Trigger a pipeline run | [AzureDevops](/docs/guide/playbooks/actions/azure_devops_pipeline) | | -| `exec` | Run a script e.g. to use `kubectl` or `aws` `CLIs` | [Exec](/docs/guide/playbooks/actions/exec) | | -| `github` | Trigger Github Action | [Github Action](/docs/guide/playbooks/actions/github) | | -| `gitops` | Update a git repository (directly or via pull request) | [Gitops](/docs/guide/playbooks/actions/gitops) | | -| `http` | Call an HTTP Endpoint | [Http](/docs/guide/playbooks/actions/http) | | -| `sql` | Execute a SQL query | [Sql](/docs/guide/playbooks/actions/sql) | | -| `pod` | Run a kubernetes pod. | [Pod](/docs/guide/playbooks/actions/pod) | | -| `notification` | Specify notification of action. | [Notification](/docs/guide/playbooks/actions/notification) | | +| Field | Description | Scheme | Required | +| --------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------- | +| **`name`** | Step Name | `string` | `true` | +| `runsOn` | Which [runner](/docs/guide/playbooks/concepts/runners) (agent) to run the action on | [`[]Agent`](/docs/reference/types#agent) | | +| `templatesOn` | Where templating (and secret management) of actions occurs | `host` or `agent` | | +| `delay` | A delay before running the action e.g. `8h` | [`Duration`](/docs/reference/types#duration) or [CEL](/docs/reference/scripting/cel) with [Playbook Context](./context) | | +| `filter` | Conditionally run an action | [CEL](/docs/reference/scripting/cel) with [Playbook Context](./context) | | +| `timeout` | Timeout on this action. | [`Duration`](/docs/reference/types#duration) | | +| `azureDevopsPipeline` | Trigger a pipeline run | [AzureDevops](/docs/guide/playbooks/actions/azure_devops_pipeline) | | +| `exec` | Run a script e.g. to use `kubectl` or `aws` `CLIs` | [Exec](/docs/guide/playbooks/actions/exec) | | +| `github` | Trigger Github Action | [Github Action](/docs/guide/playbooks/actions/github) | | +| `gitops` | Update a git repository (directly or via pull request) | [Gitops](/docs/guide/playbooks/actions/gitops) | | +| `http` | Call an HTTP Endpoint | [Http](/docs/guide/playbooks/actions/http) | | +| `sql` | Execute a SQL query | [Sql](/docs/guide/playbooks/actions/sql) | | +| `pod` | Run a kubernetes pod. | [Pod](/docs/guide/playbooks/actions/pod) | | +| `notification` | Specify notification of action. | [Notification](/docs/guide/playbooks/actions/notification) | | > Only 1 action should be specified per step diff --git a/mission-control/docs/reference/resource-selector.md b/mission-control/docs/reference/resource-selector.md index de9d4591..d0bc489b 100644 --- a/mission-control/docs/reference/resource-selector.md +++ b/mission-control/docs/reference/resource-selector.md @@ -10,5 +10,3 @@ import ResourceSelector from '@site/docs/snippets/\_resource-selector.mdx'; # Resource Selectors - - diff --git a/mission-control/docs/reference/views/_view.mdx b/mission-control/docs/reference/views/_view.mdx index bdd9c091..55509d87 100644 --- a/mission-control/docs/reference/views/_view.mdx +++ b/mission-control/docs/reference/views/_view.mdx @@ -1,4 +1,9 @@ :::note Validation Requirements -- **At least one query**: Views must define at least one named query +- **At least one query**: Views must define at least one named query (unless the view only renders `sections`) - **Output definition**: Views must have either `panels` or `columns` (or both) - **Merge requirement**: When using multiple queries with table output (columns only), you must provide a `merge` SQL query ::: @@ -83,10 +108,20 @@ Query definition for data sources. description: "Query configuration changes and audit data", scheme: "[ResourceSelector](/docs/reference/resource-selector)" }, + { + field: "sql", + description: "Run a SQL query against a configured connection (PostgreSQL, MySQL, SQL Server)", + scheme: "[SQL query](/mission-control/guide/views/queries/sql)" + }, { field: "prometheus", description: "Query time-series data from Prometheus", scheme: "[Prometheus Query](#prometheus-query)" + }, + { + field: "viewTableSelector", + description: "Read cached rows from another view table", + scheme: "[View Table Selector](#view-table-selector)" } ]}/> @@ -143,7 +178,7 @@ Column definition for view tables with data types and visualization properties. field: "type", required: true, description: "Data type that determines formatting and visualization", - scheme: "`string`, `number`, `boolean`, `datetime`, `duration`, `health`, `status`, `gauge`, `bytes`, `decimal`, `millicore`, `url`" + scheme: "`string`, `number`, `boolean`, `datetime`, `duration`, `health`, `status`, `gauge`, `bytes`, `decimal`, `millicore`, `url`, `badge`, `config_item`, `labels`" }, { field: "primaryKey", @@ -160,6 +195,31 @@ Column definition for view tables with data types and visualization properties. description: "Whether to hide the column from the UI _(default: false)_", scheme: "bool" }, + { + field: "filter", + description: "Enable column filtering in the UI. Options are populated from the cached table per request fingerprint _(type: multiselect; labels columns support key/value include-exclude filters)_", + scheme: "object" + }, + { + field: "icon", + description: "CEL expression that selects an icon per row (for tables and cards)", + scheme: "string" + }, + { + field: "url", + description: "Link to configs, other views, or custom URLs using CEL/Go templates", + scheme: "[ColumnURL](#column-urls)" + }, + { + field: "unit", + description: "Unit string appended to values (e.g., %, bytes, millicore)", + scheme: "string" + }, + { + field: "configItem", + description: "Options for `config_item` columns (choose which field to treat as the config ID)", + scheme: "ConfigItemColumnType" + }, { field: "for", description: "Reference to another column this column provides data for _(helper columns)_. Only applicable for type=url.", @@ -169,6 +229,11 @@ Column definition for view tables with data types and visualization properties. field: "gauge", description: "Configuration for gauge visualization _(required when type=gauge)_", scheme: "[GaugeConfig](#gauge-configuration)" + }, + { + field: "card", + description: "Card layout configuration for this column (position and accent highlighting)", + scheme: "CardConfig" } ]}/> @@ -196,7 +261,7 @@ Panel definition for interactive visualizations. field: "type", required: true, description: "Type of panel visualization", - scheme: "`piechart`, `table`, `number`, `gauge`" + scheme: "`piechart`, `table`, `text`, `number`, `duration`, `gauge`, `bargauge`, `timeseries`" }, { field: "query", @@ -218,6 +283,16 @@ Panel definition for interactive visualizations. field: "number", description: "Configuration for number panels", scheme: "[Panel Number Configuration](#panel-number-configuration)" + }, + { + field: "bargauge", + description: "Configuration for bar gauge panels", + scheme: "[Bar Gauge Configuration](#bar-gauge-configuration)" + }, + { + field: "timeseries", + description: "Configuration for timeseries panels", + scheme: "[Timeseries Configuration](#timeseries-configuration)" } ]}/> @@ -245,19 +320,17 @@ Configuration for gauge visualizations with thresholds and color coding. +#### Bar Gauge Configuration + +Configuration for bar gauge panels. + + + +#### Timeseries Configuration + +Configuration for timeseries panels (API-level; UI rendering in progress). + + + +### View Card Layout + +`display.card` controls the default card layout for tables with card positioning. Cards become the default view when `default: true` and at least one column defines `card.position` (or legacy `cardPosition`). Use `columns[].card.useForAccent` to color the card accent based on a column's value (status/health heuristics in the UI): + + + +### Config Tabs + +Use `display.plugins` to attach a view as a tab on config detail pages. The `configTab` selector chooses matching configs, and `variables` templating pulls values from the config into the view request. + +```yaml title="namespace.yaml" file=/modules/mission-control/fixtures/views/namespace.yaml {66-76} + +``` + +### Column Filters + +Enable `columns[].filter.type: multiselect` to expose server-side filters without re-running the view. Mission Control gathers distinct values from the cached Postgres table (scoped to the current `request_fingerprint`), builds dropdown options, and applies PostgREST filters. Labels columns support include/exclude tokens so you can target specific keys. + +```yaml title="ingress.yaml" file=/modules/mission-control/fixtures/views/ingress.yaml {8-48} + +``` + +### Column URLs + +`columns[].url` can link to Mission Control objects or external destinations: + +- `config`: CEL expression resolving to a config ID or search query (redirects to catalog page) +- `view`: Link to another view with optional query params built from CEL expressions +- `template`: Go template that renders a custom URL + +```yaml title="deployments.yaml" file=/modules/mission-control/fixtures/views/deployments.yaml {37-40,72-73} + +``` + +### Row Attributes and Grants + +Mission Control appends hidden, reserved columns to every view table: + +- `__row__attributes` (`row_attributes` type) stores per-column metadata generated during mapping: evaluated icons, URLs, gauge min/max overrides, and config item details (`id`, `type`, `class`, `health`, `status`). The UI uses these attributes to render icons, clickable links, and card accents. +- `__grants` (`grants` type) stores scope IDs computed for config queries so row-level security policies can filter PostgREST responses. + +You do not need to declare these columns, but they appear in the materialized Postgres table and PostgREST output. + +### View Table Selector + +Reference cached data from another view instead of re-querying sources. The selector matches views by `name`, `namespace`, or `labelSelector`, then treats their materialized tables as a query source. + +```yaml title="deployments-summary.yaml" file=/modules/mission-control/fixtures/views/deployments-summary.yaml {10-37} + +``` + +### Sections + +Render multiple views on one page. Section-only views can omit panels and columns; each section points at another view and is rendered with shared variable controls. + +```yaml title="workload-overview.yaml" file=/modules/mission-control/fixtures/views/workload-overview.yaml {6-24} +``` diff --git a/mission-control/package-lock.json b/mission-control/package-lock.json index 10b13fc7..12766962 100644 --- a/mission-control/package-lock.json +++ b/mission-control/package-lock.json @@ -182,6 +182,7 @@ "version": "5.21.0", "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.21.0.tgz", "integrity": "sha512-nZfgJH4njBK98tFCmCW1VX/ExH4bNOl9DSboxeXGgvhoL0fG1+4DDr/mrLe21OggVCQqHwXBMh6fFInvBeyhiQ==", + "peer": true, "dependencies": { "@algolia/client-common": "5.21.0", "@algolia/requester-browser-xhr": "5.21.0", @@ -320,6 +321,7 @@ "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", @@ -1974,6 +1976,7 @@ "url": "https://opencollective.com/csstools" } ], + "peer": true, "engines": { "node": ">=18" }, @@ -1995,6 +1998,7 @@ "url": "https://opencollective.com/csstools" } ], + "peer": true, "engines": { "node": ">=18" } @@ -2071,6 +2075,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2392,6 +2397,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -3313,6 +3319,7 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.7.0.tgz", "integrity": "sha512-GXg5V7kC9FZE4FkUZA8oo/NrlRb06UwuICzI6tcbzj0+TVgjq/mpUXXzSgKzMS82YByi4dY2Q808njcBCyy6tQ==", + "peer": true, "dependencies": { "@docusaurus/core": "3.7.0", "@docusaurus/logger": "3.7.0", @@ -4049,6 +4056,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz", "integrity": "sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==", + "peer": true, "dependencies": { "@types/mdx": "^2.0.0" }, @@ -4331,6 +4339,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -4803,6 +4812,7 @@ "node_modules/@types/react": { "version": "18.2.48", "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -5110,6 +5120,7 @@ "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5157,6 +5168,7 @@ "node_modules/ajv": { "version": "6.12.6", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5215,6 +5227,7 @@ "version": "5.21.0", "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.21.0.tgz", "integrity": "sha512-hexLq2lSO1K5SW9j21Ubc+q9Ptx7dyRTY7se19U8lhIlVMLCNXWCyQ6C22p9ez8ccX0v7QVmwkl2l1CnuGoO2Q==", + "peer": true, "dependencies": { "@algolia/client-abtesting": "5.21.0", "@algolia/client-analytics": "5.21.0", @@ -5515,6 +5528,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -6118,6 +6132,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -6352,9 +6367,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001755", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001755.tgz", - "integrity": "sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==", + "version": "1.0.30001756", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz", + "integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==", "funding": [ { "type": "opencollective", @@ -6878,6 +6893,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -7178,6 +7194,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -7267,6 +7284,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -15151,6 +15169,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -16450,6 +16469,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", @@ -17413,6 +17433,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -18298,6 +18319,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -18481,6 +18503,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -18543,6 +18566,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", + "peer": true, "dependencies": { "@types/react": "*" }, @@ -18636,6 +18660,7 @@ "node_modules/react-router": { "version": "5.3.4", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", @@ -20707,6 +20732,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", "dev": true, + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -20997,6 +21023,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", + "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -22079,6 +22106,7 @@ "version": "5.97.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", @@ -22179,6 +22207,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -22305,6 +22334,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -22841,6 +22871,7 @@ "version": "5.21.0", "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.21.0.tgz", "integrity": "sha512-nZfgJH4njBK98tFCmCW1VX/ExH4bNOl9DSboxeXGgvhoL0fG1+4DDr/mrLe21OggVCQqHwXBMh6fFInvBeyhiQ==", + "peer": true, "requires": { "@algolia/client-common": "5.21.0", "@algolia/requester-browser-xhr": "5.21.0", @@ -22943,6 +22974,7 @@ "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "peer": true, "requires": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", @@ -23984,12 +24016,14 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "peer": true, "requires": {} }, "@csstools/css-tokenizer": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", - "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==" + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "peer": true }, "@csstools/media-query-list-parser": { "version": "4.0.2", @@ -24016,6 +24050,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "peer": true, "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -24146,6 +24181,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "peer": true, "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -24637,6 +24673,7 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.7.0.tgz", "integrity": "sha512-GXg5V7kC9FZE4FkUZA8oo/NrlRb06UwuICzI6tcbzj0+TVgjq/mpUXXzSgKzMS82YByi4dY2Q808njcBCyy6tQ==", + "peer": true, "requires": { "@docusaurus/core": "3.7.0", "@docusaurus/logger": "3.7.0", @@ -25194,6 +25231,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz", "integrity": "sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==", + "peer": true, "requires": { "@types/mdx": "^2.0.0" } @@ -25360,6 +25398,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "peer": true, "requires": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -25752,6 +25791,7 @@ }, "@types/react": { "version": "18.2.48", + "peer": true, "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -26044,7 +26084,8 @@ "acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==" + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "peer": true }, "acorn-jsx": { "version": "5.3.2", @@ -26072,6 +26113,7 @@ }, "ajv": { "version": "6.12.6", + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -26113,6 +26155,7 @@ "version": "5.21.0", "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.21.0.tgz", "integrity": "sha512-hexLq2lSO1K5SW9j21Ubc+q9Ptx7dyRTY7se19U8lhIlVMLCNXWCyQ6C22p9ez8ccX0v7QVmwkl2l1CnuGoO2Q==", + "peer": true, "requires": { "@algolia/client-abtesting": "5.21.0", "@algolia/client-analytics": "5.21.0", @@ -26293,6 +26336,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -26730,6 +26774,7 @@ "version": "4.24.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "peer": true, "requires": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -26881,9 +26926,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001755", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001755.tgz", - "integrity": "sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==" + "version": "1.0.30001756", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz", + "integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==" }, "caw": { "version": "2.0.1", @@ -27220,6 +27265,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -27391,6 +27437,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "peer": true, "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -27430,6 +27477,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -32393,6 +32441,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -33215,6 +33264,7 @@ "version": "8.4.49", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "peer": true, "requires": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", @@ -33694,6 +33744,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "peer": true, "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -34248,6 +34299,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, "requires": { "loose-envify": "^1.1.0" } @@ -34373,6 +34425,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "peer": true, "requires": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -34419,6 +34472,7 @@ "version": "npm:@docusaurus/react-loadable@6.0.0", "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", + "peer": true, "requires": { "@types/react": "*" } @@ -34482,6 +34536,7 @@ }, "react-router": { "version": "5.3.4", + "peer": true, "requires": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", @@ -35927,6 +35982,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", "dev": true, + "peer": true, "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -36152,6 +36208,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", + "peer": true, "requires": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -36887,6 +36944,7 @@ "version": "5.97.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "peer": true, "requires": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", @@ -36971,6 +37029,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -37058,6 +37117,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", diff --git a/modules/canary-checker b/modules/canary-checker index 1df9e0d7..379f28f0 160000 --- a/modules/canary-checker +++ b/modules/canary-checker @@ -1 +1 @@ -Subproject commit 1df9e0d7e9eac5e8ecf89535d08029ec05351544 +Subproject commit 379f28f0ed6de8a2575d0b833ec7ce2031966a32 diff --git a/modules/config-db b/modules/config-db index 218a9c93..f26106b2 160000 --- a/modules/config-db +++ b/modules/config-db @@ -1 +1 @@ -Subproject commit 218a9c93278ab274329912ecda3037db39d95f23 +Subproject commit f26106b24ae909a29d588d483374923abcfb6f4e diff --git a/modules/duty b/modules/duty index bb543d7d..744f283f 160000 --- a/modules/duty +++ b/modules/duty @@ -1 +1 @@ -Subproject commit bb543d7d69591e14e58f4ad8505658524448e4d2 +Subproject commit 744f283f8783e607bd6a78d2477ebf1e998dd080 diff --git a/modules/mission-control b/modules/mission-control index 6bbd14cf..b76b2b28 160000 --- a/modules/mission-control +++ b/modules/mission-control @@ -1 +1 @@ -Subproject commit 6bbd14cfb180c52b8d6087000c75fb132551dd4e +Subproject commit b76b2b284613bc987064cc8d2e3eba0f4a2ba8ab diff --git a/modules/mission-control-chart b/modules/mission-control-chart index 0a7c6f0d..2a91080d 160000 --- a/modules/mission-control-chart +++ b/modules/mission-control-chart @@ -1 +1 @@ -Subproject commit 0a7c6f0d20711126e61a2812f5a77806988d75bd +Subproject commit 2a91080def4f14679f38331c6b0fe05f610380cd diff --git a/modules/mission-control-registry b/modules/mission-control-registry index d2833707..3acb35cf 160000 --- a/modules/mission-control-registry +++ b/modules/mission-control-registry @@ -1 +1 @@ -Subproject commit d28337071d1fcad5f22401f66dc170e513dfc0d2 +Subproject commit 3acb35cfda6c27d637efe8fd8e38bf7f07bb0110 diff --git a/prompts/blog.md b/prompts/blog.md index b817d0aa..54804c39 100644 --- a/prompts/blog.md +++ b/prompts/blog.md @@ -2,7 +2,7 @@ Write a 1250-1500 introductor blog post for a DevOps / Platform Engineering focu The tone should be developer-friendly, technical and informative without sounding salesy or promotional. -The writing should have no fluff, use short punchy sentences, avoid buzzwords and speak like a senior engineer would. +The writing should have no fluff, use short punchy sentences, avoid buzzwords and speak like a senior engineer would. The purpose of this copy is to generate interest in a new approach to a feature, educate DevOps engineers, increase awareness and reduce friction to trial @@ -16,7 +16,7 @@ Format the output in raw markdown suitable for copy and pasting into vscode. Write a blog post on Flanksource MIssion Control approach to AIops, primarily building a real-time and update to mirror state of cloud resources that can be queried rapidly, plus an advanced graph that builds relationships between resources e.g. Cloudformation -> Auto Scaling Group > EC2 instance and then layers on soft relationsyhips like ebs volumes, subnets, IAM poilcies - For Kubernetes it understands Flux and Gitops being able to build a graph of a Flux Kustomization object creating a HelmRelease CRD, which then creates a deployment -> replicset -> pod and then layeying relationships like services, nodes, PVC, ingress, etc.. -State based alerting (i.e. whene resource self-report failure) and traditioanl alerts from APM tools trigger playbooks that can then proactively collect infomation in a distrubuted fashion from agents deployed closest to the data, the graph, changes to the graph resources, events and pro-acrtive playboks are then fed into the model which tan the recommend futher playbooks to execute. +State based alerting (i.e. whene resource self-report failure) and traditioanl alerts from APM tools trigger playbooks that can then proactively collect infomation in a distrubuted fashion from agents deployed closest to the data, the graph, changes to the graph resources, events and pro-acrtive playboks are then fed into the model which tan the recommend futher playbooks to execute. This is advantage as acess to systems is pushed down to agents who can use secrets like pod identity and service accounts to collect duta, new agent actions are use to create with YAML based playbooks. @@ -26,30 +26,27 @@ contrasting metrics vs state driven alerting, store with concepts such as RED an Highlight the drawbacks of the canary-checker approach that is poll-based and does not scale very well and demononstrate how https://github.com/flanksource/config-db takes this one step further by using a state driven approach that watches for changes to cloud resources, and then fires events when the state becomes unhealthy. - - is more scalable and can be used to monitor the health of a cluster or application in real-time. +is more scalable and can be used to monitor the health of a cluster or application in real-time. Optionally, include {optional elements} (e.g. a strong CTA, technical example, code snippet, customer proof, comparison table). - Act as a technical blog writer targeting devops and platform engineers working with Kubernetes, GitOps, Helm and Flux, when editing and rewriting content follow these instructions strictly: 1. Use the following outline for the blog: - * Introduction - introduce the topic of the blog with a pain point or teaser - * Background - Describe the context and initial challenges. - * Step by step guide - * Common Pitfalls - Highlight common mistakes and how to avoid them and add use-cases that are not a good fiit - * Conclustion - Offer final thoughts and potential future implications. + - Introduction - introduce the topic of the blog with a pain point or teaser + - Background - Describe the context and initial challenges. + - Step by step guide + - Common Pitfalls - Highlight common mistakes and how to avoid them and add use-cases that are not a good fiit + - Conclustion - Offer final thoughts and potential future implications. 2. Write at a Grade 10 level 3. Use clear, concise simple language, even when explaining complex topics. 4. Bias toward short sentences. 5. Mix and match lists and paragraphs 6. Do not use any salesy or marketing terms, Do not use adverbs 7. Use MDX formatting -8. Precede every command with an explanation of what the command does. After the command, provide additional details about the command, such as what the arguments do and why your reader is using them. -9. Explicitly tell the user to create or open each file you’ll have them use. +8. Precede every command with an explanation of what the command does. After the command, provide additional details about the command, such as what the arguments do and why your reader is using them. +9. Explicitly tell the user to create or open each file you’ll have them use. 10. Like commands, always introduce a file or script by describing its general purpose, then explain any changes that the reader will be making in the file. Without these explanations, readers won’t be able to customize, update, or troubleshoot issues in the long run. 11. If you’re asking the reader to write code, follow the same approach for commands: introduce the code block with a high-level explanation of what it does. Then show the code, and then call out any important details. 12. Do not use the term "this document", when referring to the system or product being documented always use "Mission Control" 13. Ensure all examples and use cases are relevant - diff --git a/prompts/style.md b/prompts/style.md index e95a2c4c..3adc8093 100644 --- a/prompts/style.md +++ b/prompts/style.md @@ -5,26 +5,31 @@ Follow these strict rules: 1. Avoid adverbs and complex language ## Formating + - Format all output using MDX (markdowon) - Format code and examples using this example: ```yaml title=some-title.yaml + ``` + - Do not remove any "```" or "//highlight-next-line" text - Follow standard markdown rules provided by markdownlint - ## Verb Tense + - Use present tense verbs instead of future tense. - Say "this happens" rather than "this will happen." - Avoid words like "will," "shall," "won't," "shan't," and contractions with "'ll." ## Voice + - Do not use first person (I, me, my, mine, we, our). - Avoid phrases like "I'd," "I'll," "I'm," and "I've." - Use passive voice sparingly. Active voice is generally clearer. ## Inclusive Language + - Use considerate language that respects all readers. - Use "they" as a singular pronoun instead of "he/she" or "s/he." - Avoid terms that might be insensitive: @@ -35,11 +40,13 @@ Follow these strict rules: - Avoid phrases like "fall on deaf ears" or "blind spot" ## Tone + - Don't assume success with statements like "congratulations," "that's it," or "you did it." - Avoid condescending terms like "obvious," "obviously," "simple," "simply," "easy," "easily," "of course," "clearly," or "everyone knows." - Don't add "-ly" to ordinal numbers (avoid "firstly," "secondly," etc.) ## Clarity and Brevity + - Use simple words instead of complex ones. - Avoid foreign phrases like "i.e.," "viz.," or "ergo." - Eliminate wordiness and redundant phrases: diff --git a/scripts/README.md b/scripts/README.md index 380c5dc0..7f99d17b 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -50,4 +50,4 @@ The script resolves code imports in two formats: 1. Regular paths: `file="../examples/config.yaml"` 2. Root paths: `file=/modules/examples/config.yaml"` -Failed imports will be noted in the output with error messages. \ No newline at end of file +Failed imports will be noted in the output with error messages.