Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ ARCH ?= amd64
OS ?= $(shell uname -s | tr A-Z a-z)
K8S_LATEST_VER ?= $(shell curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)
export CONTROLLER_IMG ?= $(REGISTRY)/$(IMAGE_NAME)
TAG ?= v1.12.0
TAG ?= main

.PHONY: all
all: build
Expand Down
74 changes: 73 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,8 @@ Response fields:
- `profiles` — number of accessible Profiles
- `clusterSummaries` — number of accessible ClusterSummaries (one per profile+cluster pair)
- `eventTriggers` — number of accessible EventTriggers
- `classifiers` — number of accessible Classifiers
- `managementClusterClassifiers` — number of accessible ManagementClusterClassifiers

Each count reflects only the resources the authenticated user has permission to view.

Expand All @@ -508,7 +510,7 @@ http://localhost:9000/stats
returns

```json
{"capiClusters":1,"notReadyCAPIClusters":0,"sveltosClusters":1,"notReadySveltosClusters":0,"pullModeClusters":0,"clusterProfiles":12,"profiles":0,"clusterSummaries":11,"eventTriggers":1}
{"capiClusters":1,"notReadyCAPIClusters":0,"sveltosClusters":1,"notReadySveltosClusters":0,"pullModeClusters":0,"clusterProfiles":12,"profiles":0,"clusterSummaries":11,"eventTriggers":1,"classifiers":1,"managementClusterClassifiers":1}
```

