-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add check before creation and fix test case
- Loading branch information
Showing
321 changed files
with
9,211 additions
and
3,013 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -157,20 +157,20 @@ cue-fmt: cuetool ## Run cue fmt against code. | |
git ls-files --exclude-standard | grep "\.cue$$" | xargs $(CUE) fmt | ||
git ls-files --exclude-standard | grep "\.cue$$" | xargs $(CUE) fix | ||
|
||
.PHONY: fast-lint | ||
fast-lint: golangci staticcheck vet # [INTERNAL] fast lint | ||
.PHONY: lint-fast | ||
lint-fast: golangci staticcheck vet # [INTERNAL] fast lint | ||
$(GOLANGCILINT) run ./... | ||
|
||
.PHONY: lint | ||
lint: test-go-generate generate ## Run golangci-lint against code. | ||
$(MAKE) fast-lint | ||
$(MAKE) lint-fast | ||
|
||
.PHONY: staticcheck | ||
staticcheck: staticchecktool ## Run staticcheck against code. | ||
$(STATICCHECK) ./... | ||
|
||
.PHONY: build-checks | ||
build-checks: generate fmt vet goimports fast-lint ## Run build checks. | ||
build-checks: generate fmt vet goimports lint-fast ## Run build checks. | ||
|
||
.PHONY: mod-download | ||
mod-download: ## Run go mod download against go modules. | ||
|
@@ -269,7 +269,6 @@ kbcli-doc: generate ## generate CLI command reference manual. | |
$(GO) run ./hack/docgen/cli/main.go ./docs/user_docs/cli | ||
|
||
|
||
|
||
##@ Operator Controller Manager | ||
|
||
.PHONY: manager | ||
|
@@ -301,8 +300,6 @@ ARGUMENTS= | |
DEBUG_PORT=2345 | ||
run-delve: manifests generate fmt vet ## Run Delve debugger. | ||
dlv --listen=:$(DEBUG_PORT) --headless=true --api-version=2 --accept-multiclient debug $(GO_PACKAGE) -- $(ARGUMENTS) | ||
|
||
|
||
##@ Deployment | ||
|
||
ifndef ignore-not-found | ||
|
@@ -312,12 +309,10 @@ endif | |
.PHONY: install | ||
install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. | ||
($(KUSTOMIZE) build config/crd | kubectl replace -f -) || ($(KUSTOMIZE) build config/crd | kubectl create -f -) | ||
$(KUSTOMIZE) build $(shell $(GO) env GOPATH)/pkg/mod/github.com/kubernetes-csi/external-snapshotter/client/[email protected]/config/crd | kubectl apply -f - | ||
|
||
.PHONY: uninstall | ||
uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. | ||
$(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - | ||
$(KUSTOMIZE) build $(shell $(GO) env GOPATH)/pkg/mod/github.com/kubernetes-csi/external-snapshotter/client/[email protected]/config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - | ||
|
||
.PHONY: deploy | ||
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. | ||
|
@@ -397,6 +392,8 @@ bump-chart-ver: \ | |
bump-single-chart-ver.milvus \ | ||
bump-single-chart-ver.qdrant \ | ||
bump-single-chart-ver.qdrant-cluster \ | ||
bump-single-chart-ver.weaviate \ | ||
bump-single-chart-ver.weaviate-cluster \ | ||
bump-single-chart-ver.chatgpt-retrieval-plugin | ||
bump-chart-ver: ## Bump helm chart version. | ||
|
||
|
@@ -750,7 +747,7 @@ endif | |
.PHONY: render-smoke-testdata-manifests | ||
render-smoke-testdata-manifests: ## Update E2E test dataset | ||
$(HELM) template mycluster deploy/apecloud-mysql-cluster > test/e2e/testdata/smoketest/wesql/00_wesqlcluster.yaml | ||
$(HELM) template mycluster deploy/postgresqlcluster > test/e2e/testdata/smoketest/postgresql/00_postgresqlcluster.yaml | ||
$(HELM) template mycluster deploy/postgresql-cluster > test/e2e/testdata/smoketest/postgresql/00_postgresqlcluster.yaml | ||
$(HELM) template mycluster deploy/redis > test/e2e/testdata/smoketest/redis/00_rediscluster.yaml | ||
$(HELM) template mycluster deploy/redis-cluster >> test/e2e/testdata/smoketest/redis/00_rediscluster.yaml | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
/* | ||
Copyright ApeCloud, Inc. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package v1alpha1 | ||
|
||
import ( | ||
"golang.org/x/exp/slices" | ||
"gopkg.in/inf.v0" | ||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/api/resource" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
// ClassFamilySpec defines the desired state of ClassFamily | ||
type ClassFamilySpec struct { | ||
// Class family models, generally, a model is a specific constraint for CPU, memory and their relation. | ||
Models []ClassFamilyModel `json:"models,omitempty"` | ||
} | ||
|
||
type ClassFamilyModel struct { | ||
// The constraint for vcpu cores. | ||
// +kubebuilder:validation:Required | ||
CPU CPUConstraint `json:"cpu,omitempty"` | ||
|
||
// The constraint for memory size. | ||
// +kubebuilder:validation:Required | ||
Memory MemoryConstraint `json:"memory,omitempty"` | ||
} | ||
|
||
type CPUConstraint struct { | ||
// The maximum count of vcpu cores, [Min, Max] defines a range for valid vcpu cores, and the value in this range | ||
// must be multiple times of Step. It's useful to define a large number of valid values without defining them one by | ||
// one. Please see the documentation for Step for some examples. | ||
// If Slots is specified, Max, Min, and Step are ignored | ||
// +optional | ||
Max *resource.Quantity `json:"max,omitempty"` | ||
|
||
// The minimum count of vcpu cores, [Min, Max] defines a range for valid vcpu cores, and the value in this range | ||
// must be multiple times of Step. It's useful to define a large number of valid values without defining them one by | ||
// one. Please see the documentation for Step for some examples. | ||
// If Slots is specified, Max, Min, and Step are ignored | ||
// +optional | ||
Min *resource.Quantity `json:"min,omitempty"` | ||
|
||
// The minimum granularity of vcpu cores, [Min, Max] defines a range for valid vcpu cores and the value in this range must be | ||
// multiple times of Step. | ||
// For example: | ||
// 1. Min is 2, Max is 8, Step is 2, and the valid vcpu core is {2, 4, 6, 8}. | ||
// 2. Min is 0.5, Max is 2, Step is 0.5, and the valid vcpu core is {0.5, 1, 1.5, 2}. | ||
// +optional | ||
Step *resource.Quantity `json:"step,omitempty"` | ||
|
||
// The valid vcpu cores, it's useful if you want to define valid vcpu cores explicitly. | ||
// If Slots is specified, Max, Min, and Step are ignored | ||
// +optional | ||
Slots []resource.Quantity `json:"slots,omitempty"` | ||
} | ||
|
||
type MemoryConstraint struct { | ||
// The size of memory per vcpu core. | ||
// For example: 1Gi, 200Mi. | ||
// If SizePerCPU is specified, MinPerCPU and MaxPerCPU are ignore. | ||
// +optional | ||
SizePerCPU *resource.Quantity `json:"sizePerCPU,omitempty"` | ||
|
||
// The maximum size of memory per vcpu core, [MinPerCPU, MaxPerCPU] defines a range for valid memory size per vcpu core. | ||
// It is useful on GCP as the ratio between the CPU and memory may be a range. | ||
// If SizePerCPU is specified, MinPerCPU and MaxPerCPU are ignored. | ||
// Reference: https://cloud.google.com/compute/docs/general-purpose-machines#custom_machine_types | ||
// +optional | ||
MaxPerCPU *resource.Quantity `json:"maxPerCPU,omitempty"` | ||
|
||
// The minimum size of memory per vcpu core, [MinPerCPU, MaxPerCPU] defines a range for valid memory size per vcpu core. | ||
// It is useful on GCP as the ratio between the CPU and memory may be a range. | ||
// If SizePerCPU is specified, MinPerCPU and MaxPerCPU are ignored. | ||
// Reference: https://cloud.google.com/compute/docs/general-purpose-machines#custom_machine_types | ||
// +optional | ||
MinPerCPU *resource.Quantity `json:"minPerCPU,omitempty"` | ||
} | ||
|
||
// +kubebuilder:object:root=true | ||
// +kubebuilder:resource:categories={kubeblocks,all},scope=Cluster,shortName=cf | ||
|
||
// ClassFamily is the Schema for the classfamilies API | ||
type ClassFamily struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
||
Spec ClassFamilySpec `json:"spec,omitempty"` | ||
} | ||
|
||
// +kubebuilder:object:root=true | ||
|
||
// ClassFamilyList contains a list of ClassFamily | ||
type ClassFamilyList struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ListMeta `json:"metadata,omitempty"` | ||
Items []ClassFamily `json:"items"` | ||
} | ||
|
||
func init() { | ||
SchemeBuilder.Register(&ClassFamily{}, &ClassFamilyList{}) | ||
} | ||
|
||
// ValidateCPU validate if the CPU matches the class family model constraint | ||
func (m *ClassFamilyModel) ValidateCPU(cpu resource.Quantity) bool { | ||
if m == nil { | ||
return false | ||
} | ||
if m.CPU.Min != nil && m.CPU.Min.Cmp(cpu) > 0 { | ||
return false | ||
} | ||
if m.CPU.Max != nil && m.CPU.Max.Cmp(cpu) < 0 { | ||
return false | ||
} | ||
if m.CPU.Slots != nil && slices.Index(m.CPU.Slots, cpu) < 0 { | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
// ValidateMemory validate if the memory matches the class family model constraint | ||
func (m *ClassFamilyModel) ValidateMemory(cpu *resource.Quantity, memory *resource.Quantity) bool { | ||
if m == nil { | ||
return false | ||
} | ||
|
||
if memory == nil { | ||
return true | ||
} | ||
|
||
// fast path if cpu is specified | ||
if cpu != nil && m.Memory.SizePerCPU != nil { | ||
return inf.NewDec(1, 0).Mul(cpu.AsDec(), m.Memory.SizePerCPU.AsDec()).Cmp(memory.AsDec()) == 0 | ||
} | ||
|
||
if cpu != nil && m.Memory.MaxPerCPU != nil && m.Memory.MinPerCPU != nil { | ||
maxMemory := inf.NewDec(1, 0).Mul(cpu.AsDec(), m.Memory.MaxPerCPU.AsDec()) | ||
minMemory := inf.NewDec(1, 0).Mul(cpu.AsDec(), m.Memory.MinPerCPU.AsDec()) | ||
return maxMemory.Cmp(memory.AsDec()) >= 0 && minMemory.Cmp(memory.AsDec()) <= 0 | ||
} | ||
|
||
// TODO slow path if cpu is not specified | ||
|
||
return true | ||
} | ||
|
||
// ValidateResourceRequirements validate if the resource matches the class family model constraints | ||
func (m *ClassFamilyModel) ValidateResourceRequirements(r *corev1.ResourceRequirements) bool { | ||
var ( | ||
cpu = r.Requests.Cpu() | ||
memory = r.Requests.Memory() | ||
) | ||
|
||
if m == nil { | ||
return false | ||
} | ||
|
||
if cpu.IsZero() && memory.IsZero() { | ||
return true | ||
} | ||
|
||
if !m.ValidateCPU(*cpu) { | ||
return false | ||
} | ||
|
||
if !m.ValidateMemory(cpu, memory) { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
// FindMatchingModels find all class family models that resource matches | ||
func (c *ClassFamily) FindMatchingModels(r *corev1.ResourceRequirements) []ClassFamilyModel { | ||
if c == nil { | ||
return nil | ||
} | ||
var models []ClassFamilyModel | ||
for _, model := range c.Spec.Models { | ||
if model.ValidateResourceRequirements(r) { | ||
models = append(models, model) | ||
} | ||
} | ||
return models | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
Copyright ApeCloud, Inc. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package v1alpha1 | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/api/resource" | ||
"k8s.io/apimachinery/pkg/util/yaml" | ||
) | ||
|
||
const classFamilyBytes = ` | ||
# API scope: cluster | ||
# ClusterClassFamily | ||
apiVersion: "apps.kubeblocks.io/v1alpha1" | ||
kind: "ClassFamily" | ||
metadata: | ||
name: kb-class-family-general | ||
spec: | ||
models: | ||
- cpu: | ||
min: 0.5 | ||
max: 128 | ||
step: 0.5 | ||
memory: | ||
sizePerCPU: 4Gi | ||
- cpu: | ||
slots: [0.1, 0.2, 0.4, 0.6, 0.8, 1] | ||
memory: | ||
minPerCPU: 200Mi | ||
- cpu: | ||
min: 0.1 | ||
max: 64 | ||
step: 0.1 | ||
memory: | ||
minPerCPU: 4Gi | ||
maxPerCPU: 8Gi | ||
` | ||
|
||
func TestClassFamily_ValidateResourceRequirements(t *testing.T) { | ||
var cf ClassFamily | ||
err := yaml.Unmarshal([]byte(classFamilyBytes), &cf) | ||
if err != nil { | ||
panic("Failed to unmarshal class family: %v" + err.Error()) | ||
} | ||
cases := []struct { | ||
cpu string | ||
memory string | ||
expect bool | ||
}{ | ||
{cpu: "0.5", memory: "2Gi", expect: true}, | ||
{cpu: "0.2", memory: "40Mi", expect: true}, | ||
{cpu: "1", memory: "6Gi", expect: true}, | ||
{cpu: "2", memory: "20Gi", expect: false}, | ||
{cpu: "2", memory: "6Gi", expect: false}, | ||
} | ||
|
||
for _, item := range cases { | ||
requirements := &corev1.ResourceRequirements{ | ||
Requests: map[corev1.ResourceName]resource.Quantity{ | ||
corev1.ResourceCPU: resource.MustParse(item.cpu), | ||
corev1.ResourceMemory: resource.MustParse(item.memory), | ||
}, | ||
} | ||
assert.Equal(t, item.expect, len(cf.FindMatchingModels(requirements)) > 0) | ||
} | ||
} |
Oops, something went wrong.