From c4359ba1bcef9bfac9292289f189238eb23f8ef3 Mon Sep 17 00:00:00 2001 From: Geoffrey Israel Date: Tue, 20 Feb 2024 10:58:33 +0100 Subject: [PATCH] docs: document how to write create new keptnmetricsprovider (#2939) Signed-off-by: Geoffrey Israel Signed-off-by: odubajDT <93584209+odubajDT@users.noreply.github.com> Co-authored-by: Meg McRoberts Co-authored-by: RealAnna <89971034+RealAnna@users.noreply.github.com> Co-authored-by: odubajDT <93584209+odubajDT@users.noreply.github.com> --- .../software/add-new-metric-provider.md | 124 ++++++++++++++++++ .../example-code/new-provider-function.go | 13 ++ .../example-code/placeholder-code-example.go | 45 +++++++ mkdocs.yml | 1 + 4 files changed, 183 insertions(+) create mode 100644 docs/docs/contribute/software/add-new-metric-provider.md create mode 100644 docs/docs/contribute/software/assets/example-code/new-provider-function.go create mode 100755 docs/docs/contribute/software/assets/example-code/placeholder-code-example.go diff --git a/docs/docs/contribute/software/add-new-metric-provider.md b/docs/docs/contribute/software/add-new-metric-provider.md new file mode 100644 index 0000000000..d0dbdfff42 --- /dev/null +++ b/docs/docs/contribute/software/add-new-metric-provider.md @@ -0,0 +1,124 @@ +--- +comments: true +--- + +# Add a metrics provider + +The +[KeptnMetric](../../guides/evaluatemetrics.md) +feature works with almost any data platform +but Keptn requires that a metrics provider be defined +for any data platform it uses as a data source. +This guide gives instructions for creating a new metrics provider. +For these instructions, +we define a sample provider called `placeholder`. +The steps to create your own metrics provider are: + + +1. **Fork and clone** the +[Keptn repository](https://github.com/keptn/lifecycle-toolkit). + For more information, see + [Fork and clone the repository](https://keptn.sh/stable/docs/contribute/general/git/fork-clone/). + +2. **Define the Provider Type:** In the `metrics-operator/controllers/common/providers/common.go` file, + define the constant `KeptnPlaceholderProviderType`. + In our example we use `"placeholder"`. + + ```go + const KeptnPlaceholderProviderType = "placeholder" + ``` + +3. **Implement the Provider:** Create a new folder inside the + [metrics-operator/controllers/common/providers](). + Use the provider name as the name of the folder. + This name defines the string used to identify this provider + in the `spec.type` field of the + [KeptnMetricsProvider](../../reference/crd-reference/metricsprovider.md) + resource. + In this example, the folder is named `placeholder`. + Create a new Go package for the placeholder provider in that folder. + This package should contain a `struct` that implements the `KeptnSLIProvider` interface. + To fully implement the `KeptnSLIProvider` interface, it's necessary to implement the following functions. + + * `EvaluateQuery`(Fetches metric values from the provider) + * This function fetches metric values based on the provided + metric query from the provider. + It evaluates the query and returns the metric values + along with any additional data if required. + * It takes as input a [KeptnMetric](../../reference/crd-reference/metric.md) + and [KeptnMetricsProvider](../../reference/crd-reference/metricsprovider.md) + * `EvaluateQueryForStep`(Fetches metric values with step interval from the provider) + * This function fetches metric values with a specified step interval from the placeholder provider. + It takes into account the metric query and the step interval provided, executes the query, + and returns the metric values along with any additional data if required. + * It takes as input a [KeptnMetric](../../reference/crd-reference/metric.md) + and [KeptnMetricsProvider](../../reference/crd-reference/metricsprovider.md) + * `FetchAnalysisValue`(Fetches analysis values from the provider) functions. + * This function fetches analysis values based on the provided query and time range from the + provider. + It evaluates the query within the specified time range and returns the analysis + values along with any additional data if required. + * It takes as input an [Analysis](../../reference/crd-reference/analysis.md), + resource that contains a `query` and a + [KeptnMetricsProvider](../../reference/crd-reference/metricsprovider.md) resource. + + You can follow other existing implementations, + such as [prometheus.go](https://github.com/keptn/lifecycle-toolkit/blob/main/metrics-operator/controllers/common/providers/prometheus/prometheus.go), + as an example. + + Each of the three functions expects a string containing a float value in it. + But for example purposes we returned some of the data accessible in the function. + + Below is an example of a placeholder provider implementation. + + ```go + {% include "./assets/example-code/placeholder-code-example.go" %} + ``` + + > **Note** Refer to the documentation of the + > [KeptnMetric](../../reference/crd-reference/metric.md) + > and + > [Analysis](../../reference/crd-reference/analysis.md) + > resources + > to understand what data should be retrieved from the methods inputs to compute accurate results. + +4. **Instantiate the Provider** in the `providers.NewProvider` function + in the `metrics-operator/controllers/common/providers/provider.go` file. + add a case for the `KeptnPlaceholderProviderType`. + Instantiate the placeholder provider struct and return it. + + ```go + {% include "./assets/example-code/new-provider-function.go" %} + ``` + +5. **Update the validation webhook and crd config:** To update the validation webhook and crd config of the metrics operator. + Add the provider name next to last providers on this + [line](https://github.com/keptn/lifecycle-toolkit/blob/main/metrics-operator/api/v1beta1/keptnmetricsprovider_types.go#L29) + to look like this + + `// +kubebuilder:validation:Pattern:=prometheus|dynatrace|datadog|dql|placeholder`. + + In the metric-operator directory run `make generate manifests` to update the metrics-operator crd config + Then modify the helm chart and the helm chart crd validation to match the update in the metrics-operator crd config + +6. **Add Test Cases:** + * Write a unit test to validate your implementation at the function level. + Unit tests ensure that individual + functions behave as expected and meet their functional requirements. + + * Include a Chainsaw test to validate the behavior of Kubernetes resources managed by your code. + Chainsaw tests simulate real-world scenarios and interactions within a Kubernetes cluster, ensuring + the correctness of your Kubernetes configurations and deployments. + + Below are the steps for adding an integration test. + + * In the directory `test/chainsaw/testmetrics`, create a folder `metrics-provider-placeholder` in our case. + * Within the `keptn-metrics-placeholder` folder, create YAML file `00-install.yaml`. + * `00-install.yaml` contains a sample configuration that installs a valid `KeptnMetricsProvider` + in our case `placeholder` and it also defines a sample `KeptnMetric` configuration + representing a valid use case, while. + * Create a file named `chainsaw-test.yaml` and define the steps for the integration test in chainsaw-test.yaml. + + > For more information checkout [an already existing integration test](https://github.com/keptn/lifecycle-toolkit/tree/main/test/chainsaw/testmetrics/metrics-provider) + + diff --git a/docs/docs/contribute/software/assets/example-code/new-provider-function.go b/docs/docs/contribute/software/assets/example-code/new-provider-function.go new file mode 100644 index 0000000000..c67cd9067c --- /dev/null +++ b/docs/docs/contribute/software/assets/example-code/new-provider-function.go @@ -0,0 +1,13 @@ +// Inside the providers package + +// NewProvider function +func NewProvider(providerType string, log logr.Logger, k8sClient client.Client) (KeptnSLIProvider, error) { + switch strings.ToLower(providerType) { + case KeptnPlaceholderProviderType: + return &placeholder.KeptnPlaceholderProvider{ + Log: log, + HttpClient: http.Client{}, + }, nil + // Other cases... + } +} diff --git a/docs/docs/contribute/software/assets/example-code/placeholder-code-example.go b/docs/docs/contribute/software/assets/example-code/placeholder-code-example.go new file mode 100755 index 0000000000..991699a826 --- /dev/null +++ b/docs/docs/contribute/software/assets/example-code/placeholder-code-example.go @@ -0,0 +1,45 @@ +package placeholder + +import ( + "context" + "fmt" + "net/http" + "time" + + "github.com/go-logr/logr" + metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1beta1" +) + +type KeptnPlaceholderProvider struct { + Log logr.Logger + HttpClient http.Client +} + +func (d *KeptnPlaceholderProvider) FetchAnalysisValue(ctx context.Context, query string, analysis metricsapi.Analysis, provider *metricsapi.KeptnMetricsProvider) (string, error) { + return fmt.Sprintf("placeholder provider FetchAnalysisValue was called with query %s from %d to %d", query, analysis.GetFrom().Unix(), analysis.GetTo().Unix()), nil +} + +func (d *KeptnPlaceholderProvider) EvaluateQuery(ctx context.Context, metric metricsapi.KeptnMetric, provider metricsapi.KeptnMetricsProvider) (string, []byte, error) { + return fmt.Sprintf("placeholder provider EvaluateQuery was called with query %s", metric.Spec.Query), nil, nil +} + +func (d *KeptnPlaceholderProvider) EvaluateQueryForStep(ctx context.Context, metric metricsapi.KeptnMetric, provider metricsapi.KeptnMetricsProvider) ([]string, []byte, error) { + fromTime, toTime, stepInterval, err := getTimeRangeForStep(metric) + if err != nil { + return nil, nil, err + } + result := fmt.Sprintf("placeholder provider EvaluateQueryForStep was called with query %s from %d to %d at an interval %d", metric.Spec.Query, fromTime, toTime, stepInterval) + return []string{result}, nil, nil +} + +func getTimeRangeForStep(metric metricsapi.KeptnMetric) (int64, int64, int64, error) { + intervalDuration, err := time.ParseDuration(metric.Spec.Range.Interval) + if err != nil { + return 0, 0, 0, err + } + stepDuration, err := time.ParseDuration(metric.Spec.Range.Step) + if err != nil { + return 0, 0, 0, err + } + return time.Now().Add(-intervalDuration).Unix(), time.Now().Unix(), stepDuration.Milliseconds(), nil +} diff --git a/mkdocs.yml b/mkdocs.yml index 53b2f69b1c..5a36356e42 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -202,6 +202,7 @@ nav: - Software contributions: - docs/contribute/software/index.md - Software development environment: docs/contribute/software/dev-environ.md + - Add a metrics provider: docs/contribute/software/add-new-metric-provider.md - Documentation contributions: - docs/contribute/docs/index.md - Contribution guidelines for documentation: docs/contribute/docs/contrib-guidelines-docs.md