Files
fission-console/client-console/internal/cloud/quota.go
T

107 lines
4.8 KiB
Go

package cloud
import (
"log"
"os"
"golang.org/x/net/context"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
)
var (
quotaGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "resourcequotas"}
limitRangeGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "limitranges"}
)
// ApplyResourceQuota создаёт ResourceQuota и LimitRange в namespace ns.
// Параметры берутся из env vars (см. ниже) — можно тюнить без пересборки.
// Идемпотентно: если объекты уже существуют — пропускается без ошибки.
//
// ResourceQuota ограничивает суммарные ресурсы пользователя:
// - CPU / memory requests и limits
// - Количество подов, функций, пакетов, triggers
//
// LimitRange задаёт дефолтные limits/requests на контейнер:
// поды без явных limits получают defaults автоматически → нет unbounded containers.
//
// Env vars (все опциональны, defaults указаны ниже):
//
// QUOTA_REQ_CPU — суммарный CPU requests (default: "1")
// QUOTA_REQ_MEM — суммарная memory requests (default: "2Gi")
// QUOTA_LIM_CPU — суммарный CPU limits (default: "12")
// QUOTA_LIM_MEM — суммарная memory limits (default: "6Gi")
// QUOTA_PODS — макс. количество подов (default: "30")
// QUOTA_FUNCTIONS — макс. количество функций (default: "20")
// QUOTA_PACKAGES — макс. количество пакетов (default: "40")
// QUOTA_HTTPTRIGGERS — макс. количество HTTP triggers (default: "20")
// LIMIT_DEFAULT_CPU — дефолтный CPU limit на контейнер (default: "500m")
// LIMIT_DEFAULT_MEM — дефолтная memory limit на контейнер (default: "256Mi")
// LIMIT_REQ_CPU — дефолтный CPU request на контейнер (default: "50m")
// LIMIT_REQ_MEM — дефолтный memory request на контейнер (default: "64Mi")
// LIMIT_MAX_CPU — максимальный CPU limit на контейнер (default: "2")
// LIMIT_MAX_MEM — максимальная memory limit на контейнер (default: "1Gi")
func ApplyResourceQuota(ctx context.Context, dyn dynamic.Interface, ns string) {
quotaObj := &unstructured.Unstructured{Object: map[string]any{
"apiVersion": "v1",
"kind": "ResourceQuota",
"metadata": map[string]any{"name": "user-quota", "namespace": ns},
"spec": map[string]any{
"hard": map[string]any{
"requests.cpu": envDefault("QUOTA_REQ_CPU", "1"),
"requests.memory": envDefault("QUOTA_REQ_MEM", "2Gi"),
"limits.cpu": envDefault("QUOTA_LIM_CPU", "12"),
"limits.memory": envDefault("QUOTA_LIM_MEM", "6Gi"),
"pods": envDefault("QUOTA_PODS", "30"),
"count/functions.fission.io": envDefault("QUOTA_FUNCTIONS", "20"),
"count/packages.fission.io": envDefault("QUOTA_PACKAGES", "40"),
"count/httptriggers.fission.io": envDefault("QUOTA_HTTPTRIGGERS", "20"),
},
},
}}
_, err := dyn.Resource(quotaGVR).Namespace(ns).Create(ctx, quotaObj, metav1.CreateOptions{})
if err != nil && !apierrors.IsAlreadyExists(err) {
log.Printf("cloud.ApplyResourceQuota: %s: %v", ns, err)
}
limitRangeObj := &unstructured.Unstructured{Object: map[string]any{
"apiVersion": "v1",
"kind": "LimitRange",
"metadata": map[string]any{"name": "user-limits", "namespace": ns},
"spec": map[string]any{
"limits": []any{
map[string]any{
"type": "Container",
"default": map[string]any{
"cpu": envDefault("LIMIT_DEFAULT_CPU", "500m"),
"memory": envDefault("LIMIT_DEFAULT_MEM", "256Mi"),
},
"defaultRequest": map[string]any{
"cpu": envDefault("LIMIT_REQ_CPU", "50m"),
"memory": envDefault("LIMIT_REQ_MEM", "64Mi"),
},
"max": map[string]any{
"cpu": envDefault("LIMIT_MAX_CPU", "2"),
"memory": envDefault("LIMIT_MAX_MEM", "1Gi"),
},
},
},
},
}}
_, err = dyn.Resource(limitRangeGVR).Namespace(ns).Create(ctx, limitRangeObj, metav1.CreateOptions{})
if err != nil && !apierrors.IsAlreadyExists(err) {
log.Printf("cloud.ApplyResourceQuota LimitRange: %s: %v", ns, err)
}
}
// envDefault читает env переменную или возвращает fallback.
func envDefault(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}