### Get list of EventTriggers
Expand Down Expand Up @@ -540,6 +542,76 @@ Response contains:
{"eventTriggerName":"service-network-policy","clusterSelector":{"matchLabels":{"env":"fv"}},"eventSource":{"kind":"EventSource","apiVersion":"lib.projectsveltos.io/v1beta1","metadata":{"name":"sveltos-service"},"spec":{"resourceSelectors":[{"group":"","version":"v1","kind":"Service","labelFilters":[{"key":"sveltos","operation":"Equal","value":"fv"}]}],"collectResources":true}},"clusterEventMatches":[{"clusterNamespace":"default","clusterName":"clusterapi-workload","clusterKind":"Cluster","labels":{"cluster.x-k8s.io/cluster-name":"clusterapi-workload","env":"fv","sveltos-agent":"present","topology.cluster.x-k8s.io/owned":""},"version":"v1.35.0","ready":true,"paused":true,"failureMessage":null,"resources":[{"name":"my-service","namespace":"default","group":"","kind":"Service","version":"","profileNames":null}]}]}
```

### Get list of Classifiers

```/classifiers```

Returns a summary of all Classifier and ManagementClusterClassifier instances the user can access.

It is possible to filter by cluster:

. ```cluster_namespace=<string>``` => returns only classifiers currently matching a cluster in this namespace

. ```cluster_name=<string>``` => returns only classifiers currently matching a cluster with this name

. ```cluster_type=<Capi|Sveltos>``` => returns only classifiers currently matching a cluster of this type

When a cluster filter is set, the list is narrowed to instances currently matching that cluster, but each entry's `matchingClusterCount` still reflects the instance's total match count across all clusters, not just the filtered one.

This API supports pagination. Use:

. ```limit=<int>``` to specify the number of classifiers the API will return

. ```skip=<int>``` to specify from which classifier to start (classifiers are ordered by name, then type)

For instance:

```
http://localhost:9000/classifiers
```

returns

```json
{"totalClassifiers":2,"classifiers":[{"name":"default-classifier","type":"Classifier","labelCount":1,"matchingClusterCount":2},{"name":"tag-production-clusters","type":"ManagementClusterClassifier","labelCount":2,"matchingClusterCount":1}]}
```

### Get Classifier Details

```/classifier?name=<classifier name>&type=<Classifier|ManagementClusterClassifier>```

Response contains:

- Name: name of the Classifier/ManagementClusterClassifier
- Type: `Classifier` or `ManagementClusterClassifier`
- Spec fields specific to the type: resource selectors, aggregated classification and kubernetes version constraints for `Classifier`; match resources and classification Lua for `ManagementClusterClassifier`
- ClassifierLabels: the labels this instance is configured to add to matching clusters
- MatchingClusters: clusters currently matching this instance. For each cluster: the labels it currently owns there (joined from the corresponding ClassifierReport/ManagementClusterClassifierReport), and any labels this instance would like to manage but currently cannot because a different Classifier or ManagementClusterClassifier already owns them

For instance:

```
http://localhost:9000/classifier?name=default-classifier&type=Classifier
```

returns

```json
{"name":"default-classifier","type":"Classifier","classifierLabels":[{"key":"sveltos-agent","value":"present"}],"matchingClusters":[{"clusterNamespace":"default","clusterName":"clusterapi-workload","clusterType":"Capi","managedLabels":[{"key":"sveltos-agent","value":"present"}]},{"clusterNamespace":"mgmt","clusterName":"mgmt","clusterType":"Sveltos","managedLabels":[{"key":"sveltos-agent","value":"present"}]}]}
```

For a ManagementClusterClassifier:

```
http://localhost:9000/classifier?name=tag-production-clusters&type=ManagementClusterClassifier
```

returns

```json
{"name":"tag-production-clusters","type":"ManagementClusterClassifier","matchResources":[{"group":"","version":"v1","kind":"ConfigMap","namespace":"projectsveltos","selector":{"matchLabels":{"sveltos.io/env":"production"}}}],"classificationLua":"function evaluate(resources)\n local result = {}\n for _, cm in ipairs(resources) do\n local ns = cm.data.clusterNamespace\n local name = cm.data.clusterName\n if ns ~= nil and name ~= nil then\n table.insert(result, {namespace=ns, name=name, kind=\"Cluster\"})\n end\n end\n return result\nend\n","classifierLabels":[{"key":"env","value":"fv"},{"key":"cost-centre","value":"platform"}],"matchingClusters":[{"clusterNamespace":"default","clusterName":"clusterapi-workload","clusterType":"Capi","managedLabels":[{"key":"env","value":"fv"},{"key":"cost-centre","value":"platform"}]}]}
```

### How to get token

First, create a service account in the desired namespace:
Expand Down
56 changes: 52 additions & 4 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/spf13/pflag"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
cliflag "k8s.io/component-base/cli/flag"
Expand Down Expand Up @@ -73,9 +74,12 @@ var (
)

const (
defaultReconcilers = 10
mebibytes_bytes = 1 << 20
gibibytes_per_bytes = 1 << 30
defaultReconcilers = 10
mebibytes_bytes = 1 << 20
// memoryLimitCoefficient is the fraction of the container memory limit that
// GOMEMLIMIT is set to. Kept below 1 so Go's GC reacts to memory pressure
// and frees memory before the kubelet OOM-kills the container.
memoryLimitCoefficient = 0.75
)

// Add RBAC for the authorized diagnostics endpoint.
Expand All @@ -96,6 +100,13 @@ const (
//+kubebuilder:rbac:groups=lib.projectsveltos.io,resources=eventreports,verbs=get;list;watch
//+kubebuilder:rbac:groups=lib.projectsveltos.io,resources=eventreports/status,verbs=get;list;watch

//+kubebuilder:rbac:groups=lib.projectsveltos.io,resources=classifiers,verbs=get;list;watch
//+kubebuilder:rbac:groups=lib.projectsveltos.io,resources=classifiers/status,verbs=get;list;watch
//+kubebuilder:rbac:groups=lib.projectsveltos.io,resources=managementclusterclassifiers,verbs=get;list;watch
//+kubebuilder:rbac:groups=lib.projectsveltos.io,resources=managementclusterclassifiers/status,verbs=get;list;watch
//+kubebuilder:rbac:groups=lib.projectsveltos.io,resources=classifierreports,verbs=get;list;watch
//+kubebuilder:rbac:groups=lib.projectsveltos.io,resources=managementclusterclassifierreports,verbs=get;list;watch

func main() {
scheme, err := controller.InitScheme()
if err != nil {
Expand All @@ -120,6 +131,9 @@ func main() {
}),
Cache: cache.Options{
SyncPeriod: &syncPeriod,
// ui-backend never reads managedFields; stripping them before they
// are committed to the cache significantly reduces its memory usage.
DefaultTransform: cache.TransformStripManagedFields(),
},
PprofBindAddress: profilerAddress,
}
Expand All @@ -141,7 +155,7 @@ func main() {
libsveltosv1beta1.ComponentUIBackend, ctrl.Log.WithName("log-setter"),
ctrl.GetConfigOrDie())

debug.SetMemoryLimit(gibibytes_per_bytes)
setMemoryLimit(ctrl.Log.WithName("memory-usage"))
go printMemUsage(ctrl.Log.WithName("memory-usage"))

server.InitializeManagerInstance(ctx, mgr.GetConfig(), mgr.GetClient(), scheme,
Expand Down Expand Up @@ -292,6 +306,40 @@ func getDiagnosticsOptions() metricsserver.Options {
}
}

// setMemoryLimit configures GOMEMLIMIT from the container's memory limit,
// read via the TOTAL_MEMORY_LIMIT env var (populated by a resourceFieldRef to
// limits.memory in the deployment manifest). No-op if the env var is unset or
// invalid, e.g. when running outside the container manifest.
func setMemoryLimit(logger logr.Logger) {
limitStr := os.Getenv("TOTAL_MEMORY_LIMIT")
if limitStr == "" {
return
}

quantity, err := resource.ParseQuantity(limitStr)
if err != nil {
logger.Error(err, "failed to parse TOTAL_MEMORY_LIMIT", "value", limitStr)
return
}

limitBytes := quantity.Value()
if limitBytes <= 0 {
return
}

aggressiveLimit := int64(float64(limitBytes) * memoryLimitCoefficient)
debug.SetMemoryLimit(aggressiveLimit)

var goLimitMiB uint64
if aggressiveLimit > 0 {
goLimitMiB = bToMb(uint64(aggressiveLimit))
}

logger.V(logs.LogDebug).Info("configured GOMEMLIMIT",
"container_limit_mib", bToMb(uint64(limitBytes)),
"go_limit_mib", goLimitMiB)
}

// printMemUsage memory stats. Call GC
func printMemUsage(logger logr.Logger) {
for {
Expand Down
2 changes: 1 addition & 1 deletion config/default/manager_image_patch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ spec:
spec:
containers:
# Change the value of image field below to your controller image URL
- image: projectsveltos/ui-backend:v1.12.0
- image: projectsveltos/ui-backend:main
name: manager
5 changes: 5 additions & 0 deletions config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ spec:
- /manager
args:
- --v=5
env:
- name: TOTAL_MEMORY_LIMIT
valueFrom:
resourceFieldRef:
resource: limits.memory
image: controller:latest
name: manager
ports:
Expand Down
6 changes: 6 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,19 @@ rules:
- apiGroups:
- lib.projectsveltos.io
resources:
- classifierreports
- classifiers
- classifiers/status
- debuggingconfigurations
- eventreports
- eventreports/status
- eventsources
- eventsources/status
- eventtriggers
- eventtriggers/status
- managementclusterclassifierreports
- managementclusterclassifiers
- managementclusterclassifiers/status
- sveltosclusters
- sveltosclusters/status
verbs:
Expand Down
Loading