diff --git a/.golangci.yml b/.golangci.yml index 7bc00ac..45621df 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,107 +1,13 @@ -linters-settings: - errcheck: - # report about not checking of errors in type assetions: `a := b.(MyStruct)`; - # default is false: such cases aren't reported by default. - check-type-assertions: false - - # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; - # default is false: such cases aren't reported by default. - check-blank: false - - # [deprecated] comma-separated list of pairs of the form pkg:regex - # the regex is used to ignore names within pkg. (default "fmt:.*"). - # see https://github.com/kisielk/errcheck#the-deprecated-method for details - exclude-functions: - - fmt:.* - - io/ioutil:^Read.* - - govet: - # report about shadowed variables - disable: - - shadow - gofmt: - # simplify code: gofmt with `-s` option, true by default - simplify: true - - goimports: - # put imports beginning with prefix after 3rd-party packages; - # it's a comma-separated list of prefixes - local-prefixes: github.com/SAP/metrics-operator - - gocyclo: - # minimal code complexity to report, 30 by default (but we recommend 10-20) - min-complexity: 10 - - dupl: - # tokens count to trigger issue, 150 by default - threshold: 100 - - goconst: - # minimal length of string constant, 3 by default - min-len: 3 - # minimal occurrences count to trigger, 3 by default - min-occurrences: 5 - - lll: - # Max line length, lines longer will be reported. - # '\t' is counted as 1 character by default, and can be changed with the tab-width option. - # Default: 120. - line-length: 120 - # Tab width in spaces. - # Default: 1 - tab-width: 1 - - unused: - # treat code as a program (not a library) and report unused exported identifiers; default is false. - # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find funcs usages. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - exported-fields-are-used: false - - unparam: - # Inspect exported functions, default is false. Set to true if no external program/library imports your code. - # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find external interfaces. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: false - - nakedret: - # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 - max-func-lines: 30 - - prealloc: - # XXX: we don't recommend using this linter before doing performance profiling. - # For most programs usage of prealloc will be a premature optimization. - - # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. - # True by default. - simple: true - range-loops: true # Report preallocation suggestions on range loops, true by default - for-loops: false # Report preallocation suggestions on for loops, false by default - - gocritic: - # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint` run to see all tags and checks. - # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". - enabled-tags: - - performance - - settings: # settings passed to gocritic - captLocal: # must be valid enabled check name - paramsOnly: true - rangeValCopy: - sizeThreshold: 32 +version: "2" linters: enable: - - gosimple - staticcheck - unused - govet - gocyclo - gocritic - goconst - - goimports - - gofmt # We enable this as well as goimports for its simplify mode. - prealloc - revive - unconvert @@ -109,75 +15,146 @@ linters: - nakedret disable: - nilnesserr - presets: - - bugs - - unused - fast: false - + settings: + errcheck: + # report about not checking of errors in type assetions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: false + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: false + + # [deprecated] comma-separated list of pairs of the form pkg:regex + # the regex is used to ignore names within pkg. (default "fmt:.*"). + # see https://github.com/kisielk/errcheck#the-deprecated-method for details + exclude-functions: + - fmt:.* + - io/ioutil:^Read.* + + govet: + # report about shadowed variables + disable: + - shadow + gocyclo: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 10 + + dupl: + # tokens count to trigger issue, 150 by default + threshold: 100 + + goconst: + # minimal length of string constant, 3 by default + min-len: 3 + # minimal occurrences count to trigger, 3 by default + min-occurrences: 5 + + lll: + # Max line length, lines longer will be reported. + # '\t' is counted as 1 character by default, and can be changed with the tab-width option. + # Default: 120. + line-length: 120 + # Tab width in spaces. + # Default: 1 + tab-width: 1 + + unused: + # treat code as a program (not a library) and report unused exported identifiers; default is false. + # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find funcs usages. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + exported-fields-are-used: false + + unparam: + # Inspect exported functions, default is false. Set to true if no external program/library imports your code. + # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find external interfaces. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + + nakedret: + # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 + max-func-lines: 30 + + prealloc: + # XXX: we don't recommend using this linter before doing performance profiling. + # For most programs usage of prealloc will be a premature optimization. + + # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. + # True by default. + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + + gocritic: + # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint` run to see all tags and checks. + # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". + enabled-tags: + - performance + + settings: # settings passed to gocritic + captLocal: # must be valid enabled check name + paramsOnly: true + rangeValCopy: + sizeThreshold: 32 + + exclusions: + generated: strict + rules: + # Exclude some linters from running on tests files. + - path: _test(ing)?\.go + linters: + - gocyclo + - errcheck + - dupl + - gosec + - scopelint + - unparam + - revive + + # Ease some gocritic warnings on test files. + - path: _test\.go + text: "(unnamedResult|exitAfterDefer)" + linters: + - gocritic + + # These are performance optimisations rather than style issues per se. + # They warn when function arguments or range values copy a lot of memory + # rather than using a pointer. + - text: "(hugeParam|rangeValCopy):" + linters: + - gocritic + + # This "TestMain should call os.Exit to set exit code" warning is not clever + # enough to notice that we call a helper method that calls os.Exit. + - text: "SA3000:" + linters: + - staticcheck + + - text: "k8s.io/api/core/v1" + linters: + - goimports + + # This is a "potential hardcoded credentials" warning. It's triggered by + # any variable with 'secret' in the same, and thus hits a lot of false + # positives in Kubernetes land where a Secret is an object type. + - text: "G101:" + linters: + - gosec + - gas + + # This is an 'errors unhandled' warning that duplicates errcheck. + - text: "G104:" + linters: + - gosec + - gas + # ease package comments rule + - text: "package-comments:" + linters: + - revive issues: - exclude-files: - - 'zz_generated.*\.go' - # Excluding configuration per-path and per-linter - exclude-rules: - # Exclude some linters from running on tests files. - - path: _test(ing)?\.go - linters: - - gocyclo - - errcheck - - dupl - - gosec - - scopelint - - unparam - - revive - - # Ease some gocritic warnings on test files. - - path: _test\.go - text: "(unnamedResult|exitAfterDefer)" - linters: - - gocritic - - # These are performance optimisations rather than style issues per se. - # They warn when function arguments or range values copy a lot of memory - # rather than using a pointer. - - text: "(hugeParam|rangeValCopy):" - linters: - - gocritic - - # This "TestMain should call os.Exit to set exit code" warning is not clever - # enough to notice that we call a helper method that calls os.Exit. - - text: "SA3000:" - linters: - - staticcheck - - - text: "k8s.io/api/core/v1" - linters: - - goimports - - # This is a "potential hardcoded credentials" warning. It's triggered by - # any variable with 'secret' in the same, and thus hits a lot of false - # positives in Kubernetes land where a Secret is an object type. - - text: "G101:" - linters: - - gosec - - gas - - # This is an 'errors unhandled' warning that duplicates errcheck. - - text: "G104:" - linters: - - gosec - - gas - # ease package comments rule - - text: "package-comments:" - linters: - - revive - - # Independently from option `exclude` we use default exclude patterns, - # it can be disabled by this option. To list all - # excluded by default patterns execute `golangci-lint run --help`. - # Default value for this option is true. - exclude-use-default: false - # Show only new issues: if there are unstaged changes or untracked files, # only those changes are analyzed, else only changes in HEAD~ are analyzed. # It's a super-useful option for integration of golangci-lint into existing @@ -192,5 +169,15 @@ issues: # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. max-same-issues: 0 +formatters: + enable: + - gofmt + - goimports + settings: + + gofmt: + # simplify code: gofmt with `-s` option, true by default + simplify: true + run: timeout: 10m diff --git a/Makefile b/Makefile index 208968b..00d868b 100644 --- a/Makefile +++ b/Makefile @@ -158,7 +158,7 @@ GOLANGCILINT ?= $(LOCALBIN)/golangci-lint ## Tool Versions KUSTOMIZE_VERSION ?= v5.4.1 CONTROLLER_TOOLS_VERSION ?= v0.17.2 -GOLANGCILINT_VERSION ?= v1.64.8 +GOLANGCILINT_VERSION ?= v2.0.2 .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. @@ -289,7 +289,7 @@ $(GOLANGCILINT): $(LOCALBIN) echo "$(LOCALBIN)/golangci-lint version is not expected $(GOLANGCILINT_VERSION). Removing it before installing."; \ rm -rf $(LOCALBIN)/golangci-lint; \ fi - test -s $(golangci-lint)/golangci-lint || GOBIN=$(LOCALBIN) GO111MODULE=on go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCILINT_VERSION) + test -s $(golangci-lint)/golangci-lint || GOBIN=$(LOCALBIN) GO111MODULE=on go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCILINT_VERSION) .PHONY: lint diff --git a/PROJECT b/PROJECT new file mode 100644 index 0000000..847b708 --- /dev/null +++ b/PROJECT @@ -0,0 +1,81 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html +domain: metrics.cloud.sap +layout: +- go.kubebuilder.io/v4 +projectName: metrics-operator +repo: github.com/SAP/metrics-operator +resources: +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: metrics.cloud.sap + kind: Metric + path: github.com/SAP/metrics-operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: metrics.cloud.sap + kind: ManagedMetric + path: github.com/SAP/metrics-operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: metrics.cloud.sap + kind: RemoteClusterAccess + path: github.com/SAP/metrics-operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: metrics.cloud.sap + kind: SingleMetric + path: github.com/SAP/metrics-operator/api/v1beta1 + version: v1beta1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: metrics.cloud.sap + kind: CompoundMetric + path: github.com/SAP/metrics-operator/api/v1beta1 + version: v1beta1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: metrics.cloud.sap + kind: FederatedMetric + path: github.com/SAP/metrics-operator/api/v1beta1 + version: v1beta1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: metrics.cloud.sap + kind: ClusterAccess + path: github.com/SAP/metrics-operator/api/v1beta1 + version: v1beta1 +- api: + crdVersion: v1 + namespaced: true + domain: metrics.cloud.sap + kind: FederatedClusterAccess + path: github.com/SAP/metrics-operator/api/v1beta1 + version: v1beta1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: metrics.cloud.sap + kind: FederatedManagedMetric + path: github.com/SAP/metrics-operator/api/v1beta1 + version: v1beta1 +version: "3" diff --git a/README.md b/README.md index c6362db..1dfde6a 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,283 @@ [![REUSE status](https://api.reuse.software/badge/github.com/SAP/metrics-operator)](https://api.reuse.software/info/github.com/SAP/metrics-operator) -# metrics-operator +# Metrics Operator -## About this project +The Metrics Operator is a powerful tool designed to monitor and provide insights into the state, usage, patterns, and trends of distributed systems and their associated components. -This project enables insights into the state, usage, patterns and trends of distributed systems with particular focus on the k8s clusters (one or many) and their associated components. +## Table of Contents -## Requirements and Setup +- [Key Features](#key-features) +- [Installation](#installation) +- [Usage](#usage) +- [RBAC Configuration](#rbac-configuration) +- [Remote Cluster Access](#remote-cluster-access) +- [Data Sink Integration](#data-sink-integration) -*Insert a short description what is required to get your project running...* +## Key Features + +- **Comprehensive Resource Tracking**: Quantifies and catalogs various resource types, providing a holistic view of resource distribution and utilization. +- **Multi-dimensional Analysis**: Examines specific attributes and dimensions of resources, generating nuanced metrics for deeper understanding of system behavior. +- **Comparative Analytics**: Enables side-by-side analysis of different resource configurations, highlighting patterns and potential imbalances in resource allocation. +- **Custom Component Focus**: Tailored to monitor and analyze complex, custom-defined resources across your infrastructure. +- **Predictive Insights**: Aggregates data over time to identify emerging trends, supporting data-driven decision making for future system enhancements. +- **Strategic Decision Support**: Offers data-backed insights to guide product evolution. +- **Customizable Alerting System**: Allows defining alerts based on specific metric thresholds, enabling proactive response to potential issues or significant changes in system state. + +## Installation + +### Prerequisites + +1. Create a namespace for the Metrics Operator. +2. Create a secret containing the credentials for the artifactory (read-only) in the operator's namespace. +3. Create a secret containing the data sink credentials in the operator's namespace. + +### Deployment + +Deploy the Metrics Operator using the Helm chart: + +```bash +helm upgrade --install co-metrics-operator deploy-releases-hyperspace-helm/co-metrics-operator \ + --namespace \ + --create-namespace \ + --set imagePullSecrets[0].name= \ + --version= +``` + +Replace ``, ``, and `` with appropriate values. + +## Usage + + +### Single Metric +To create a basic metric, deploy a `SingleMetric` resource in your desired namespace. The Metrics Operator will pick up the resource and start monitoring it, periodically sending data points to the configured data sink. + +Example `SingleMetric` resource: + +```yaml +apiVersion: metrics.cloud.sap/v1beta1 +kind: SingleMetric +metadata: + name: single-pod +spec: + name: single-metric-pods + description: Pods + target: + kind: Pod + group: "" + version: v1 + frequency: 1 # in minutes +--- +``` + +Apply the metric: + +```bash +kubectl apply -f singlemetric.yaml +``` + +### Compound Metric + +Compound metrics have additional capabilities, such as projections. Projections allow you to extract specific fields from the target resource and include them in the metric data. +This can be useful for tracking additional dimensions of the resource, such as fields, labels or annotations. It uses the dot notation to access nested fields. +The projections are then translated to dimensions in the metric. + +```yaml +apiVersion: metrics.cloud.sap/v1beta1 +kind: CompoundMetric +metadata: + name: comp-pod +spec: + name: comp-metric-pods + description: Pods + target: + resource: pods + group: "" + version: v1 + frequency: 1 # in minutes + projections: + - name: pod-namespace + fieldPath: "metadata.namespace" +--- +``` + +### Federated Metric +Federated metrics deal with resources that are spread across multiple clusters. To monitor these resources, you need to define a `FederatedMetric` resource. +They offer capabilities to aggregate data as well as filtering down to a specific cluster or field using projections. +```yaml +apiVersion: metrics.cloud.sap/v1beta1 +kind: FederatedMetric +metadata: + name: xfed-prov +spec: + name: xfed-prov + description: crossplane providers + target: + group: pkg.crossplane.io + resource: providers + version: v1 + frequency: 1 # in minutes + projections: + - name: package + fieldPath: "spec.package" + federateCaRef: + name: federate-ca-sample + namespace: default +--- + +``` + +### Federated Managed Metric +This is a special use case metric, it is looking at all the crossplane managed resource across all clusters. +The pre-condition here is that if a resource comes from a crossplane provider, its CRD should have categories "crossplane" and "managed". + + +```yaml +apiVersion: metrics.cloud.sap/v1beta1 +kind: FederatedManagedMetric +metadata: + name: xfed-managed +spec: + name: xfed-managed + description: crossplane managed resources + frequency: 1 # in minutes + federateCaRef: + name: federate-ca-sample + namespace: default +--- +``` + +## Remote Cluster Access + + +### Cluster Access +The Metrics Operator can monitor both the cluster it's deployed in and remote clusters. To monitor a remote cluster, define a `ClusterAccess` resource: + +This cluster access resource can be used by `SingleMetric` and `CompoundMetric` resources to monitor resources in the remote cluster. + +```yaml +apiVersion: metrics.cloud.sap/v1beta1 +kind: ClusterAccess +metadata: + name: remote-cluster + namespace: +spec: + remoteClusterConfig: + clusterSecretRef: + name: remote-cluster-secret + namespace: + serviceAccountName: + serviceAccountNamespace: +``` + + +### Federated Cluster Access + +To monitor resources across multiple clusters, define a `FederatedClusterAccess` resource: + +```yaml +apiVersion: metrics.cloud.sap/v1beta1 +kind: FederatedClusterAccess +metadata: + name: federate-ca-sample + namespace: default +spec: + target: + group: core.orchestrate.cloud.sap + resource: controlplanes #plural always, lowecase only + version: v1beta1 + kubeConfigPath: spec.target.kubeconfig #case sensitive +``` + + +## RBAC Configuration + +The Metrics Operator requires appropriate permissions to monitor the resources you specify. You need to configure RBAC (Role-Based Access Control) to grant these permissions. Here's an example of how to create a ClusterRole and ClusterRoleBinding for the Metrics Operator: + +```yaml +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metrics-operator-role +rules: +- apiGroups: + - "example.group" + resources: + - "exampleresources" + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: metrics-operator-rolebinding +subjects: +- kind: ServiceAccount + name: metrics-operator-sa + namespace: +roleRef: + kind: ClusterRole + name: metrics-operator-role + apiGroup: rbac.authorization.k8s.io +``` + +Replace `` with the namespace where the Metrics Operator is deployed. Adjust the `apiGroups` and `resources` fields to match the resources you want to monitor. + +Apply the RBAC configuration: + +```bash +kubectl apply -f rbac-config.yaml +``` + +Remember to update this RBAC configuration whenever you add new resource types to monitor. + + +## Data Sink Integration + +The Metrics Operator sends collected data to a configured data sink for storage and analysis. The data sink (e.g., Dynatrace) provides tools for data aggregation, filtering, and visualization. + +To make the most of your metrics: + +1. Configure your data sink according to its documentation. +2. Use the data sink's query language or UI to create custom views of your metrics. +3. Set up alerts based on metric thresholds or patterns. +4. Leverage the data sink's analysis tools to gain insights into your system's behavior and performance. + +For specific instructions on using your data sink's features, refer to its documentation. For example, if using Dynatrace, consult the Dynatrace documentation for information on creating custom charts, setting up alerts, and performing advanced analytics on your metric data. + + +## Getting Started +You’ll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster. +**Note:** Your controller will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows). + +### Running on the cluster +1. Install Instances of Custom Resources: + +```sh +make dev-local-all +``` + +2. Run the controller: + +```sh +make dev-run +``` +Or run it from your IDE. + +### Delete Kind Cluster +Delete Kind cluster +```sh +make dev-clean +``` + +### Modifying the API definitions +If you are editing the API definitions, generate the manifests such as CRs or CRDs using: + +```sh +make manifests generate +``` ## Support, Feedback, Contributing diff --git a/internal/controller/federatedmanagedmetric_controller.go b/internal/controller/federatedmanagedmetric_controller.go index b3224c6..390f391 100644 --- a/internal/controller/federatedmanagedmetric_controller.go +++ b/internal/controller/federatedmanagedmetric_controller.go @@ -135,7 +135,7 @@ func (r *FederatedManagedMetricReconciler) Reconcile(ctx context.Context, req ct /* 1.2 Create QueryConfig to query the resources in the K8S cluster or external cluster based on the kubeconfig secret reference */ - queryConfigs, err := config.CreateExternalQueryConfigSet(ctx, metric.Spec.FederateCAFacade.FederatedCARef, r.getClient(), r.getRestConfig()) + queryConfigs, err := config.CreateExternalQueryConfigSet(ctx, metric.Spec.FederatedCARef, r.getClient(), r.getRestConfig()) if err != nil { l.Error(err, "unable to create query configs") return ctrl.Result{RequeueAfter: RequeueAfterError}, err diff --git a/internal/controller/federatedmetric_controller.go b/internal/controller/federatedmetric_controller.go index 3ab83eb..001dae7 100644 --- a/internal/controller/federatedmetric_controller.go +++ b/internal/controller/federatedmetric_controller.go @@ -140,7 +140,7 @@ func (r *FederatedMetricReconciler) Reconcile(ctx context.Context, req ctrl.Requ /* 1.2 Create QueryConfig to query the resources in the K8S cluster or external cluster based on the kubeconfig secret reference */ - queryConfigs, err := config.CreateExternalQueryConfigSet(ctx, metric.Spec.FederateCAFacade.FederatedCARef, r.getClient(), r.getRestConfig()) + queryConfigs, err := config.CreateExternalQueryConfigSet(ctx, metric.Spec.FederatedCARef, r.getClient(), r.getRestConfig()) if err != nil { l.Error(err, "unable to create query configs") return ctrl.Result{RequeueAfter: RequeueAfterError}, err