From ce1063ac15a67a3731bd163233866f7977dfe5fa Mon Sep 17 00:00:00 2001 From: Kiichiro YUKAWA Date: Mon, 13 Feb 2023 11:21:24 +0900 Subject: [PATCH 01/11] Implement base of continuous benchmark tool (#1776) * Create Continuous Bench Search Job tool (#1733) * :sparkles: create bench job search tools Signed-off-by: vankichi * :sparkles: add load hdf5 functions Signed-off-by: vankichi * :recycle: fix format Signed-off-by: vankichi * :recycle: fix docker and use hdf5 data Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: refactor benchmark job Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: fix proto Signed-off-by: vankichi * :green_heart: add benchmark job image build ci Signed-off-by: vankichi * :green_heart: invest Signed-off-by: vankichi * Revert ":green_heart: invest" This reverts commit f0f585ccf71b1c95a88559941557a27774096e69. * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply code review Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Hiroto Funakoshi * :sparkles: apply from feedback Signed-off-by: vankichi * Update internal/config/benchmark.go Co-authored-by: Yusuke Kato * :recycle: change directory path Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi * Add crds for continuous benchmark tools (#1789) * :sparkles: add crds for continuous benchmark operator Signed-off-by: vankichi * :sparkles: add benchmark operator/job scheme Signed-off-by: vankichi * :sparkles: rename package names and add doc.go Signed-off-by: vankichi * :sparkles: create runtime object Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply feedback Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato * Add Job reconciler & Change directory constitution of internal/k8s for benchmark (#1825) * :sparkles: :recycle: add Job reconciler & use scenario instead of operator Signed-off-by: vankichi * :recycle: fix format & rename file Signed-off-by: vankichi Signed-off-by: vankichi * Add benchmark operator framework (#1916) * :sparkles: impl benchmark reconciler Signed-off-by: vankichi * :sparkles: create benchmark operator framework Signed-off-by: vankichi * :recycle: remove unness changes Signed-off-by: vankichi Signed-off-by: vankichi * Format code with prettier and gofumpt * impl reconcile logic for create benchmark job (#1923) * :sparkles: impl reconcile logic for create benchmark job Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: fix Signed-off-by: vankichi * :recycle: refactor continuous benchmark's crds Signed-off-by: vankichi * :recycle: resolve error due to update conn bench crds for pkg/tools/benchmark/job Signed-off-by: vankichi * :recycle: refactor continuous benchmark job logic Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: update charts Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: rafactor con bench config and bug fix reconcile logic Signed-off-by: vankichi * :bug: Bugfix: fix typo and recall function logic Signed-off-by: vankichi * :recycle: refactor pkg benchmark job Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --- .../workflows/dockers-benchmark-job-image.yml | 129 ++++++ Makefile | 2 + Makefile.d/build.mk | 69 ++- Makefile.d/docker.mk | 34 +- Makefile.d/helm.mk | 22 + apis/docs/v1/docs.md | 3 + apis/grpc/v1/benchmark/benchmark.pb.go | 87 ++++ .../grpc/v1/benchmark/benchmark_vtproto.pb.go | 132 ++++++ apis/proto/v1/benchmark/benchmark.proto | 32 ++ .../proto/v1/benchmark/benchmark.swagger.json | 45 ++ .../crds/valdbenchmarkoperatorrelease.yaml | 225 ++++++++++ .../values.schema.json | 263 +++++++++++ charts/vald-benchmark-operator/values.yaml | 274 ++++++++++++ cmd/tools/benchmark/job/main.go | 60 +++ cmd/tools/benchmark/job/sample.yaml | 168 +++++++ cmd/tools/benchmark/operator/main.go | 59 +++ cmd/tools/benchmark/operator/sample.yaml | 0 dockers/tools/benchmark/job/Dockerfile | 100 +++++ dockers/tools/benchmark/operator/Dockerfile | 87 ++++ internal/config/benchmark.go | 116 +++++ internal/config/server.go | 2 +- internal/errors/http.go | 5 + internal/errors/http_test.go | 61 +++ internal/k8s/client/client.go | 79 ++++ internal/k8s/client/option.go | 29 ++ .../k8s/crd/benchmark/valdbenchmarkjob.yaml | 381 ++++++++++++++++ .../crd/benchmark/valdbenchmarkscenario.yaml | 422 ++++++++++++++++++ internal/k8s/job/job.go | 160 +++++++ internal/k8s/job/option.go | 65 +++ .../k8s/rbac/benchmark/job/clusterrole.yaml | 92 ++++ .../benchmark/job/clusterrolebinding.yaml | 27 ++ .../rbac/benchmark/job/serviceaccount.yaml | 20 + internal/k8s/rbac/benchmark/job/svc.yaml | 31 ++ .../rbac/benchmark/operator/clusterrole.yaml | 95 ++++ .../operator/clusterrolebinding.yaml | 27 ++ .../benchmark/operator/serviceaccount.yaml | 20 + internal/k8s/reconciler.go | 7 + internal/k8s/vald/benchmark/api/v1/info.go | 40 ++ .../k8s/vald/benchmark/api/v1/job_types.go | 233 ++++++++++ .../vald/benchmark/api/v1/scenario_types.go | 144 ++++++ internal/k8s/vald/benchmark/job/doc.go | 18 + internal/k8s/vald/benchmark/job/job.go | 140 ++++++ internal/k8s/vald/benchmark/job/option.go | 68 +++ internal/k8s/vald/benchmark/scenario/doc.go | 18 + .../k8s/vald/benchmark/scenario/option.go | 68 +++ .../k8s/vald/benchmark/scenario/scenario.go | 128 ++++++ internal/test/data/hdf5/doc.go | 18 + internal/test/data/hdf5/hdf5.go | 268 +++++++++++ internal/test/data/hdf5/option.go | 59 +++ pkg/tools/benchmark/job/README.md | 1 + pkg/tools/benchmark/job/config/config.go | 309 +++++++++++++ pkg/tools/benchmark/job/config/config_test.go | 127 ++++++ pkg/tools/benchmark/job/config/doc.go | 18 + pkg/tools/benchmark/job/handler/doc.go | 17 + .../benchmark/job/handler/grpc/handler.go | 56 +++ .../benchmark/job/handler/grpc/option.go | 22 + .../benchmark/job/handler/rest/handler.go | 38 ++ .../benchmark/job/handler/rest/option.go | 22 + pkg/tools/benchmark/job/router/doc.go | 18 + pkg/tools/benchmark/job/router/option.go | 47 ++ pkg/tools/benchmark/job/router/router.go | 52 +++ pkg/tools/benchmark/job/service/doc.go | 18 + pkg/tools/benchmark/job/service/job.go | 188 ++++++++ pkg/tools/benchmark/job/service/option.go | 164 +++++++ pkg/tools/benchmark/job/service/search.go | 105 +++++ pkg/tools/benchmark/job/usecase/benchmarkd.go | 245 ++++++++++ pkg/tools/benchmark/operator/README.md | 1 + pkg/tools/benchmark/operator/config/config.go | 175 ++++++++ pkg/tools/benchmark/operator/config/doc.go | 18 + pkg/tools/benchmark/operator/handler/doc.go | 17 + .../operator/handler/grpc/handler.go | 56 +++ .../benchmark/operator/handler/grpc/option.go | 22 + .../operator/handler/rest/handler.go | 38 ++ .../benchmark/operator/handler/rest/option.go | 22 + pkg/tools/benchmark/operator/router/doc.go | 18 + pkg/tools/benchmark/operator/router/option.go | 47 ++ pkg/tools/benchmark/operator/router/router.go | 52 +++ pkg/tools/benchmark/operator/service/doc.go | 18 + .../benchmark/operator/service/option.go | 55 +++ .../benchmark/operator/service/scenario.go | 372 +++++++++++++++ .../benchmark/operator/usecase/benchmarkd.go | 200 +++++++++ 81 files changed, 7187 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/dockers-benchmark-job-image.yml create mode 100644 apis/grpc/v1/benchmark/benchmark.pb.go create mode 100644 apis/grpc/v1/benchmark/benchmark_vtproto.pb.go create mode 100644 apis/proto/v1/benchmark/benchmark.proto create mode 100644 apis/swagger/v1/benchmark/apis/proto/v1/benchmark/benchmark.swagger.json create mode 100644 charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml create mode 100644 charts/vald-benchmark-operator/values.schema.json create mode 100644 charts/vald-benchmark-operator/values.yaml create mode 100644 cmd/tools/benchmark/job/main.go create mode 100644 cmd/tools/benchmark/job/sample.yaml create mode 100644 cmd/tools/benchmark/operator/main.go create mode 100644 cmd/tools/benchmark/operator/sample.yaml create mode 100644 dockers/tools/benchmark/job/Dockerfile create mode 100644 dockers/tools/benchmark/operator/Dockerfile create mode 100644 internal/config/benchmark.go create mode 100644 internal/k8s/client/client.go create mode 100644 internal/k8s/client/option.go create mode 100644 internal/k8s/crd/benchmark/valdbenchmarkjob.yaml create mode 100644 internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml create mode 100644 internal/k8s/job/job.go create mode 100644 internal/k8s/job/option.go create mode 100644 internal/k8s/rbac/benchmark/job/clusterrole.yaml create mode 100644 internal/k8s/rbac/benchmark/job/clusterrolebinding.yaml create mode 100644 internal/k8s/rbac/benchmark/job/serviceaccount.yaml create mode 100644 internal/k8s/rbac/benchmark/job/svc.yaml create mode 100644 internal/k8s/rbac/benchmark/operator/clusterrole.yaml create mode 100644 internal/k8s/rbac/benchmark/operator/clusterrolebinding.yaml create mode 100644 internal/k8s/rbac/benchmark/operator/serviceaccount.yaml create mode 100644 internal/k8s/vald/benchmark/api/v1/info.go create mode 100644 internal/k8s/vald/benchmark/api/v1/job_types.go create mode 100644 internal/k8s/vald/benchmark/api/v1/scenario_types.go create mode 100644 internal/k8s/vald/benchmark/job/doc.go create mode 100644 internal/k8s/vald/benchmark/job/job.go create mode 100644 internal/k8s/vald/benchmark/job/option.go create mode 100644 internal/k8s/vald/benchmark/scenario/doc.go create mode 100644 internal/k8s/vald/benchmark/scenario/option.go create mode 100644 internal/k8s/vald/benchmark/scenario/scenario.go create mode 100644 internal/test/data/hdf5/doc.go create mode 100644 internal/test/data/hdf5/hdf5.go create mode 100644 internal/test/data/hdf5/option.go create mode 100644 pkg/tools/benchmark/job/README.md create mode 100644 pkg/tools/benchmark/job/config/config.go create mode 100644 pkg/tools/benchmark/job/config/config_test.go create mode 100644 pkg/tools/benchmark/job/config/doc.go create mode 100644 pkg/tools/benchmark/job/handler/doc.go create mode 100644 pkg/tools/benchmark/job/handler/grpc/handler.go create mode 100644 pkg/tools/benchmark/job/handler/grpc/option.go create mode 100644 pkg/tools/benchmark/job/handler/rest/handler.go create mode 100644 pkg/tools/benchmark/job/handler/rest/option.go create mode 100644 pkg/tools/benchmark/job/router/doc.go create mode 100644 pkg/tools/benchmark/job/router/option.go create mode 100644 pkg/tools/benchmark/job/router/router.go create mode 100644 pkg/tools/benchmark/job/service/doc.go create mode 100644 pkg/tools/benchmark/job/service/job.go create mode 100644 pkg/tools/benchmark/job/service/option.go create mode 100644 pkg/tools/benchmark/job/service/search.go create mode 100644 pkg/tools/benchmark/job/usecase/benchmarkd.go create mode 100644 pkg/tools/benchmark/operator/README.md create mode 100644 pkg/tools/benchmark/operator/config/config.go create mode 100644 pkg/tools/benchmark/operator/config/doc.go create mode 100644 pkg/tools/benchmark/operator/handler/doc.go create mode 100644 pkg/tools/benchmark/operator/handler/grpc/handler.go create mode 100644 pkg/tools/benchmark/operator/handler/grpc/option.go create mode 100644 pkg/tools/benchmark/operator/handler/rest/handler.go create mode 100644 pkg/tools/benchmark/operator/handler/rest/option.go create mode 100644 pkg/tools/benchmark/operator/router/doc.go create mode 100644 pkg/tools/benchmark/operator/router/option.go create mode 100644 pkg/tools/benchmark/operator/router/router.go create mode 100644 pkg/tools/benchmark/operator/service/doc.go create mode 100644 pkg/tools/benchmark/operator/service/option.go create mode 100644 pkg/tools/benchmark/operator/service/scenario.go create mode 100644 pkg/tools/benchmark/operator/usecase/benchmarkd.go diff --git a/.github/workflows/dockers-benchmark-job-image.yml b/.github/workflows/dockers-benchmark-job-image.yml new file mode 100644 index 0000000000..b45c698aab --- /dev/null +++ b/.github/workflows/dockers-benchmark-job-image.yml @@ -0,0 +1,129 @@ +# +# Copyright (C) 2019-2022 vdaas.org vald team +# +# 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 +# +# https://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. +# +name: "Build docker image: benchmark-job" +on: + push: + branches: + - master + tags: + - "*.*.*" + - "v*.*.*" + - "*.*.*-*" + - "v*.*.*-*" + paths: + - ".github/actions/docker-build/actions.yaml" + - ".github/workflows/dockers-benchmak-job-image.yml" + - "go.mod" + - "go.sum" + - "internal/**" + - "!internal/**/*_test.go" + - "!internal/db/**" + - "!internal/k8s/**" + - "apis/grpc/**" + - "pkg/benchmark/job/**" + - "cmd/benchmark/job/**" + - "dockers/benchmark/job/Dockerfile" + - "versions/GO_VERSION" + - "versions/NGT_VERSION" + pull_request: + paths: + - ".github/actions/docker-build/actions.yaml" + - ".github/workflows/dockers-benchmak-job-image.yml" + - "go.mod" + - "go.sum" + - "internal/**" + - "!internal/**/*_test.go" + - "!internal/db/**" + - "!internal/k8s/**" + - "apis/grpc/**" + - "pkg/benchmark/job/**" + - "cmd/benchmark/job/**" + - "dockers/benchmark/job/Dockerfile" + - "versions/GO_VERSION" + - "versions/NGT_VERSION" + +jobs: + build: + strategy: + max-parallel: 4 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup QEMU + uses: docker/setup-qemu-action@v1 + with: + platforms: all + - name: Setup Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + with: + buildkitd-flags: "--debug" + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_PASS }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ secrets.PACKAGE_USER }} + password: ${{ secrets.PACKAGE_TOKEN }} + - name: Build and Publish + id: build_and_publish + uses: ./.github/actions/docker-build + with: + target: benchmark-job + builder: ${{ steps.buildx.outputs.name }} + - name: Initialize CodeQL + if: startsWith( github.ref, 'refs/tags/') + uses: github/codeql-action/init@v2 + - name: Run vulnerability scanner (table) + if: startsWith( github.ref, 'refs/tags/') + uses: aquasecurity/trivy-action@master + with: + image-ref: "${{ steps.build_and_publish.outputs.IMAGE_NAME }}:${{ steps.build_and_publish.outputs.PRIMARY_TAG }}" + format: "table" + - name: Run vulnerability scanner (sarif) + if: startsWith( github.ref, 'refs/tags/') + uses: aquasecurity/trivy-action@master + with: + image-ref: "${{ steps.build_and_publish.outputs.IMAGE_NAME }}:${{ steps.build_and_publish.outputs.PRIMARY_TAG }}" + format: "template" + template: "@/contrib/sarif.tpl" + output: "trivy-results.sarif" + - name: Upload Trivy scan results to Security tab + if: startsWith( github.ref, 'refs/tags/') + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: "trivy-results.sarif" + slack: + name: Slack notification + needs: build + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/master' || startsWith( github.ref, 'refs/tags/') + steps: + - uses: technote-space/workflow-conclusion-action@v2 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: 8398a7/action-slack@v3 + with: + author_name: benchmark-job image build + status: ${{ env.WORKFLOW_CONCLUSION }} + only_mention_fail: channel + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_NOTIFY_WEBHOOK_URL }} diff --git a/Makefile b/Makefile index d859cef45d..40b8447d04 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,8 @@ LB_GATEWAY_IMAGE = $(NAME)-lb-gateway LOADTEST_IMAGE = $(NAME)-loadtest INDEX_CORRECTION_IMAGE = $(NAME)-index-correction MANAGER_INDEX_IMAGE = $(NAME)-manager-index +BENCHMARK_JOB_IMAGE = $(NAME)-benchmark-job +BENCHMARK_OPERATOR_IMAGE = $(NAME)-benchmark-operator MAINTAINER = "$(ORG).org $(NAME) team <$(NAME)@$(ORG).org>" VERSION ?= $(eval VERSION := $(shell cat versions/VALD_VERSION))$(VERSION) diff --git a/Makefile.d/build.mk b/Makefile.d/build.mk index f2f8275b46..6489344d86 100644 --- a/Makefile.d/build.mk +++ b/Makefile.d/build.mk @@ -22,7 +22,9 @@ binary/build: \ cmd/discoverer/k8s/discoverer \ cmd/gateway/lb/lb \ cmd/gateway/filter/filter \ - cmd/manager/index/index + cmd/manager/index/index \ + cmd/tools/benchmark/job/job \ + cmd/tools/benchmark/operator/operator cmd/agent/core/ngt/ngt: \ ngt/install \ @@ -235,6 +237,64 @@ cmd/index/job/correction/index-correction: \ $(dir $@)main.go $@ -version +cmd/tools/benchmark/job/job: \ + $(GO_SOURCES_INTERNAL) \ + $(PBGOS) \ + $(shell find ./cmd/tools/benchmark/job -type f -name '*.go' -not -name '*_test.go' -not -name 'doc.go') \ + $(shell find ./pkg/tools/benchmark/job -type f -name '*.go' -not -name '*_test.go' -not -name 'doc.go') + CFLAGS="$(CFLAGS)" \ + CXXFLAGS="$(CXXFLAGS)" \ + CGO_ENABLED=1 \ + CGO_CXXFLAGS="-g -Ofast -march=native" \ + CGO_FFLAGS="-g -Ofast -march=native" \ + CGO_LDFLAGS="-g -Ofast -march=native" \ + GO111MODULE=on \ + GOPRIVATE=$(GOPRIVATE) \ + go build \ + --ldflags "-s -w \ + -X '$(GOPKG)/internal/info.Version=$(VERSION)' \ + -X '$(GOPKG)/internal/info.GitCommit=$(GIT_COMMIT)' \ + -X '$(GOPKG)/internal/info.BuildTime=$(DATETIME)' \ + -X '$(GOPKG)/internal/info.GoVersion=$(GO_VERSION)' \ + -X '$(GOPKG)/internal/info.GoOS=$(GOOS)' \ + -X '$(GOPKG)/internal/info.GoArch=$(GOARCH)' \ + -X '$(GOPKG)/internal/info.CGOEnabled=$${CGO_ENABLED}' \ + -X '$(GOPKG)/internal/info.NGTVersion=$(NGT_VERSION)' \ + -X '$(GOPKG)/internal/info.BuildCPUInfoFlags=$(CPU_INFO_FLAGS)' \ + -buildid=" \ + -mod=readonly \ + -modcacherw \ + -a \ + -tags "cgo osusergo netgo" \ + -trimpath \ + -o $@ \ + $(dir $@)main.go + $@ -version + +cmd/tools/benchmark/operator/operator: \ + $(GO_SOURCES_INTERNAL) \ + $(PBGOS) \ + $(shell find ./cmd/tools/benchmark/operator -type f -name '*.go' -not -name '*_test.go' -not -name 'doc.go') \ + $(shell find ./pkg/tools/benchmark/operator -type f -name '*.go' -not -name '*_test.go' -not -name 'doc.go') + CFLAGS="$(CFLAGS)" \ + CXXFLAGS="$(CXXFLAGS)" \ + CGO_ENABLED=1 \ + CGO_CXXFLAGS="-g -Ofast -march=native" \ + CGO_FFLAGS="-g -Ofast -march=native" \ + CGO_LDFLAGS="-g -Ofast -march=native" \ + -X '$(GOPKG)/internal/info.CGOEnabled=$${CGO_ENABLED}' \ + -X '$(GOPKG)/internal/info.NGTVersion=$(NGT_VERSION)' \ + -X '$(GOPKG)/internal/info.BuildCPUInfoFlags=$(CPU_INFO_FLAGS)' \ + -buildid=" \ + -mod=readonly \ + -modcacherw \ + -a \ + -tags "cgo osusergo netgo" \ + -trimpath \ + -o $@ \ + $(dir $@)main.go + $@ -version + .PHONY: binary/build/zip ## build all binaries and zip them binary/build/zip: \ @@ -269,3 +329,10 @@ artifacts/vald-manager-index-$(GOOS)-$(GOARCH).zip: cmd/manager/index/index $(call mkdir, $(dir $@)) zip --junk-paths $@ $< +artifacts/vald-benchmark-job-$(GOOS)-$(GOARCH).zip: cmd/tools/benchmark/job/job + $(call mkdir, $(dir $@)) + zip --junk-paths $@ $< + +artifacts/vald-benchmark-operator-$(GOOS)-$(GOARCH).zip: cmd/tools/benchmark/operator/operator + $(call mkdir, $(dir $@)) + zip --junk-paths $@ $< diff --git a/Makefile.d/docker.mk b/Makefile.d/docker.mk index 5771c857e6..fc5597bd21 100644 --- a/Makefile.d/docker.mk +++ b/Makefile.d/docker.mk @@ -22,7 +22,9 @@ docker/build: \ docker/build/gateway-lb \ docker/build/gateway-filter \ docker/build/manager-index \ - docker/build/operator/helm + docker/build/benchmark-job \ + docker/build/benchmark-operator \ + docker/build/helm-operator .PHONY: docker/name/org docker/name/org: @@ -202,3 +204,33 @@ docker/build/index-correction: -t $(ORG)/$(INDEX_CORRECTION_IMAGE):$(TAG) . \ --build-arg MAINTAINER=$(MAINTAINER) \ --build-arg GO_VERSION=$(GO_VERSION) + +.PHONY: docker/name/benchmark-job +docker/name/benchmark-job: + @echo "$(ORG)/$(BENCHMARK_JOB_IMAGE)" + +.PHONY: docker/build/benchmark-job +## build benchmark job +docker/build/benchmark-job: + $(DOCKER) build \ + $(DOCKER_OPTS) \ + -f dockers/tools/benchmark/job/Dockerfile \ + -t $(ORG)/$(BENCHMARK_JOB_IMAGE):$(TAG) . \ + --build-arg GO_VERSION=$(GO_VERSION) \ + --build-arg DISTROLESS_IMAGE=$(DISTROLESS_IMAGE) \ + --build-arg DISTROLESS_IMAGE_TAG=$(DISTROLESS_IMAGE_TAG) + +.PHONY: docker/name/benchmark-operator +docker/name/benchmark-operator: + @echo "$(ORG)/$(BENCHMARK_OPERATOR_IMAGE)" + +.PHONY: docker/build/benchmark-operator +## build benchmark operator +docker/build/benchmark-operator: + $(DOCKER) build \ + $(DOCKER_OPTS) \ + -f dockers/tools/benchmark/operator/Dockerfile \ + -t $(ORG)/$(BENCHMARK_OPERATOR_IMAGE):$(TAG) . \ + --build-arg GO_VERSION=$(GO_VERSION) \ + --build-arg DISTROLESS_IMAGE=$(DISTROLESS_IMAGE) \ + --build-arg DISTROLESS_IMAGE_TAG=$(DISTROLESS_IMAGE_TAG) diff --git a/Makefile.d/helm.mk b/Makefile.d/helm.mk index 663a71ed54..3123fee36d 100644 --- a/Makefile.d/helm.mk +++ b/Makefile.d/helm.mk @@ -90,6 +90,17 @@ charts/vald-helm-operator/values.schema.json: \ GOPRIVATE=$(GOPRIVATE) \ go run -mod=readonly hack/helm/schema/gen/main.go charts/vald-helm-operator/values.yaml > charts/vald-helm-operator/values.schema.json +.PHONY: helm/schema/vald-benchmark-operator +## generate json schema for Vald Benchmark Operator Chart +helm/schema/vald-benchmark-operator: charts/vald-benchmark-operator/values.schema.json + +charts/vald-benchmark-operator/values.schema.json: \ + charts/vald-benchmark-operator/values.yaml \ + hack/helm/schema/gen/main.go + GOPRIVATE=$(GOPRIVATE) \ + go run -mod=readonly hack/helm/schema/gen/main.go charts/vald-benchmark-operator/values.yaml > charts/vald-benchmark-operator/values.schema.json + + .PHONY: yq/install ## install yq yq/install: $(BINDIR)/yq @@ -121,3 +132,14 @@ helm/schema/crd/vald-helm-operator: \ charts/vald-helm-operator/values.yaml > $(TEMP_DIR)/valdhelmoperatorrelease-spec.yaml $(BINDIR)/yq eval-all 'select(fileIndex==0).spec.versions[0].schema.openAPIV3Schema.properties.spec = select(fileIndex==1).spec | select(fileIndex==0)' \ $(TEMP_DIR)/valdhelmoperatorrelease.yaml $(TEMP_DIR)/valdhelmoperatorrelease-spec.yaml > charts/vald-helm-operator/crds/valdhelmoperatorrelease.yaml + +.PHONY: helm/schema/crd/vald-benchmark-operator +## generate OpenAPI v3 schema for ValdBenchmarkOperatorRelease +helm/schema/crd/vald-benchmark-operator: \ + yq/install + mv charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml $(TEMP_DIR)/valdbenchmarkoperatorrelease.yaml + GOPRIVATE=$(GOPRIVATE) \ + go run -mod=readonly hack/helm/schema/crd/main.go \ + charts/vald-benchmark-operator/values.yaml > $(TEMP_DIR)/valdbenchmarkoperatorrelease-spec.yaml + $(BINDIR)/yq eval-all 'select(fileIndex==0).spec.versions[0].schema.openAPIV3Schema.properties.spec = select(fileIndex==1).spec | select(fileIndex==0)' \ + $(TEMP_DIR)/valdbenchmarkoperatorrelease.yaml $(TEMP_DIR)/valdbenchmarkoperatorrelease-spec.yaml > charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml diff --git a/apis/docs/v1/docs.md b/apis/docs/v1/docs.md index f918850f3a..83195cb6da 100644 --- a/apis/docs/v1/docs.md +++ b/apis/docs/v1/docs.md @@ -125,6 +125,9 @@ - [RequestInfo](#rpc-v1-RequestInfo) - [ResourceInfo](#rpc-v1-ResourceInfo) - [RetryInfo](#rpc-v1-RetryInfo) +- [apis/proto/v1/benchmark/benchmark.proto](#apis_proto_v1_benchmark_benchmark-proto) + - [Controller](#benchmark-v1-Controller) + - [Job](#benchmark-v1-Job) - [Scalar Value Types](#scalar-value-types) diff --git a/apis/grpc/v1/benchmark/benchmark.pb.go b/apis/grpc/v1/benchmark/benchmark.pb.go new file mode 100644 index 0000000000..62c2735b80 --- /dev/null +++ b/apis/grpc/v1/benchmark/benchmark.pb.go @@ -0,0 +1,87 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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. +// + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc v3.6.1 +// source: apis/proto/v1/benchmark/benchmark.proto + +package benchmark + +import ( + reflect "reflect" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +var File_apis_proto_v1_benchmark_benchmark_proto protoreflect.FileDescriptor + +var file_apis_proto_v1_benchmark_benchmark_proto_rawDesc = []byte{ + 0x0a, 0x27, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x2f, + 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x2f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, + 0x61, 0x72, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x62, 0x65, 0x6e, 0x63, 0x68, + 0x6d, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x32, 0x0c, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x32, 0x05, 0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x42, 0x5c, 0x0a, 0x1f, + 0x6f, 0x72, 0x67, 0x2e, 0x76, 0x64, 0x61, 0x61, 0x73, 0x2e, 0x76, 0x61, 0x6c, 0x64, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x42, + 0x09, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x64, 0x61, 0x61, 0x73, 0x2f, 0x76, + 0x61, 0x6c, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x76, 0x31, + 0x2f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var file_apis_proto_v1_benchmark_benchmark_proto_goTypes = []interface{}{} +var file_apis_proto_v1_benchmark_benchmark_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_apis_proto_v1_benchmark_benchmark_proto_init() } +func file_apis_proto_v1_benchmark_benchmark_proto_init() { + if File_apis_proto_v1_benchmark_benchmark_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_apis_proto_v1_benchmark_benchmark_proto_rawDesc, + NumEnums: 0, + NumMessages: 0, + NumExtensions: 0, + NumServices: 2, + }, + GoTypes: file_apis_proto_v1_benchmark_benchmark_proto_goTypes, + DependencyIndexes: file_apis_proto_v1_benchmark_benchmark_proto_depIdxs, + }.Build() + File_apis_proto_v1_benchmark_benchmark_proto = out.File + file_apis_proto_v1_benchmark_benchmark_proto_rawDesc = nil + file_apis_proto_v1_benchmark_benchmark_proto_goTypes = nil + file_apis_proto_v1_benchmark_benchmark_proto_depIdxs = nil +} diff --git a/apis/grpc/v1/benchmark/benchmark_vtproto.pb.go b/apis/grpc/v1/benchmark/benchmark_vtproto.pb.go new file mode 100644 index 0000000000..450737e354 --- /dev/null +++ b/apis/grpc/v1/benchmark/benchmark_vtproto.pb.go @@ -0,0 +1,132 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 benchmark + +import ( + grpc "google.golang.org/grpc" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// ControllerClient is the client API for Controller service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ControllerClient interface { +} + +type controllerClient struct { + cc grpc.ClientConnInterface +} + +func NewControllerClient(cc grpc.ClientConnInterface) ControllerClient { + return &controllerClient{cc} +} + +// ControllerServer is the server API for Controller service. +// All implementations must embed UnimplementedControllerServer +// for forward compatibility +type ControllerServer interface { + mustEmbedUnimplementedControllerServer() +} + +// UnimplementedControllerServer must be embedded to have forward compatible implementations. +type UnimplementedControllerServer struct { +} + +func (UnimplementedControllerServer) mustEmbedUnimplementedControllerServer() {} + +// UnsafeControllerServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ControllerServer will +// result in compilation errors. +type UnsafeControllerServer interface { + mustEmbedUnimplementedControllerServer() +} + +func RegisterControllerServer(s grpc.ServiceRegistrar, srv ControllerServer) { + s.RegisterService(&Controller_ServiceDesc, srv) +} + +// Controller_ServiceDesc is the grpc.ServiceDesc for Controller service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Controller_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "benchmark.v1.Controller", + HandlerType: (*ControllerServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{}, + Metadata: "apis/proto/v1/benchmark/benchmark.proto", +} + +// JobClient is the client API for Job service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type JobClient interface { +} + +type jobClient struct { + cc grpc.ClientConnInterface +} + +func NewJobClient(cc grpc.ClientConnInterface) JobClient { + return &jobClient{cc} +} + +// JobServer is the server API for Job service. +// All implementations must embed UnimplementedJobServer +// for forward compatibility +type JobServer interface { + mustEmbedUnimplementedJobServer() +} + +// UnimplementedJobServer must be embedded to have forward compatible implementations. +type UnimplementedJobServer struct { +} + +func (UnimplementedJobServer) mustEmbedUnimplementedJobServer() {} + +// UnsafeJobServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to JobServer will +// result in compilation errors. +type UnsafeJobServer interface { + mustEmbedUnimplementedJobServer() +} + +func RegisterJobServer(s grpc.ServiceRegistrar, srv JobServer) { + s.RegisterService(&Job_ServiceDesc, srv) +} + +// Job_ServiceDesc is the grpc.ServiceDesc for Job service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Job_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "benchmark.v1.Job", + HandlerType: (*JobServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{}, + Metadata: "apis/proto/v1/benchmark/benchmark.proto", +} diff --git a/apis/proto/v1/benchmark/benchmark.proto b/apis/proto/v1/benchmark/benchmark.proto new file mode 100644 index 0000000000..f798b27e05 --- /dev/null +++ b/apis/proto/v1/benchmark/benchmark.proto @@ -0,0 +1,32 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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. +// + +syntax = "proto3"; + +package benchmark.v1; + +option go_package = "github.com/vdaas/vald/apis/grpc/v1/benchmark"; +option java_multiple_files = true; +option java_package = "org.vdaas.vald.api.v1.benchmark"; +option java_outer_classname = "Benchmark"; + +service Controller { + // TODO define API spec here +} + +service Job { + // TODO define API spec here +} diff --git a/apis/swagger/v1/benchmark/apis/proto/v1/benchmark/benchmark.swagger.json b/apis/swagger/v1/benchmark/apis/proto/v1/benchmark/benchmark.swagger.json new file mode 100644 index 0000000000..85c012ecac --- /dev/null +++ b/apis/swagger/v1/benchmark/apis/proto/v1/benchmark/benchmark.swagger.json @@ -0,0 +1,45 @@ +{ + "swagger": "2.0", + "info": { + "title": "apis/proto/v1/benchmark/benchmark.proto", + "version": "version not set" + }, + "consumes": ["application/json"], + "produces": ["application/json"], + "paths": {}, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "typeUrl": { + "type": "string" + }, + "value": { + "type": "string", + "format": "byte" + } + } + }, + "runtimeError": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + } + } +} diff --git a/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml b/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml new file mode 100644 index 0000000000..ae88b72dc5 --- /dev/null +++ b/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml @@ -0,0 +1,225 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: valdbenchmarkscenarios.vald.vdaas.org +spec: + group: vald.vdaas.org + names: + kind: ValdBenchmarkScenario + listKind: ValdBenchmarkScenarioList + plural: valdbenchmarkscenarios + singular: valdbenchmarkscenario + shortNames: + - vbo + - vbos + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - jsonPath: .status + name: STATUS + type: string + schema: + openAPIV3Schema: + description: ValdBenchmarkScenario is the Schema for the valdbenchmarkscenarios API + type: object + properties: + apiVersion: + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + status: + description: ValdBenchmarkScenarioStatus defines the observed state of ValdBenchmarkScenario + enum: + - NotReady + - Available + - Healthy + type: string + spec: + type: object + properties: + client_config: + type: object + properties: + addrs: + type: array + items: + type: string + backoff: + type: object + properties: + backoff_factor: + type: number + backoff_time_limit: + type: string + enable_error_log: + type: boolean + initial_duration: + type: string + jitter_limit: + type: string + maximum_duration: + type: string + retry_count: + type: integer + call_option: + type: object + x-kubernetes-preserve-unknown-fields: true + circuit_breaker: + type: object + properties: + closed_error_rate: + type: number + closed_refresh_timeout: + type: string + half_open_error_rate: + type: number + min_samples: + type: integer + open_timeout: + type: string + connection_pool: + type: object + properties: + enable_dns_resolver: + type: boolean + enable_rebalance: + type: boolean + old_conn_close_duration: + type: string + rebalance_duration: + type: string + size: + type: integer + dial_option: + type: object + properties: + backoff_base_delay: + type: string + backoff_jitter: + type: number + backoff_max_delay: + type: string + backoff_multiplier: + type: number + enable_backoff: + type: boolean + initial_connection_window_size: + type: integer + initial_window_size: + type: integer + insecure: + type: boolean + interceptors: + type: array + items: + type: string + enum: + - TraceInterceptor + keepalive: + type: object + properties: + permit_without_stream: + type: boolean + time: + type: string + timeout: + type: string + max_msg_size: + type: integer + min_connection_timeout: + type: string + net: + type: object + properties: + dialer: + type: object + properties: + dual_stack_enabled: + type: boolean + keepalive: + type: string + timeout: + type: string + dns: + type: object + properties: + cache_enabled: + type: boolean + cache_expiration: + type: string + refresh_duration: + type: string + socket_option: + type: "" + tls: + type: "" + read_buffer_size: + type: integer + timeout: + type: string + write_buffer_size: + type: integer + health_check_duration: + type: string + max_recv_msg_size: + type: integer + max_retry_rpc_buffer_size: + type: integer + max_send_msg_size: + type: integer + tls: + type: "" + wait_for_ready: + type: boolean + dataset: + type: object + properties: + group: + type: string + indexes: + type: integer + name: + type: string + range: + type: object + properties: + end: + type: integer + start: + type: integer + jobs: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + target: + type: object + properties: + host: + type: string + port: + type: integer diff --git a/charts/vald-benchmark-operator/values.schema.json b/charts/vald-benchmark-operator/values.schema.json new file mode 100644 index 0000000000..0764934803 --- /dev/null +++ b/charts/vald-benchmark-operator/values.schema.json @@ -0,0 +1,263 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Values", + "type": "object", + "properties": { + "client_config": { + "type": "object", + "properties": { + "addrs": { + "type": "array", + "description": "gRPC client addresses", + "items": { "type": "string" } + }, + "backoff": { + "type": "object", + "properties": { + "backoff_factor": { + "type": "number", + "description": "gRPC client backoff factor" + }, + "backoff_time_limit": { + "type": "string", + "description": "gRPC client backoff time limit" + }, + "enable_error_log": { + "type": "boolean", + "description": "gRPC client backoff log enabled" + }, + "initial_duration": { + "type": "string", + "description": "gRPC client backoff initial duration" + }, + "jitter_limit": { + "type": "string", + "description": "gRPC client backoff jitter limit" + }, + "maximum_duration": { + "type": "string", + "description": "gRPC client backoff maximum duration" + }, + "retry_count": { + "type": "integer", + "description": "gRPC client backoff retry count" + } + } + }, + "call_option": { "type": "object" }, + "circuit_breaker": { + "type": "object", + "properties": { + "closed_error_rate": { + "type": "number", + "description": "gRPC client circuitbreaker closed error rate" + }, + "closed_refresh_timeout": { + "type": "string", + "description": "gRPC client circuitbreaker closed refresh timeout" + }, + "half_open_error_rate": { + "type": "number", + "description": "gRPC client circuitbreaker half-open error rate" + }, + "min_samples": { + "type": "integer", + "description": "gRPC client circuitbreaker minimum sampling count" + }, + "open_timeout": { + "type": "string", + "description": "gRPC client circuitbreaker open timeout" + } + } + }, + "connection_pool": { + "type": "object", + "properties": { + "enable_dns_resolver": { + "type": "boolean", + "description": "enables gRPC client connection pool dns resolver, when enabled vald uses ip handshake exclude dns discovery which improves network performance" + }, + "enable_rebalance": { + "type": "boolean", + "description": "enables gRPC client connection pool rebalance" + }, + "old_conn_close_duration": { + "type": "string", + "description": "makes delay before gRPC client connection closing during connection pool rebalance" + }, + "rebalance_duration": { + "type": "string", + "description": "gRPC client connection pool rebalance duration" + }, + "size": { + "type": "integer", + "description": "gRPC client connection pool size" + } + } + }, + "dial_option": { + "type": "object", + "properties": { + "backoff_base_delay": { + "type": "string", + "description": "gRPC client dial option base backoff delay" + }, + "backoff_jitter": { + "type": "number", + "description": "gRPC client dial option base backoff delay" + }, + "backoff_max_delay": { + "type": "string", + "description": "gRPC client dial option max backoff delay" + }, + "backoff_multiplier": { + "type": "number", + "description": "gRPC client dial option base backoff delay" + }, + "enable_backoff": { + "type": "boolean", + "description": "gRPC client dial option backoff enabled" + }, + "initial_connection_window_size": { + "type": "integer", + "description": "gRPC client dial option initial connection window size" + }, + "initial_window_size": { + "type": "integer", + "description": "gRPC client dial option initial window size" + }, + "insecure": { + "type": "boolean", + "description": "gRPC client dial option insecure enabled" + }, + "interceptors": { + "type": "array", + "description": "gRPC client interceptors", + "items": { "type": "string", "enum": ["TraceInterceptor"] } + }, + "keepalive": { + "type": "object", + "properties": { + "permit_without_stream": { + "type": "boolean", + "description": "gRPC client keep alive permit without stream" + }, + "time": { + "type": "string", + "description": "gRPC client keep alive time" + }, + "timeout": { + "type": "string", + "description": "gRPC client keep alive timeout" + } + } + }, + "max_msg_size": { + "type": "integer", + "description": "gRPC client dial option max message size" + }, + "min_connection_timeout": { + "type": "string", + "description": "gRPC client dial option minimum connection timeout" + }, + "net": { + "type": "object", + "properties": { + "dialer": { + "type": "object", + "properties": { + "dual_stack_enabled": { + "type": "boolean", + "description": "gRPC client TCP dialer dual stack enabled" + }, + "keepalive": { + "type": "string", + "description": "gRPC client TCP dialer keep alive" + }, + "timeout": { + "type": "string", + "description": "gRPC client TCP dialer timeout" + } + } + }, + "dns": { + "type": "object", + "properties": { + "cache_enabled": { + "type": "boolean", + "description": "gRPC client TCP DNS cache enabled" + }, + "cache_expiration": { + "type": "string", + "description": "gRPC client TCP DNS cache expiration" + }, + "refresh_duration": { + "type": "string", + "description": "gRPC client TCP DNS cache refresh duration" + } + } + }, + "socket_option": { "type": "" }, + "tls": { "type": "" } + } + }, + "read_buffer_size": { + "type": "integer", + "description": "gRPC client dial option read buffer size" + }, + "timeout": { + "type": "string", + "description": "gRPC client dial option timeout" + }, + "write_buffer_size": { + "type": "integer", + "description": "gRPC client dial option write buffer size" + } + } + }, + "health_check_duration": { + "type": "string", + "description": "gRPC client health check duration" + }, + "max_recv_msg_size": { "type": "integer" }, + "max_retry_rpc_buffer_size": { "type": "integer" }, + "max_send_msg_size": { "type": "integer" }, + "tls": { "type": "" }, + "wait_for_ready": { "type": "boolean" } + } + }, + "dataset": { + "type": "object", + "description": "dataset information", + "properties": { + "group": { + "type": "string", + "description": "the hdf5 group name of dataset" + }, + "indexes": { + "type": "integer", + "description": "the amount of indexes" + }, + "name": { "type": "string", "description": "the name of dataset" }, + "range": { + "type": "object", + "description": "the data range of indexes", + "properties": { + "end": { "type": "integer", "description": "end index number" }, + "start": { "type": "integer", "description": "start index number" } + } + } + } + }, + "jobs": { + "type": "array", + "description": "benchmark jobs", + "items": { "type": "object" } + }, + "target": { + "type": "array", + "description": "target cluster host\u0026port", + "items": { "type": "object" } + } + } +} diff --git a/charts/vald-benchmark-operator/values.yaml b/charts/vald-benchmark-operator/values.yaml new file mode 100644 index 0000000000..064136e226 --- /dev/null +++ b/charts/vald-benchmark-operator/values.yaml @@ -0,0 +1,274 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# + +# @schema {"name": "dataset", "type": "object"} +# dataset -- dataset information +dataset: + # @schema {"name": "dataset.name", "type": "string" } + # dataset.name -- the name of dataset + name: "fashion-mnist" + # @schema {"name": "dataset.indexes", "type": "integer"} + # dataset.indexes -- the amount of indexes + indexes: 10000 + # @schema {"name": "dataset.group", "type": "string"} + # dataset.group -- the hdf5 group name of dataset + group: "train" + # @schema {"name": "dataset.range", "type": "object"} + # dataset.range -- the data range of indexes + range: + # @schema {"name": "dataset.range.start", "type": "integer"} + # dataset.range.start -- start index number + start: 0 + # @schema {"name": "dataset.range.end", "type": "integer"} + # dataset.range.end -- end index number + end: 10000 + +# @schema {"name": "target", "type": "object"} +# target -- target cluster host&port +target: + # @schema {"name": "target.host", "type": "string"} + # target.host -- target cluster host + host: "vald-lb-gateway.vald.svc.cluster.local" + # @schema {"name": "target.port", "type": "integer"} + # target.port -- target cluster port + port: 8081 + +# @schema {"name": "jobs", "type": "array", "items": {"type": "object"}} +# jobs -- benchmark jobs +jobs: + # @schema {"name": "jobs.items.dataset", "type": "object"} + - dataset: + name: "fashion-mnist" + indexes: 10000 + group: "train" + range: + start: 0 + end: 10000 + repetition: 1 + replica: 1 + dimension: 784 + target: + host: "localhost" + port: "8081" + rules: [] + search_config: + epsilon: 0.1 + radius: -1 + num: 10 + min_num: 5 + timeout: "1m" + # @schema {"name": "client_config", "type": "object", "anchor": "client_config"} + client_config: + # @schema {"name": "client_config.addrs", "type": "array", "items": {"type": "string"}} + # client_config.addrs -- gRPC client addresses + addrs: [] + # @schema {"name": "client_config.health_check_duration", "type": "string"} + # client_config.health_check_duration -- gRPC client health check duration + health_check_duration: "1s" + # @schema {"name": "client_config.connection_pool", "type": "object"} + connection_pool: + # @schema {"name": "client_config.connection_pool.enable_dns_resolver", "type": "boolean"} + # client_config.connection_pool.enable_dns_resolver -- enables gRPC client connection pool dns resolver, when enabled vald uses ip handshake exclude dns discovery which improves network performance + enable_dns_resolver: true + # @schema {"name": "client_config.connection_pool.enable_rebalance", "type": "boolean"} + # client_config.connection_pool.enable_rebalance -- enables gRPC client connection pool rebalance + enable_rebalance: true + # @schema {"name": "client_config.connection_pool.rebalance_duration", "type": "string"} + # client_config.connection_pool.rebalance_duration -- gRPC client connection pool rebalance duration + rebalance_duration: 30m + # @schema {"name": "client_config.connection_pool.size", "type": "integer"} + # client_config.connection_pool.size -- gRPC client connection pool size + size: 3 + # @schema {"name": "client_config.connection_pool.old_conn_close_duration", "type": "string"} + # client_config.connection_pool.old_conn_close_duration -- makes delay before gRPC client connection closing during connection pool rebalance + old_conn_close_duration: "2m" + # @schema {"name": "client_config.backoff", "type": "object", "anchor": "backoff"} + backoff: + # @schema {"name": "client_config.backoff.initial_duration", "type": "string"} + # client_config.backoff.initial_duration -- gRPC client backoff initial duration + initial_duration: 5ms + # @schema {"name": "client_config.backoff.backoff_time_limit", "type": "string"} + # client_config.backoff.backoff_time_limit -- gRPC client backoff time limit + backoff_time_limit: 5s + # @schema {"name": "client_config.backoff.maximum_duration", "type": "string"} + # client_config.backoff.maximum_duration -- gRPC client backoff maximum duration + maximum_duration: 5s + # @schema {"name": "client_config.backoff.jitter_limit", "type": "string"} + # client_config.backoff.jitter_limit -- gRPC client backoff jitter limit + jitter_limit: 100ms + # @schema {"name": "client_config.backoff.backoff_factor", "type": "number"} + # client_config.backoff.backoff_factor -- gRPC client backoff factor + backoff_factor: 1.1 + # @schema {"name": "client_config.backoff.retry_count", "type": "integer"} + # client_config.backoff.retry_count -- gRPC client backoff retry count + retry_count: 100 + # @schema {"name": "client_config.backoff.enable_error_log", "type": "boolean"} + # client_config.backoff.enable_error_log -- gRPC client backoff log enabled + enable_error_log: true + # @schema {"name": "client_config.circuit_breaker", "type": "object"} + circuit_breaker: + # @schema {"name": "client_config.circuit_breaker.closed_error_rate", "type": "number"} + # client_config.circuit_breaker.closed_error_rate -- gRPC client circuitbreaker closed error rate + closed_error_rate: 0.7 + # @schema {"name": "client_config.circuit_breaker.half_open_error_rate", "type": "number"} + # client_config.circuit_breaker.half_open_error_rate -- gRPC client circuitbreaker half-open error rate + half_open_error_rate: 0.5 + # @schema {"name": "client_config.circuit_breaker.min_samples", "type": "integer"} + # client_config.circuit_breaker.min_samples -- gRPC client circuitbreaker minimum sampling count + min_samples: 1000 + # @schema {"name": "client_config.circuit_breaker.open_timeout", "type": "string"} + # client_config.circuit_breaker.open_timeout -- gRPC client circuitbreaker open timeout + open_timeout: "1s" + # @schema {"name": "client_config.circuit_breaker.closed_refresh_timeout", "type": "string"} + # client_config.circuit_breaker.closed_refresh_timeout -- gRPC client circuitbreaker closed refresh timeout + closed_refresh_timeout: "10s" + # @schema {"name": "client_config.call_option", "type": "object"} + call_option: + # @schema {"name": "client_config.wait_for_ready", "type": "boolean"} + # client_config.call_option.wait_for_ready -- gRPC client call option wait for ready + wait_for_ready: true + # @schema {"name": "client_config.max_retry_rpc_buffer_size", "type": "integer"} + # client_config.call_option.max_retry_rpc_buffer_size -- gRPC client call option max retry rpc buffer size + max_retry_rpc_buffer_size: 0 + # @schema {"name": "client_config.max_recv_msg_size", "type": "integer"} + # client_config.call_option.max_recv_msg_size -- gRPC client call option max receive message size + max_recv_msg_size: 0 + # @schema {"name": "client_config.max_send_msg_size", "type": "integer"} + # client_config.call_option.max_send_msg_size -- gRPC client call option max send message size + max_send_msg_size: 0 + # @schema {"name": "client_config.dial_option", "type": "object"} + dial_option: + # @schema {"name": "client_config.dial_option.write_buffer_size", "type": "integer"} + # client_config.dial_option.write_buffer_size -- gRPC client dial option write buffer size + write_buffer_size: 0 + # @schema {"name": "client_config.dial_option.read_buffer_size", "type": "integer"} + # client_config.dial_option.read_buffer_size -- gRPC client dial option read buffer size + read_buffer_size: 0 + # @schema {"name": "client_config.dial_option.initial_window_size", "type": "integer"} + # client_config.dial_option.initial_window_size -- gRPC client dial option initial window size + initial_window_size: 0 + # @schema {"name": "client_config.dial_option.initial_connection_window_size", "type": "integer"} + # client_config.dial_option.initial_connection_window_size -- gRPC client dial option initial connection window size + initial_connection_window_size: 0 + # @schema {"name": "client_config.dial_option.max_msg_size", "type": "integer"} + # client_config.dial_option.max_msg_size -- gRPC client dial option max message size + max_msg_size: 0 + # @schema {"name": "client_config.dial_option.backoff_max_delay", "type": "string"} + # client_config.dial_option.backoff_max_delay -- gRPC client dial option max backoff delay + backoff_max_delay: "120s" + # @schema {"name": "client_config.dial_option.backoff_base_delay", "type": "string"} + # client_config.dial_option.backoff_base_delay -- gRPC client dial option base backoff delay + backoff_base_delay: "1s" + # @schema {"name": "client_config.dial_option.backoff_multiplier", "type": "number"} + # client_config.dial_option.backoff_multiplier -- gRPC client dial option base backoff delay + backoff_multiplier: 1.6 + # @schema {"name": "client_config.dial_option.backoff_jitter", "type": "number"} + # client_config.dial_option.backoff_jitter -- gRPC client dial option base backoff delay + backoff_jitter: 0.2 + # @schema {"name": "client_config.dial_option.min_connection_timeout", "type": "string"} + # client_config.dial_option.min_connection_timeout -- gRPC client dial option minimum connection timeout + min_connection_timeout: "20s" + # @schema {"name": "client_config.dial_option.enable_backoff", "type": "boolean"} + # client_config.dial_option.enable_backoff -- gRPC client dial option backoff enabled + enable_backoff: false + # @schema {"name": "client_config.dial_option.insecure", "type": "boolean"} + # client_config.dial_option.insecure -- gRPC client dial option insecure enabled + insecure: true + # @schema {"name": "client_config.dial_option.timeout", "type": "string"} + # client_config.dial_option.timeout -- gRPC client dial option timeout + timeout: "" + # @schema {"name": "client_config.dial_option.interceptors", "type": "array", "items": {"type": "string", "enum": ["TraceInterceptor"]}} + # client_config.dial_option.interceptors -- gRPC client interceptors + interceptors: [] + # @schema {"name": "client_config.dial_option.net", "type": "object", "anchor": "net"} + net: + # @schema {"name": "client_config.dial_option.net.dns", "type": "object"} + dns: + # @schema {"name": "client_config.dial_option.net.dns.cache_enabled", "type": "boolean"} + # client_config.dial_option.net.dns.cache_enabled -- gRPC client TCP DNS cache enabled + cache_enabled: true + # @schema {"name": "client_config.dial_option.net.dns.refresh_duration", "type": "string"} + # client_config.dial_option.net.dns.refresh_duration -- gRPC client TCP DNS cache refresh duration + refresh_duration: 30m + # @schema {"name": "client_config.dial_option.net.dns.cache_expiration", "type": "string"} + # client_config.dial_option.net.dns.cache_expiration -- gRPC client TCP DNS cache expiration + cache_expiration: 1h + # @schema {"name": "client_config.dial_option.net.dialer", "type": "object"} + dialer: + # @schema {"name": "client_config.dial_option.net.dialer.timeout", "type": "string"} + # client_config.dial_option.net.dialer.timeout -- gRPC client TCP dialer timeout + timeout: "" + # @schema {"name": "client_config.dial_option.net.dialer.keepalive", "type": "string"} + # client_config.dial_option.net.dialer.keepalive -- gRPC client TCP dialer keep alive + keepalive: "" + # @schema {"name": "client_config.dial_option.net.dialer.dual_stack_enabled", "type": "boolean"} + # client_config.dial_option.net.dialer.dual_stack_enabled -- gRPC client TCP dialer dual stack enabled + dual_stack_enabled: true + # @schema {"name": "client_config.dial_option.net.tls"} + tls: + # client_config.dial_option.net.tls.enabled -- TLS enabled + enabled: false + # client_config.dial_option.net.tls.cert -- TLS cert path + cert: /path/to/cert + # client_config.dial_option.net.tls.key -- TLS key path + key: /path/to/key + # client_config.dial_option.net.tls.ca -- TLS ca path + ca: /path/to/ca + # client_config.dial_option.net.tls.insecure_skip_verify -- enable/disable skip SSL certificate verification + insecure_skip_verify: false + # @schema {"name": "client_config.dial_option.net.socket_option"} + socket_option: + # client_config.dial_option.net.socket_option.reuse_port -- server listen socket option for reuse_port functionality + reuse_port: true + # client_config.dial_option.net.socket_option.reuse_addr -- server listen socket option for reuse_addr functionality + reuse_addr: true + # client_config.dial_option.net.socket_option.tcp_fast_open -- server listen socket option for tcp_fast_open functionality + tcp_fast_open: true + # client_config.dial_option.net.socket_option.tcp_no_delay -- server listen socket option for tcp_no_delay functionality + tcp_no_delay: true + # client_config.dial_option.net.socket_option.tcp_cork -- server listen socket option for tcp_cork functionality + tcp_cork: false + # client_config.dial_option.net.socket_option.tcp_quick_ack -- server listen socket option for tcp_quick_ack functionality + tcp_quick_ack: true + # client_config.dial_option.net.socket_option.tcp_defer_accept -- server listen socket option for tcp_defer_accept functionality + tcp_defer_accept: true + # client_config.dial_option.net.socket_option.ip_transparent -- server listen socket option for ip_transparent functionality + ip_transparent: false + # client_config.dial_option.net.socket_option.ip_recover_destination_addr -- server listen socket option for ip_recover_destination_addr functionality + ip_recover_destination_addr: false + # @schema {"name": "client_config.dial_option.keepalive", "type": "object"} + keepalive: + # @schema {"name": "client_config.dial_option.keepalive.time", "type": "string"} + # client_config.dial_option.keepalive.time -- gRPC client keep alive time + time: "120s" + # @schema {"name": "client_config.dial_option.keepalive.timeout", "type": "string"} + # client_config.dial_option.keepalive.timeout -- gRPC client keep alive timeout + timeout: "30s" + # @schema {"name": "client_config.dial_option.keepalive.permit_without_stream", "type": "boolean"} + # client_config.dial_option.keepalive.permit_without_stream -- gRPC client keep alive permit without stream + permit_without_stream: true + # @schema {"name": "client_config.tls"} + tls: + # client_config.tls.enabled -- TLS enabled + enabled: false + # client_config.tls.cert -- TLS cert path + cert: /path/to/cert + # client_config.tls.key -- TLS key path + key: /path/to/key + # client_config.tls.ca -- TLS ca path + ca: /path/to/ca + # client_config.tls.insecure_skip_verify -- enable/disable skip SSL certificate verification + insecure_skip_verify: false diff --git a/cmd/tools/benchmark/job/main.go b/cmd/tools/benchmark/job/main.go new file mode 100644 index 0000000000..b63331da0f --- /dev/null +++ b/cmd/tools/benchmark/job/main.go @@ -0,0 +1,60 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 main provides program main +package main + +import ( + "context" + + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/info" + "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/runner" + "github.com/vdaas/vald/internal/safety" + "github.com/vdaas/vald/pkg/tools/benchmark/job/config" + "github.com/vdaas/vald/pkg/tools/benchmark/job/usecase" +) + +const ( + maxVersion = "v0.0.10" + minVersion = "v0.0.0" + name = "benchmark job" +) + +func main() { + if err := safety.RecoverFunc(func() error { + ctx := context.Background() + return runner.Do( + ctx, + runner.WithName(name), + runner.WithVersion(info.Version, maxVersion, minVersion), + runner.WithConfigLoader(func(path string) (interface{}, *config.GlobalConfig, error) { + cfg, err := config.NewConfig(ctx, path) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to load "+name+"'s configuration") + } + return cfg, &cfg.GlobalConfig, nil + }), + runner.WithDaemonInitializer(func(cfg interface{}) (runner.Runner, error) { + return usecase.New(cfg.(*config.Config)) + }), + ) + })(); err != nil { + log.Fatal(err, info.Get()) + return + } +} diff --git a/cmd/tools/benchmark/job/sample.yaml b/cmd/tools/benchmark/job/sample.yaml new file mode 100644 index 0000000000..76dd83f8ea --- /dev/null +++ b/cmd/tools/benchmark/job/sample.yaml @@ -0,0 +1,168 @@ +# +# Copyright (C) 2019-2022 vdaas.org vald team +# +# 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 +# +# https://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. +# +version: v0.0.0 +time_zone: JST +logging: + format: raw + level: debug + logger: glg +server_config: + servers: + - name: grpc + host: 0.0.0.0 + port: 8081 + grpc: + bidirectional_stream_concurrency: 20 + connection_timeout: "" + header_table_size: 0 + initial_conn_window_size: 0 + initial_window_size: 0 + interceptors: [] + keepalive: + max_conn_age: "" + max_conn_age_grace: "" + max_conn_idle: "" + time: "" + timeout: "" + max_header_list_size: 0 + max_receive_message_size: 0 + max_send_message_size: 0 + read_buffer_size: 0 + write_buffer_size: 0 + mode: GRPC + probe_wait_time: 60s + restart: true + health_check_servers: + - name: liveness + host: 0.0.0.0 + port: 3000 + http: + handler_timeout: "" + idle_timeout: "" + read_header_timeout: "" + read_timeout: "" + shutdown_duration: 5s + write_timeout: "" + mode: "" + probe_wait_time: 60s + - name: readiness + host: 0.0.0.0 + port: 3001 + http: + handler_timeout: "" + idle_timeout: "" + read_header_timeout: "" + read_timeout: "" + shutdown_duration: 0s + write_timeout: "" + mode: "" + probe_wait_time: 60s + metrics_servers: + startup_strategy: + - liveness + - grpc + - readiness + full_shutdown_duration: 600s + tls: + ca: /path/to/ca + cert: /path/to/cert + enabled: false + key: /path/to/key +observability: + enabled: false + collector: + duration: 5s + metrics: + enable_cgo: true + enable_goroutine: true + enable_memory: true + enable_version_info: true + version_info_labels: + - vald_version + - server_name + - git_commit + - build_time + - go_version + - go_os + - go_arch + - ngt_version + trace: + enabled: false + sampling_rate: 1 + prometheus: + enabled: false + endpoint: /metrics + namespace: vald + jaeger: + enabled: false + collector_endpoint: "" + agent_endpoint: "jaeger-agent.default.svc.cluster.local:6831" + username: "" + password: "" + service_name: "vald-benchmark-job" + buffer_max_count: 10 + stackdriver: + project_id: "" + client: + api_key: "" + audiences: [] + authentication_enabled: true + credentials_file: "" + credentials_json: "" + endpoint: "" + quota_project: "" + request_reason: "" + scopes: [] + telemetry_enabled: true + user_agent: "" + exporter: + bundle_count_threshold: 0 + bundle_delay_threshold: "0" + location: "" + metric_prefix: vald.vdaas.org + monitoring_enabled: false + number_of_workers: 1 + reporting_interval: 1m + skip_cmd: false + timeout: 5s + trace_spans_buffer_max_bytes: 0 + tracing_enabled: false + profiler: + enabled: false + service: "vald-benchmark-job" + service_version: "" + debug_logging: false + mutex_profiling: true + cpu_profiling: true + alloc_profiling: true + heap_profiling: true + goroutine_profiling: true + alloc_force_gc: false + api_addr: "" + instance: "" + zone: "" +job: + job_type: "search" + dimension: 784 + iter: 100 + num: 10 + minNum: 10 + radius: -1 + epsilon: 0.1 + timeout: 5s + gateway_client: + addrs: + - vald-lb-gateway.default.svc.cluster.local:8081 diff --git a/cmd/tools/benchmark/operator/main.go b/cmd/tools/benchmark/operator/main.go new file mode 100644 index 0000000000..13660db983 --- /dev/null +++ b/cmd/tools/benchmark/operator/main.go @@ -0,0 +1,59 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 main provides program main +package main + +import ( + "context" + + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/info" + "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/runner" + "github.com/vdaas/vald/internal/safety" + "github.com/vdaas/vald/pkg/tools/benchmark/operator/config" + "github.com/vdaas/vald/pkg/tools/benchmark/operator/usecase" +) + +const ( + maxVersion = "v0.0.10" + minVersion = "v0.0.0" + name = "benchmark operator" +) + +func main() { + if err := safety.RecoverFunc(func() error { + return runner.Do( + context.Background(), + runner.WithName(name), + runner.WithVersion(info.Version, maxVersion, minVersion), + runner.WithConfigLoader(func(path string) (interface{}, *config.GlobalConfig, error) { + cfg, err := config.NewConfig(path) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to load "+name+"'s configuration") + } + return cfg, &cfg.GlobalConfig, nil + }), + runner.WithDaemonInitializer(func(cfg interface{}) (runner.Runner, error) { + return usecase.New(cfg.(*config.Config)) + }), + ) + })(); err != nil { + log.Fatal(err, info.Get()) + return + } +} diff --git a/cmd/tools/benchmark/operator/sample.yaml b/cmd/tools/benchmark/operator/sample.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dockers/tools/benchmark/job/Dockerfile b/dockers/tools/benchmark/job/Dockerfile new file mode 100644 index 0000000000..4c378a90a6 --- /dev/null +++ b/dockers/tools/benchmark/job/Dockerfile @@ -0,0 +1,100 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +ARG GO_VERSION=latest +ARG DISTROLESS_IMAGE=gcr.io/distroless/static +ARG DISTROLESS_IMAGE_TAG=nonroot +ARG UPX_OPTIONS=-9 +ARG MAINTAINER="vdaas.org vald team " + +FROM golang:${GO_VERSION} AS builder + +ARG UPX_OPTIONS + +ENV GO111MODULE on +ENV LANG en_US.UTF-8 +ENV ORG vdaas +ENV REPO vald +ENV APP_NAME job +ENV PKG tools/benchmark/${APP_NAME} + +RUN apt-get update && apt-get install -y --no-install-recommends \ + upx \ + git \ + libhdf5-dev \ + && ldconfig \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN mkdir -p ${GOPATH}/src + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} + +COPY go.mod . +COPY go.sum . + +RUN go mod download + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/internal +COPY internal . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/apis/grpc +COPY apis/grpc . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/pkg/${PKG} +COPY pkg/${PKG} . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/cmd/${PKG} +COPY cmd/${PKG} . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/versions +COPY versions . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/Makefile.d +COPY Makefile.d . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} +COPY Makefile . + +COPY .git . + +RUN make REPO=${ORG} NAME=${REPO} cmd/${PKG}/${APP_NAME} \ + && upx ${UPX_OPTIONS} -o "/usr/bin/${APP_NAME}" "cmd/${PKG}/${APP_NAME}" + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/cmd/${PKG} +RUN cp sample.yaml /tmp/config.yaml + +FROM ${DISTROLESS_IMAGE}:${DISTROLESS_IMAGE_TAG} +LABEL maintainer "Vald team " +ENV APP_NAME job + +COPY --from=builder /usr/lib/x86_64-linux-gnu/libaec* /usr/lib/x86_64-linux-gnu/ +COPY --from=builder /usr/lib/x86_64-linux-gnu/libhdf5* /usr/lib/x86_64-linux-gnu/ +COPY --from=builder /usr/lib/x86_64-linux-gnu/libsz* /usr/lib/x86_64-linux-gnu/ +COPY --from=builder /usr/lib/x86_64-linux-gnu/libhdf5_serial.so /usr/lib/x86_64-linux-gnu/ +COPY --from=builder /usr/lib/x86_64-linux-gnu/libhdf5_serial_hl.so /usr/lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libpthread.so.0 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libm.so.6 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib64/ld-linux-x86-64.so.2 /lib64/ + +COPY --from=builder /usr/bin/${APP_NAME} /go/bin/${APP_NAME} +COPY --from=builder /tmp/config.yaml /etc/server/config.yaml + +USER nonroot:nonroot + +ENTRYPOINT ["/go/bin/job"] diff --git a/dockers/tools/benchmark/operator/Dockerfile b/dockers/tools/benchmark/operator/Dockerfile new file mode 100644 index 0000000000..48f0e1dc54 --- /dev/null +++ b/dockers/tools/benchmark/operator/Dockerfile @@ -0,0 +1,87 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# + +ARG GO_VERSION=latest +ARG DISTROLESS_IMAGE=gcr.io/distroless/static +ARG DISTROLESS_IMAGE_TAG=nonroot +ARG UPX_OPTIONS=-9 +ARG MAINTAINER="vdaas.org vald team " + +FROM golang:${GO_VERSION} AS builder + +ARG UPX_OPTIONS + +ENV GO111MODULE on +ENV LANG en_US.UTF-8 +ENV ORG vdaas +ENV REPO vald +ENV APP_NAME operator +ENV PKG tools/benchmark/${APP_NAME} + +RUN apt-get update && apt-get install -y --no-install-recommends \ + upx \ + git \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN mkdir -p ${GOPATH}/src + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} + +COPY go.mod . +COPY go.sum . + +RUN go mod download + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/internal +COPY internal . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/apis/grpc +COPY apis/grpc . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/pkg/${PKG} +COPY pkg/${PKG} . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/cmd/${PKG} +COPY cmd/${PKG} . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/versions +COPY versions . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/Makefile.d +COPY Makefile.d . + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} +COPY Makefile . + +COPY .git . + +RUN make REPO=${ORG} NAME=${REPO} cmd/${PKG}/${APP_NAME} \ + && upx ${UPX_OPTIONS} -o "/usr/bin/${APP_NAME}" "cmd/${PKG}/${APP_NAME}" + +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/cmd/${PKG} +RUN cp sample.yaml /tmp/config.yaml + +FROM ${DISTROLESS_IMAGE}:${DISTROLESS_IMAGE_TAG} +LABEL maintainer "Vald team " +ENV APP_NAME operator + +COPY --from=builder /usr/bin/${APP_NAME} /go/bin/${APP_NAME} +COPY --from=builder /tmp/config.yaml /etc/server/config.yaml + +USER nonroot:nonroot + +ENTRYPOINT ["/go/bin/operator"] diff --git a/internal/config/benchmark.go b/internal/config/benchmark.go new file mode 100644 index 0000000000..37bcba94b9 --- /dev/null +++ b/internal/config/benchmark.go @@ -0,0 +1,116 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 config providers configuration type and load configuration logic +package config + +// BenchmarkJob represents the configuration for the internal benchmark search job. +type BenchmarkJob struct { + Target *BenchmarkTarget `json:"target,omitempty" yaml:"target"` + Dataset *BenchmarkDataset `json:"dataset,omitempty" yaml:"dataset"` + Dimension int `json:"dimension,omitempty" yaml:"dimension"` + Replica int `json:"replica,omitempty" yaml:"replica"` + Repetition int `json:"repetition,omitempty" yaml:"repetition"` + JobType string `json:"job_type,omitempty" yaml:"job_type"` + InsertConfig *InsertConfig `json:"insert_config,omitempty" yaml:"insert_config"` + UpdateConfig *UpdateConfig `json:"update_config,omitempty" yaml:"update_config"` + UpsertConfig *UpsertConfig `json:"upsert_config,omitempty" yaml:"upsert_config"` + SearchConfig *SearchConfig `json:"search_config,omitempty" yaml:"search_config"` + RemoveConfig *RemoveConfig `json:"remove_config,omitempty" yaml:"remove_config"` + ClientConfig *GRPCClient `json:"client_config,omitempty" yaml:"client_config"` + Rules []*BenchmarkJobRule `json:"rules,omitempty" yaml:"rules"` +} + +// BenchmarkScenario represents the configuration for the internal benchmark scenario. +type BenchmarkScenario struct { + Target *BenchmarkTarget `json:"target" yaml:"target"` + Dataset *BenchmarkDataset `jon:"dataset" yaml:"dataset"` + Jobs []*BenchmarkJob `job:"jobs" yaml:jobs` +} + +// BenchmarkTarget defines the desired state of BenchmarkTarget +type BenchmarkTarget struct { + Host string `json:"host,omitempty"` + Port int `json:"port,omitempty"` +} + +// BenchmarkDataset defines the desired state of BenchmarkDateset +type BenchmarkDataset struct { + Name string `json:"name,omitempty"` + Group string `json:"group,omitempty"` + Indexes int `json:"indexes,omitempty"` + Range *BenchmarkDatasetRange `json:"range,omitempty"` +} + +// BenchmarkDatasetRange defines the desired state of BenchmarkDatesetRange +type BenchmarkDatasetRange struct { + Start int `json:"start,omitempty"` + End int `json:"end,omitempty"` +} + +// BenchmarkJobRule defines the desired state of BenchmarkJobRule +type BenchmarkJobRule struct { + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` +} + +// InsertConfig defines the desired state of insert config +type InsertConfig struct { + SkipStrictExistCheck bool `json:"skip_strict_exist_check,omitempty"` + Timestamp string `json:"timestamp,omitempty"` +} + +// UpdateConfig defines the desired state of update config +type UpdateConfig struct { + SkipStrictExistCheck bool `json:"skip_strict_exist_check,omitempty"` + Timestamp string `json:"timestamp,omitempty"` +} + +// UpsertConfig defines the desired state of upsert config +type UpsertConfig struct { + SkipStrictExistCheck bool `json:"skip_strict_exist_check,omitempty"` + Timestamp string `json:"timestamp,omitempty"` +} + +// SearchConfig defines the desired state of search config +type SearchConfig struct { + Epsilon float32 `json:"epsilon,omitempty"` + Radius float32 `json:"radius,omitempty"` + Num int32 `json:"num,omitempty"` + MinNum int32 `json:"min_num,omitempty"` + Timeout string `json:"timeout,omitempty"` +} + +// RemoveConfig defines the desired state of remove config +type RemoveConfig struct { + SkipStrictExistCheck bool `json:"skip_strict_exist_check,omitempty"` + Timestamp string `json:"timestamp,omitempty"` +} + +// Bind binds the actual data from the Job receiver fields. +func (b *BenchmarkJob) Bind() *BenchmarkJob { + b.JobType = GetActualValue(b.JobType) + + if b.ClientConfig != nil { + b.ClientConfig = b.ClientConfig.Bind() + } + return b +} + +// Bind binds the actual data from the BenchmarkScenario receiver fields. +func (b *BenchmarkScenario) Bind() *BenchmarkScenario { + return b +} diff --git a/internal/config/server.go b/internal/config/server.go index 5bbf8564ee..b6d592e9b3 100644 --- a/internal/config/server.go +++ b/internal/config/server.go @@ -39,7 +39,7 @@ type Servers struct { // StartUpStrategy represent starting order of server name StartUpStrategy []string `json:"startup_strategy" yaml:"startup_strategy"` - // ShutdownStrategy represent shutdonw order of server name + // ShutdownStrategy represent shutdown order of server name ShutdownStrategy []string `json:"shutdown_strategy" yaml:"shutdown_strategy"` // FullShutdownDuration represent summary duration of shutdown time diff --git a/internal/errors/http.go b/internal/errors/http.go index 586a3c74e8..49398dd764 100644 --- a/internal/errors/http.go +++ b/internal/errors/http.go @@ -53,4 +53,9 @@ var ( // ErrTransportRetryable represents an error that the transport is retryable. ErrTransportRetryable = New("transport is retryable") + + // ErrInvalidStatusCode represents a function to generate an error that the http status code is invalid. + ErrInvalidStatusCode = func(code int) error { + return Errorf("invalid status code: %d", code) + } ) diff --git a/internal/errors/http_test.go b/internal/errors/http_test.go index 2498de2c33..3b010df817 100644 --- a/internal/errors/http_test.go +++ b/internal/errors/http_test.go @@ -526,4 +526,65 @@ func TestErrTransportRetryable(t *testing.T) { } } +func TestErrInvalidStatusCode(t *testing.T) { + type args struct { + code int + } + type want struct { + want error + } + type test struct { + name string + args args + want want + checkFunc func(want, error) error + beforeFunc func() + afterFunc func() + } + + defaultCheckFunc := func(w want, got error) error { + if !Is(got, w.want) { + return Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + { + name: "return ErrInvalidStatusCode error when code is empty", + want: want{ + want: New("invalid status code: 0"), + }, + }, + { + name: "return ErrInvalidStatusCode error when code is 500", + args: args{ + code: 500, + }, + want: want{ + want: New("invalid status code: 500"), + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + if test.beforeFunc != nil { + test.beforeFunc() + } + if test.afterFunc != nil { + defer test.afterFunc() + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + got := ErrInvalidStatusCode(test.args.code) + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + // NOT IMPLEMENTED BELOW diff --git a/internal/k8s/client/client.go b/internal/k8s/client/client.go new file mode 100644 index 0000000000..9822d36afe --- /dev/null +++ b/internal/k8s/client/client.go @@ -0,0 +1,79 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 client is Kubernetes client for getting resource from Kubernetes cluster. +package client + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + cli "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type Client interface { + // Get retrieves an obj for the given object key from the Kubernetes Cluster. + // obj must be a struct pointer so that obj can be updated with the response + // returned by the Server. + Get(ctx context.Context, name string, namespace string, obj cli.Object, opts ...cli.GetOption) error + // List retrieves list of objects for a given namespace and list options. On a + // successful call, Items field in the list will be populated with the + // result returned from the server. + List(ctx context.Context, list cli.ObjectList, opts ...cli.ListOption) error +} + +type client struct { + scheme *runtime.Scheme + reader cli.Reader +} + +func New(opts ...Option) (Client, error) { + c := new(client) + if c.scheme == nil { + c.scheme = runtime.NewScheme() + } + for _, opt := range opts { + if err := opt(c); err != nil { + return nil, err + } + } + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), manager.Options{ + Scheme: c.scheme, + }) + if err != nil { + return nil, err + } + c.reader = mgr.GetAPIReader() + return c, nil +} + +func (c *client) Get(ctx context.Context, name string, namespace string, obj cli.Object, opts ...cli.GetOption) error { + return c.reader.Get( + ctx, + cli.ObjectKey{ + Name: name, + Namespace: namespace, + }, + obj, + opts..., + ) +} + +func (c *client) List(ctx context.Context, list cli.ObjectList, opts ...cli.ListOption) error { + return c.reader.List(ctx, list, opts...) +} diff --git a/internal/k8s/client/option.go b/internal/k8s/client/option.go new file mode 100644 index 0000000000..cae27f6775 --- /dev/null +++ b/internal/k8s/client/option.go @@ -0,0 +1,29 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 client is Kubernetes client for getting resource from Kubernetes cluster. +package client + +import "sigs.k8s.io/controller-runtime/pkg/scheme" + +type Option func(*client) error + +func WithSchemeBuilder(sb scheme.Builder) Option { + return func(c *client) error { + sb.AddToScheme(c.scheme) + return nil + } +} diff --git a/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml b/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml new file mode 100644 index 0000000000..34a92c68ca --- /dev/null +++ b/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml @@ -0,0 +1,381 @@ +# +# Copyright (C) 2019-2022 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: valdbenchmarkjobs.vald.vdaas.org +spec: + group: vald.vdaas.org + names: + kind: ValdBenchmarkJob + listKind: ValdBenchmarkJobList + plural: valdbenchmarkjobs + singular: valdbenchmarkjob + shortNames: + - vbj + - vbjs + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - jsonPath: .spec.replica + name: REPLICAS + type: integer + - jsonPath: .status + name: STATUS + type: string + schema: + openAPIV3Schema: + description: ValdBenchmarkJob is the Schema for the valdbenchmarkjobs API + type: object + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + status: + description: ValdBenchmarkJobStatus defines the observed state of ValdBenchmarkJob + enum: + - NotReady + - Available + - Healthy + type: string + spec: + description: ValdBenchmarkJobSpec defines the desired state of ValdBenchmarkJob + type: object + properties: + target: + description: BenchmarkTarget defines the desired state of BenchmarkTarget + properties: + host: + type: string + port: + type: integer + required: + - host + - port + type: object + dataset: + description: BenchmarkDataset defines the desired state of BenchmarkDateset + properties: + group: + type: string + indexes: + type: integer + name: + type: string + range: + description: BenchmarkDatasetRange defines the desired state of BenchmarkDatesetRange + properties: + end: + type: integer + start: + type: integer + required: + - end + - start + type: object + required: + - group + - indexes + - name + type: object + dimension: + type: integer + job_type: + type: string + repetition: + type: integer + replica: + type: integer + rules: + items: + description: BenchmarkJobRule defines the desired state of BenchmarkJobRule + properties: + name: + type: string + type: + type: string + required: + - name + - type + type: object + type: array + insert_config: + description: InsertConfig defines the desired state of insert config + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + type: object + remove_config: + description: RemoveConfig defines the desired state of remove config + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + type: object + search_config: + description: SearchConfig defines the desired state of search config + properties: + epsilon: + type: number + min_num: + format: int32 + type: integer + num: + format: int32 + type: integer + radius: + type: number + timeout: + type: string + type: object + update_config: + description: UpdateConfig defines the desired state of update config + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + type: object + upsert_config: + description: UpsertConfig defines the desired state of upsert config + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + type: object + client_config: + description: ClientConfig represents the configurations for gRPC client. + properties: + addrs: + items: + type: string + type: array + backoff: + description: Backoff represents the configuration for the internal backoff package. + properties: + backoff_factor: + type: number + backoff_time_limit: + type: string + enable_error_log: + type: boolean + initial_duration: + type: string + jitter_limit: + type: string + maximum_duration: + type: string + retry_count: + type: integer + type: object + call_option: + description: CallOption represents the configurations for call option. + properties: + max_recv_msg_size: + type: integer + max_retry_rpc_buffer_size: + type: integer + max_send_msg_size: + type: integer + wait_for_ready: + type: boolean + type: object + circuit_breaker: + description: CircuitBreaker represents the configuration for the internal circuitbreaker package. + properties: + closed_error_rate: + type: number + closed_refresh_timeout: + type: string + half_open_error_rate: + type: number + min_samples: + format: int64 + type: integer + open_timeout: + type: string + type: object + connection_pool: + description: ConnectionPool represents the configurations for connection pool. + properties: + enable_rebalance: + type: boolean + old_conn_close_duration: + type: string + rebalance_duration: + type: string + resolve_dns: + type: boolean + size: + type: integer + type: object + dial_option: + description: DialOption represents the configurations for dial option. + properties: + backoff_base_delay: + type: string + backoff_jitter: + type: number + backoff_max_delay: + type: string + backoff_multiplier: + type: number + enable_backoff: + type: boolean + initial_connection_window_size: + type: integer + initial_window_size: + type: integer + insecure: + type: boolean + interceptors: + items: + type: string + type: array + keepalive: + description: GRPCClientKeepalive represents the configurations for gRPC keep-alive. + properties: + permit_without_stream: + type: boolean + time: + type: string + timeout: + type: string + type: object + max_msg_size: + type: integer + minimum_connection_timeout: + type: string + net: + description: Net represents the network configuration tcp, udp, unix domain socket. + properties: + dialer: + description: Dialer represents the configuration for dial. + properties: + dual_stack_enabled: + type: boolean + fallback_delay: + type: string + keepalive: + type: string + timeout: + type: string + type: object + dns: + description: DNS represents the configuration for resolving DNS. + properties: + cache_enabled: + type: boolean + cache_expiration: + type: string + refresh_duration: + type: string + type: object + socket_option: + description: SocketOption represents the socket configurations. + properties: + ip_recover_destination_addr: + type: boolean + ip_transparent: + type: boolean + reuse_addr: + type: boolean + reuse_port: + type: boolean + tcp_cork: + type: boolean + tcp_defer_accept: + type: boolean + tcp_fast_open: + type: boolean + tcp_no_delay: + type: boolean + tcp_quick_ack: + type: boolean + type: object + tls: + description: TLS represent the TLS configuration for server. + properties: + ca: + description: CA represent the CA certificate environment variable key used to start server. + type: string + cert: + description: Cert represent the certificate environment variable key used to start server. + type: string + enabled: + description: Enable represent the server enable TLS or not. + type: boolean + insecure_skip_verify: + description: InsecureSkipVerify represent enable/disable skip SSL certificate verification + type: boolean + key: + description: Key represent the private key environment variable key used to start server. + type: string + type: object + type: object + read_buffer_size: + type: integer + timeout: + type: string + write_buffer_size: + type: integer + type: object + health_check_duration: + type: string + tls: + description: TLS represent the TLS configuration for server. + properties: + ca: + description: CA represent the CA certificate environment variable key used to start server. + type: string + cert: + description: Cert represent the certificate environment variable key used to start server. + type: string + enabled: + description: Enable represent the server enable TLS or not. + type: boolean + insecure_skip_verify: + description: InsecureSkipVerify represent enable/disable skip SSL certificate verification + type: boolean + key: + description: Key represent the private key environment variable key used to start server. + type: string + type: object + type: object + required: + - job_type + - dimension + - dataset diff --git a/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml b/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml new file mode 100644 index 0000000000..09a73f17cc --- /dev/null +++ b/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml @@ -0,0 +1,422 @@ +# +# Copyright (C) 2019-2022 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: valdbenchmarkscenarios.vald.vdaas.org +spec: + group: vald.vdaas.org + names: + kind: ValdBenchmarkScenario + listKind: ValdBenchmarkScenarioList + plural: valdbenchmarkscenarios + singular: valdbenchmarkscenario + shortNames: + - vbo + - vbos + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - jsonPath: .status + name: STATUS + type: string + schema: + openAPIV3Schema: + description: ValdBenchmarkScenario is the Schema for the valdbenchmarkscenarios API + type: object + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + status: + description: ValdBenchmarkScenarioStatus defines the observed state of ValdBenchmarkScenario + enum: + - NotReady + - Available + - Healthy + type: string + spec: + description: ValdBenchmarkScenarioSpec defines the desired state of ValdBenchmarkScenario + type: object + properties: + dataset: + description: BenchmarkDataset defines the desired state of BenchmarkDateset + properties: + group: + type: string + indexes: + type: integer + name: + type: string + range: + description: BenchmarkDatasetRange defines the desired state of BenchmarkDatesetRange + properties: + end: + type: integer + start: + type: integer + required: + - end + - start + type: object + required: + - group + - indexes + - name + type: object + target: + properties: + host: + type: string + port: + type: integer + required: + - host + - port + type: object + jobs: + type: array + items: + description: BenchmarkJobSpec defines the desired state of ValdBenchmarkJob + type: object + properties: + target: + description: BenchmarkTarget defines the desired state of BenchmarkTarget + properties: + host: + type: string + port: + type: integer + required: + - host + - port + type: object + dataset: + description: BenchmarkDataset defines the desired state of BenchmarkDateset + properties: + group: + type: string + indexes: + type: integer + name: + type: string + range: + description: BenchmarkDatasetRange defines the desired state of BenchmarkDatesetRange + properties: + end: + type: integer + start: + type: integer + required: + - end + - start + type: object + required: + - group + - indexes + - name + type: object + dimension: + type: integer + job_type: + type: string + repetition: + type: integer + replica: + type: integer + rules: + items: + description: BenchmarkJobRule defines the desired state of BenchmarkJobRule + properties: + name: + type: string + type: + type: string + required: + - name + - type + type: object + type: array + insert_config: + description: InsertConfig defines the desired state of insert config + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + type: object + remove_config: + description: RemoveConfig defines the desired state of remove config + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + type: object + search_config: + description: SearchConfig defines the desired state of search config + properties: + epsilon: + type: number + min_num: + format: int32 + type: integer + num: + format: int32 + type: integer + radius: + type: number + timeout: + type: string + type: object + update_config: + description: UpdateConfig defines the desired state of update config + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + type: object + upsert_config: + description: UpsertConfig defines the desired state of upsert config + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + type: object + client_config: + description: ClientConfig represents the configurations for gRPC client. + properties: + addrs: + items: + type: string + type: array + backoff: + description: Backoff represents the configuration for the internal backoff package. + properties: + backoff_factor: + type: number + backoff_time_limit: + type: string + enable_error_log: + type: boolean + initial_duration: + type: string + jitter_limit: + type: string + maximum_duration: + type: string + retry_count: + type: integer + type: object + call_option: + description: CallOption represents the configurations for call option. + properties: + max_recv_msg_size: + type: integer + max_retry_rpc_buffer_size: + type: integer + max_send_msg_size: + type: integer + wait_for_ready: + type: boolean + type: object + circuit_breaker: + description: CircuitBreaker represents the configuration for the internal circuitbreaker package. + properties: + closed_error_rate: + type: number + closed_refresh_timeout: + type: string + half_open_error_rate: + type: number + min_samples: + format: int64 + type: integer + open_timeout: + type: string + type: object + connection_pool: + description: ConnectionPool represents the configurations for connection pool. + properties: + enable_rebalance: + type: boolean + old_conn_close_duration: + type: string + rebalance_duration: + type: string + resolve_dns: + type: boolean + size: + type: integer + type: object + dial_option: + description: DialOption represents the configurations for dial option. + properties: + backoff_base_delay: + type: string + backoff_jitter: + type: number + backoff_max_delay: + type: string + backoff_multiplier: + type: number + enable_backoff: + type: boolean + initial_connection_window_size: + type: integer + initial_window_size: + type: integer + insecure: + type: boolean + interceptors: + items: + type: string + type: array + keepalive: + description: GRPCClientKeepalive represents the configurations for gRPC keep-alive. + properties: + permit_without_stream: + type: boolean + time: + type: string + timeout: + type: string + type: object + max_msg_size: + type: integer + minimum_connection_timeout: + type: string + net: + description: Net represents the network configuration tcp, udp, unix domain socket. + properties: + dialer: + description: Dialer represents the configuration for dial. + properties: + dual_stack_enabled: + type: boolean + fallback_delay: + type: string + keepalive: + type: string + timeout: + type: string + type: object + dns: + description: DNS represents the configuration for resolving DNS. + properties: + cache_enabled: + type: boolean + cache_expiration: + type: string + refresh_duration: + type: string + type: object + socket_option: + description: SocketOption represents the socket configurations. + properties: + ip_recover_destination_addr: + type: boolean + ip_transparent: + type: boolean + reuse_addr: + type: boolean + reuse_port: + type: boolean + tcp_cork: + type: boolean + tcp_defer_accept: + type: boolean + tcp_fast_open: + type: boolean + tcp_no_delay: + type: boolean + tcp_quick_ack: + type: boolean + type: object + tls: + description: TLS represent the TLS configuration for server. + properties: + ca: + description: CA represent the CA certificate environment variable key used to start server. + type: string + cert: + description: Cert represent the certificate environment variable key used to start server. + type: string + enabled: + description: Enable represent the server enable TLS or not. + type: boolean + insecure_skip_verify: + description: InsecureSkipVerify represent enable/disable skip SSL certificate verification + type: boolean + key: + description: Key represent the private key environment variable key used to start server. + type: string + type: object + type: object + read_buffer_size: + type: integer + timeout: + type: string + write_buffer_size: + type: integer + type: object + health_check_duration: + type: string + tls: + description: TLS represent the TLS configuration for server. + properties: + ca: + description: CA represent the CA certificate environment variable key used to start server. + type: string + cert: + description: Cert represent the certificate environment variable key used to start server. + type: string + enabled: + description: Enable represent the server enable TLS or not. + type: boolean + insecure_skip_verify: + description: InsecureSkipVerify represent enable/disable skip SSL certificate verification + type: boolean + key: + description: Key represent the private key environment variable key used to start server. + type: string + type: object + type: object + required: + - job_type + - dimension + required: + - jobs + - dataset + - target diff --git a/internal/k8s/job/job.go b/internal/k8s/job/job.go new file mode 100644 index 0000000000..026baa69e2 --- /dev/null +++ b/internal/k8s/job/job.go @@ -0,0 +1,160 @@ +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 job + +import ( + "context" + "reflect" + "strings" + "sync" + "time" + + batchv1 "k8s.io/api/batch/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/k8s" + "github.com/vdaas/vald/internal/log" +) + +// JobWatcher is a type alias for k8s resource controller. +type JobWatcher k8s.ResourceController + +type reconciler struct { + mgr manager.Manager + name string + namespaces []string + onError func(err error) + onReconcile func(ctx context.Context, jobList map[string][]Job) + listOpts []client.ListOption + jobsByAppNamePool sync.Pool // map[app][]Job +} + +// Job is a type alias for the k8s job definition. +type Job = batchv1.Job + +// New returns the JobWatcher that implements reconciliation loop, or any errors occurred. +func New(opts ...Option) (JobWatcher, error) { + r := &reconciler{ + jobsByAppNamePool: sync.Pool{ + New: func() interface{} { + return make(map[string][]Job) + }, + }, + } + + for _, opt := range append(defaultOpts, opts...) { + if err := opt(r); err != nil { + return nil, errors.ErrOptionFailed(err, reflect.ValueOf(opt)) + } + } + + if len(r.namespaces) != 0 { + r.listOpts = make([]client.ListOption, 0, len(r.namespaces)) + for _, ns := range r.namespaces { + r.listOpts = append(r.listOpts, client.InNamespace(ns)) + } + } + + return r, nil +} + +// Reconcile implements k8s reconciliation loop to retrieve the Job information from k8s. +func (r *reconciler) Reconcile(ctx context.Context, req reconcile.Request) (res reconcile.Result, err error) { + js := new(batchv1.JobList) + + err = r.mgr.GetClient().List(ctx, js, r.listOpts...) + if err != nil { + if r.onError != nil { + r.onError(err) + } + res = reconcile.Result{ + Requeue: true, + RequeueAfter: time.Millisecond * 100, + } + if k8serrors.IsNotFound(err) { + log.Error("not found", err) + return reconcile.Result{ + Requeue: true, + RequeueAfter: time.Second, + }, nil + } + return + } + + jobs := r.jobsByAppNamePool.Get().(map[string][]Job) + for _, job := range js.Items { + name, ok := job.GetObjectMeta().GetLabels()["app"] + if !ok { + jns := strings.Split(job.GetName(), "-") + name = strings.Join(jns[:len(jns)-1], "-") + } + + if _, ok := jobs[name]; !ok { + jobs[name] = make([]Job, 0, len(js.Items)) + } + jobs[name] = append(jobs[name], job) + } + + if r.onReconcile != nil { + r.onReconcile(ctx, jobs) + } + + for name := range jobs { + jobs[name] = jobs[name][:0:len(jobs[name])] + } + + r.jobsByAppNamePool.Put(jobs) + + return +} + +// GetName returns the name of resource controller. +func (r *reconciler) GetName() string { + return r.name +} + +// NewReconciler returns the reconciler for the Job. +func (r *reconciler) NewReconciler(ctx context.Context, mgr manager.Manager) reconcile.Reconciler { + if r.mgr == nil && mgr != nil { + r.mgr = mgr + } + + batchv1.AddToScheme(r.mgr.GetScheme()) + return r +} + +// For returns the runtime.Object which is job. +func (r *reconciler) For() (client.Object, []builder.ForOption) { + return new(batchv1.Job), nil +} + +// Owns returns the owner of the job watcher. +// It will always return nil. +func (r *reconciler) Owns() (client.Object, []builder.OwnsOption) { + return nil, nil +} + +// Watches returns the kind of the job and the event handler. +// It will always return nil. +func (r *reconciler) Watches() (*source.Kind, handler.EventHandler, []builder.WatchesOption) { + // return &source.Kind{Type: new(corev1.Pod)}, &handler.EnqueueRequestForObject{} + return nil, nil, nil +} diff --git a/internal/k8s/job/option.go b/internal/k8s/job/option.go new file mode 100644 index 0000000000..283f4b156c --- /dev/null +++ b/internal/k8s/job/option.go @@ -0,0 +1,65 @@ +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 job + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +// Option represents functional option for reconciler. +type Option func(*reconciler) error + +var defaultOpts = []Option{} + +// WithControllerName returns Option that sets r.name. +func WithControllerName(name string) Option { + return func(r *reconciler) error { + r.name = name + return nil + } +} + +// WithManager returns Option that sets r.mgr. +func WithManager(mgr manager.Manager) Option { + return func(r *reconciler) error { + r.mgr = mgr + return nil + } +} + +// WithNamespaces returns Option to set the namespace. +func WithNamespaces(nss ...string) Option { + return func(r *reconciler) error { + r.namespaces = nss + return nil + } +} + +// WithOnErrorFunc returns Option that sets r.onError. +func WithOnErrorFunc(f func(err error)) Option { + return func(r *reconciler) error { + r.onError = f + return nil + } +} + +// WithOnReconcileFunc returns Option that sets r.onReconcile. +func WithOnReconcileFunc(f func(ctx context.Context, jobList map[string][]Job)) Option { + return func(r *reconciler) error { + r.onReconcile = f + return nil + } +} diff --git a/internal/k8s/rbac/benchmark/job/clusterrole.yaml b/internal/k8s/rbac/benchmark/job/clusterrole.yaml new file mode 100644 index 0000000000..4cdc233ec4 --- /dev/null +++ b/internal/k8s/rbac/benchmark/job/clusterrole.yaml @@ -0,0 +1,92 @@ +# +# Copyright (C) 2019-2022 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: benchmark-job-role # TODO: fix + namespace: default # TODO: fix +rules: + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - vald.vdaas.org + resources: + - valdbenchmarkjobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - vald.vdaas.org + resources: + - valdbenchmarkjobs/finalizers + verbs: + - update + - apiGroups: + - vald.vdaas.org + resources: + - valdbenchmarkjobs/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - update + - apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/internal/k8s/rbac/benchmark/job/clusterrolebinding.yaml b/internal/k8s/rbac/benchmark/job/clusterrolebinding.yaml new file mode 100644 index 0000000000..f856e1b753 --- /dev/null +++ b/internal/k8s/rbac/benchmark/job/clusterrolebinding.yaml @@ -0,0 +1,27 @@ +# +# Copyright (C) 2019-2022 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: benchmark-job-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: benchmark-job-role # TODO: fix +subjects: + - kind: ServiceAccount + name: benchmark-job-service # TODO: fix + namespace: default # TODO: fix diff --git a/internal/k8s/rbac/benchmark/job/serviceaccount.yaml b/internal/k8s/rbac/benchmark/job/serviceaccount.yaml new file mode 100644 index 0000000000..3bf6b046ca --- /dev/null +++ b/internal/k8s/rbac/benchmark/job/serviceaccount.yaml @@ -0,0 +1,20 @@ +# +# Copyright (C) 2019-2022 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersoin: v1 +kind: ServiceAccount +metadata: + name: benchmark-job-service # TODO: fix + namespace: default # TODO: fix diff --git a/internal/k8s/rbac/benchmark/job/svc.yaml b/internal/k8s/rbac/benchmark/job/svc.yaml new file mode 100644 index 0000000000..ce79a170e1 --- /dev/null +++ b/internal/k8s/rbac/benchmark/job/svc.yaml @@ -0,0 +1,31 @@ +# +# Copyright (C) 2019-2022 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: v1 +kind: Service +metadata: + name: benchmark-job-svc +spec: + ports: + - name: prometheus + port: 4000 # TODO: fix + targetPort: 4000 # TODO: fix + protocol: TCP + selector: + app.kubernetes.io/name: benchmark-job-svc # TODO: fix + app.kubernetes.io/component: benchmark-job + clusterIP: None + type: ClusterIP + externalTrafficPolicy: "" diff --git a/internal/k8s/rbac/benchmark/operator/clusterrole.yaml b/internal/k8s/rbac/benchmark/operator/clusterrole.yaml new file mode 100644 index 0000000000..acd2ffe14c --- /dev/null +++ b/internal/k8s/rbac/benchmark/operator/clusterrole.yaml @@ -0,0 +1,95 @@ +# +# Copyright (C) 2019-2022 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: benchmark-scenario-role # TODO: fix + namespace: default # TODO: fix +rules: + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - vald.vdaas.org + resources: + - valdbenchmarkscenarios + - valdbenchmarkjobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - vald.vdaas.org + resources: + - valdbenchmarkscenarios/finalizers + - valdbenchmarkjobs/finalizers + verbs: + - update + - apiGroups: + - vald.vdaas.org + resources: + - valdbenchmarkscenarios/status + - valdbenchmarkjobs/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - update + - apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/internal/k8s/rbac/benchmark/operator/clusterrolebinding.yaml b/internal/k8s/rbac/benchmark/operator/clusterrolebinding.yaml new file mode 100644 index 0000000000..e87baf6242 --- /dev/null +++ b/internal/k8s/rbac/benchmark/operator/clusterrolebinding.yaml @@ -0,0 +1,27 @@ +# +# Copyright (C) 2019-2022 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: benchmark-scenario-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: benchmark-scenario-role # TODO: fix +subjects: + - kind: ServiceAccount + name: vald-benchmark-operator # TODO: fix + namespace: default # TODO: fix diff --git a/internal/k8s/rbac/benchmark/operator/serviceaccount.yaml b/internal/k8s/rbac/benchmark/operator/serviceaccount.yaml new file mode 100644 index 0000000000..612c1bf84a --- /dev/null +++ b/internal/k8s/rbac/benchmark/operator/serviceaccount.yaml @@ -0,0 +1,20 @@ +# +# Copyright (C) 2019-2022 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: v1 +kind: ServiceAccount +metadata: + name: vald-benchmark-operator # TODO: fix + namespace: default # TODO: fix diff --git a/internal/k8s/reconciler.go b/internal/k8s/reconciler.go index b5533169d4..08f3a5df49 100644 --- a/internal/k8s/reconciler.go +++ b/internal/k8s/reconciler.go @@ -34,8 +34,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) +type Manager = manager.Manager + type Controller interface { Start(ctx context.Context) (<-chan error, error) + GetManager() Manager } type ResourceController interface { @@ -135,3 +138,7 @@ func (c *controller) Start(ctx context.Context) (<-chan error, error) { return ech, nil } + +func (c *controller) GetManager() Manager { + return c.mgr +} diff --git a/internal/k8s/vald/benchmark/api/v1/info.go b/internal/k8s/vald/benchmark/api/v1/info.go new file mode 100644 index 0000000000..1ad2f03a4a --- /dev/null +++ b/internal/k8s/vald/benchmark/api/v1/info.go @@ -0,0 +1,40 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 v1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "vald.vdaas.org", Version: "v1"} + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) + +func init() { + SchemeBuilder.Register( + &ValdBenchmarkScenario{}, + &ValdBenchmarkScenarioList{}, + &ValdBenchmarkJob{}, + &ValdBenchmarkJobList{}, + ) +} diff --git a/internal/k8s/vald/benchmark/api/v1/job_types.go b/internal/k8s/vald/benchmark/api/v1/job_types.go new file mode 100644 index 0000000000..dc3996dbb0 --- /dev/null +++ b/internal/k8s/vald/benchmark/api/v1/job_types.go @@ -0,0 +1,233 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 v1 + +import ( + "github.com/vdaas/vald/internal/config" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +type BenchmarkJobSpec struct { + Target *BenchmarkTarget `json:"target,omitempty"` + Dataset *BenchmarkDataset `json:"dataset,omitempty"` + Dimension int `json:"dimension,omitempty"` + Replica int `json:"replica,omitempty"` + Repetition int `json:"repetition,omitempty"` + JobType string `json:"job_type,omitempty"` + InsertConfig *config.InsertConfig `json:"insert_config,omitempty"` + UpdateConfig *config.UpdateConfig `json:"update_config,omitempty"` + UpsertConfig *config.UpsertConfig `json:"upsert_config,omitempty"` + SearchConfig *config.SearchConfig `json:"search_config,omitempty"` + RemoveConfig *config.RemoveConfig `json:"remove_config,omitempty"` + ClientConfig *config.GRPCClient `json:"client_config,omitempty"` + Rules []*config.BenchmarkJobRule `json:"rules,omitempty"` +} + +type BenchmarkJobStatus string + +const ( + BenchmarkJobNotReady = BenchmarkJobStatus("NotReady") + BenchmarkJobAvailable = BenchmarkJobStatus("Available") + BenchmarkJobHealthy = BenchmarkJobStatus("Healthy") +) + +// BenchmarkTarget defines the desired state of BenchmarkTarget +type BenchmarkTarget config.BenchmarkTarget + +// BenchmarkDataset defines the desired state of BenchmarkDateset +type BenchmarkDataset config.BenchmarkDataset + +// BenchmarkDatasetRange defines the desired state of BenchmarkDatesetRange +type BenchmarkDatasetRange config.BenchmarkDatasetRange + +// BenchmarkJobRule defines the desired state of BenchmarkJobRule +type BenchmarkJobRule config.BenchmarkJobRule + +type ValdBenchmarkJob struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + Spec BenchmarkJobSpec `json:"spec,omitempty"` + Status BenchmarkJobStatus `json:"status,omitempty"` +} + +type ValdBenchmarkJobList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + Items []ValdBenchmarkJob `json:"items,omitempty"` +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BenchmarkDataset) DeepCopyInto(out *BenchmarkDataset) { + *out = *in + if in.Range != nil { + in, out := &in.Range, &out.Range + *out = new(config.BenchmarkDatasetRange) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BenchmarkDataset. +func (in *BenchmarkDataset) DeepCopy() *BenchmarkDataset { + if in == nil { + return nil + } + out := new(BenchmarkDataset) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BenchmarkDatasetRange) DeepCopyInto(out *BenchmarkDatasetRange) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BenchmarkDatasetRange. +func (in *BenchmarkDatasetRange) DeepCopy() *BenchmarkDatasetRange { + if in == nil { + return nil + } + out := new(BenchmarkDatasetRange) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BenchmarkJobRule) DeepCopyInto(out *BenchmarkJobRule) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BenchmarkJobRule. +func (in *BenchmarkJobRule) DeepCopy() *BenchmarkJobRule { + if in == nil { + return nil + } + out := new(BenchmarkJobRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BenchmarkJobSpec) DeepCopyInto(out *BenchmarkJobSpec) { + *out = *in + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(BenchmarkTarget) + **out = **in + } + if in.Dataset != nil { + in, out := &in.Dataset, &out.Dataset + *out = new(BenchmarkDataset) + (*in).DeepCopyInto(*out) + } + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]*config.BenchmarkJobRule, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(config.BenchmarkJobRule) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BenchmarkJobSpec. +func (in *BenchmarkJobSpec) DeepCopy() *BenchmarkJobSpec { + if in == nil { + return nil + } + out := new(BenchmarkJobSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BenchmarkTarget) DeepCopyInto(out *BenchmarkTarget) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BenchmarkTarget. +func (in *BenchmarkTarget) DeepCopy() *BenchmarkTarget { + if in == nil { + return nil + } + out := new(BenchmarkTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ValdBenchmarkJob) DeepCopyInto(out *ValdBenchmarkJob) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BenchmarkOperator. +func (in *ValdBenchmarkJob) DeepCopy() *ValdBenchmarkJob { + if in == nil { + return nil + } + out := new(ValdBenchmarkJob) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ValdBenchmarkJob) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ValdBenchmarkJobList) DeepCopyInto(out *ValdBenchmarkJobList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ValdBenchmarkJob, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BenchmarkOperatorList. +func (in *ValdBenchmarkJobList) DeepCopy() *ValdBenchmarkJobList { + if in == nil { + return nil + } + out := new(ValdBenchmarkJobList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ValdBenchmarkJobList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/internal/k8s/vald/benchmark/api/v1/scenario_types.go b/internal/k8s/vald/benchmark/api/v1/scenario_types.go new file mode 100644 index 0000000000..0ef2adb341 --- /dev/null +++ b/internal/k8s/vald/benchmark/api/v1/scenario_types.go @@ -0,0 +1,144 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +type ValdBenchmarkScenarioSpec struct { + Target *BenchmarkTarget `json:"target,omitempty"` + Dataset *BenchmarkDataset `json:"dataset,omitempty"` + Jobs []*BenchmarkJobSpec `json:"jobs,omitempty"` +} + +type ValdBenchmarkScenarioStatus string + +const ( + BenchmarkScenarioNotReady ValdBenchmarkScenarioStatus = "NotReady" + BenchmarkScenarioAvailable ValdBenchmarkScenarioStatus = "Available" + BenchmarkScenarioHealthy ValdBenchmarkScenarioStatus = "Healthy" +) + +type ValdBenchmarkScenario struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ValdBenchmarkScenarioSpec `json:"spec,omitempty"` + Status ValdBenchmarkScenarioStatus `json:"status,omitempty"` +} + +type ValdBenchmarkScenarioList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ValdBenchmarkScenario `json:"items"` +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ValdBenchmarkScenario) DeepCopyInto(out *ValdBenchmarkScenario) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BenchmarkScenario. +func (in *ValdBenchmarkScenario) DeepCopy() *ValdBenchmarkScenario { + if in == nil { + return nil + } + out := new(ValdBenchmarkScenario) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ValdBenchmarkScenario) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ValdBenchmarkScenarioList) DeepCopyInto(out *ValdBenchmarkScenarioList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ValdBenchmarkScenario, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BenchmarkScenarioList. +func (in *ValdBenchmarkScenarioList) DeepCopy() *ValdBenchmarkScenarioList { + if in == nil { + return nil + } + out := new(ValdBenchmarkScenarioList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ValdBenchmarkScenarioList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ValdBenchmarkScenarioSpec) DeepCopyInto(out *ValdBenchmarkScenarioSpec) { + *out = *in + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(BenchmarkTarget) + **out = **in + } + if in.Dataset != nil { + in, out := &in.Dataset, &out.Dataset + *out = new(BenchmarkDataset) + (*in).DeepCopyInto(*out) + } + if in.Jobs != nil { + in, out := &in.Jobs, &out.Jobs + *out = make([]*BenchmarkJobSpec, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(BenchmarkJobSpec) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BenchmarkScenarioSpec. +func (in *ValdBenchmarkScenarioSpec) DeepCopy() *ValdBenchmarkScenarioSpec { + if in == nil { + return nil + } + out := new(ValdBenchmarkScenarioSpec) + in.DeepCopyInto(out) + return out +} diff --git a/internal/k8s/vald/benchmark/job/doc.go b/internal/k8s/vald/benchmark/job/doc.go new file mode 100644 index 0000000000..678bce94ab --- /dev/null +++ b/internal/k8s/vald/benchmark/job/doc.go @@ -0,0 +1,18 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 job provides benchmark job crd information and preriodically update +package job diff --git a/internal/k8s/vald/benchmark/job/job.go b/internal/k8s/vald/benchmark/job/job.go new file mode 100644 index 0000000000..3cfed17ed6 --- /dev/null +++ b/internal/k8s/vald/benchmark/job/job.go @@ -0,0 +1,140 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 job + +import ( + "context" + "time" + + "github.com/vdaas/vald/internal/k8s" + v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" + "github.com/vdaas/vald/internal/log" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/scheme" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +type BenchmarkJobWatcher k8s.ResourceController + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "vald.benchmark.job", Version: "v1"} + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) + +type reconciler struct { + mgr manager.Manager + name string + namespaces []string + onError func(err error) + onReconcile func(ctx context.Context, jobList map[string]v1.ValdBenchmarkJob) + lopts []client.ListOption +} + +func New(opts ...Option) (BenchmarkJobWatcher, error) { + r := new(reconciler) + for _, opt := range append(defaultOpts, opts...) { + // TODO: impl error handling after implement functional option + opt(r) + } + return r, nil +} + +func (r *reconciler) AddListOpts(opt client.ListOption) { + if opt == nil { + return + } + if r.lopts == nil { + r.lopts = make([]client.ListOption, 0, 1) + } + r.lopts = append(r.lopts, opt) +} + +func (r *reconciler) Reconcile(ctx context.Context, req reconcile.Request) (res reconcile.Result, err error) { + bj := new(v1.ValdBenchmarkJobList) + + if r.lopts == nil { + err = r.mgr.GetClient().List(ctx, bj, r.lopts...) + } else { + err = r.mgr.GetClient().List(ctx, bj) + } + + if err != nil { + if r.onError != nil { + r.onError(err) + } + res = reconcile.Result{ + Requeue: true, + RequeueAfter: time.Millisecond * 100, + } + if errors.IsNotFound(err) { + log.Errorf("not found: %s", err) + return reconcile.Result{ + Requeue: true, + RequeueAfter: time.Second, + }, nil + } + return + } + + jobs := make(map[string]v1.ValdBenchmarkJob, 0) + for _, item := range bj.Items { + name := item.GetName() + jobs[name] = item + } + + if r.onReconcile != nil { + r.onReconcile(ctx, jobs) + } + return +} + +func (r *reconciler) GetName() string { + return r.name +} + +func (r *reconciler) NewReconciler(ctx context.Context, mgr manager.Manager) reconcile.Reconciler { + if r.mgr == nil && mgr != nil { + r.mgr = mgr + } + + v1.AddToScheme(r.mgr.GetScheme()) + + return r +} + +func (r *reconciler) For() (client.Object, []builder.ForOption) { + return new(v1.ValdBenchmarkJob), nil +} + +func (r *reconciler) Owns() (client.Object, []builder.OwnsOption) { + return nil, nil +} + +func (r *reconciler) Watches() (*source.Kind, handler.EventHandler, []builder.WatchesOption) { + // return &source.Kind{Type: new(corev1.Pod)}, &handler.EnqueueRequestForObject{} + return nil, nil, nil +} diff --git a/internal/k8s/vald/benchmark/job/option.go b/internal/k8s/vald/benchmark/job/option.go new file mode 100644 index 0000000000..bf834ec347 --- /dev/null +++ b/internal/k8s/vald/benchmark/job/option.go @@ -0,0 +1,68 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 job + +import ( + "context" + + v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type Option func(*reconciler) error + +var defaultOpts = []Option{} + +// WithControllerName returns Option that sets r.name. +func WithControllerName(name string) Option { + return func(r *reconciler) error { + r.name = name + return nil + } +} + +// WithManager returns Option that sets r.mgr. +func WithManager(mgr manager.Manager) Option { + return func(r *reconciler) error { + r.mgr = mgr + return nil + } +} + +// WithNamespaces returns Option to set the namespace. +func WithNamespaces(nss ...string) Option { + return func(r *reconciler) error { + r.namespaces = nss + return nil + } +} + +// WithOnErrorFunc returns Option that sets r.onError. +func WithOnErrorFunc(f func(err error)) Option { + return func(r *reconciler) error { + r.onError = f + return nil + } +} + +// WithOnReconcileFunc returns Option that sets r.onReconcile. +func WithOnReconcileFunc(f func(ctx context.Context, jobList map[string]v1.ValdBenchmarkJob)) Option { + return func(r *reconciler) error { + r.onReconcile = f + return nil + } +} diff --git a/internal/k8s/vald/benchmark/scenario/doc.go b/internal/k8s/vald/benchmark/scenario/doc.go new file mode 100644 index 0000000000..c3800f6723 --- /dev/null +++ b/internal/k8s/vald/benchmark/scenario/doc.go @@ -0,0 +1,18 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 operator provides benchmark operator crd information and preriodically update +package scenario diff --git a/internal/k8s/vald/benchmark/scenario/option.go b/internal/k8s/vald/benchmark/scenario/option.go new file mode 100644 index 0000000000..fcbc83af47 --- /dev/null +++ b/internal/k8s/vald/benchmark/scenario/option.go @@ -0,0 +1,68 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 scenario + +import ( + "context" + + v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type Option func(*reconciler) error + +var defaultOpts = []Option{} + +// WithControllerName returns Option that sets r.name. +func WithControllerName(name string) Option { + return func(r *reconciler) error { + r.name = name + return nil + } +} + +// WithManager returns Option that sets r.mgr. +func WithManager(mgr manager.Manager) Option { + return func(r *reconciler) error { + r.mgr = mgr + return nil + } +} + +// WithNamespaces returns Option to set the namespace. +func WithNamespaces(nss ...string) Option { + return func(r *reconciler) error { + r.namespaces = nss + return nil + } +} + +// WithOnErrorFunc returns Option that sets r.onError. +func WithOnErrorFunc(f func(err error)) Option { + return func(r *reconciler) error { + r.onError = f + return nil + } +} + +// WithOnReconcileFunc returns Option that sets r.onReconcile. +func WithOnReconcileFunc(f func(ctx context.Context, scenarioList map[string]v1.ValdBenchmarkScenario)) Option { + return func(r *reconciler) error { + r.onReconcile = f + return nil + } +} diff --git a/internal/k8s/vald/benchmark/scenario/scenario.go b/internal/k8s/vald/benchmark/scenario/scenario.go new file mode 100644 index 0000000000..c3e99b4987 --- /dev/null +++ b/internal/k8s/vald/benchmark/scenario/scenario.go @@ -0,0 +1,128 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 scenario + +import ( + "context" + "time" + + "github.com/vdaas/vald/internal/k8s" + v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" + "github.com/vdaas/vald/internal/log" + "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +type BenchmarkScenarioWatcher k8s.ResourceController + +type reconciler struct { + mgr manager.Manager + name string + namespaces []string + onError func(err error) + onReconcile func(ctx context.Context, operatorList map[string]v1.ValdBenchmarkScenario) + lopts []client.ListOption +} + +func New(opts ...Option) (BenchmarkScenarioWatcher, error) { + r := new(reconciler) + for _, opt := range append(defaultOpts, opts...) { + // TODO: impl error handling after implement functional option + opt(r) + } + return r, nil +} + +func (r *reconciler) AddListOpts(opt client.ListOption) { + if opt == nil { + return + } + if r.lopts == nil { + r.lopts = make([]client.ListOption, 0, 1) + } + r.lopts = append(r.lopts, opt) +} + +func (r *reconciler) Reconcile(ctx context.Context, req reconcile.Request) (res reconcile.Result, err error) { + bs := new(v1.ValdBenchmarkScenarioList) + + if r.lopts == nil { + err = r.mgr.GetClient().List(ctx, bs, r.lopts...) + } else { + err = r.mgr.GetClient().List(ctx, bs) + } + + if err != nil { + if r.onError != nil { + r.onError(err) + } + res = reconcile.Result{ + Requeue: true, + RequeueAfter: time.Millisecond * 100, + } + if errors.IsNotFound(err) { + log.Errorf("not found: %s", err) + return reconcile.Result{ + Requeue: true, + RequeueAfter: time.Second, + }, nil + } + return + } + scenarios := make(map[string]v1.ValdBenchmarkScenario, 0) + for _, item := range bs.Items { + name := item.Name + scenarios[name] = item + } + + if r.onReconcile != nil { + r.onReconcile(ctx, scenarios) + } + + return +} + +func (r *reconciler) GetName() string { + return r.name +} + +func (r *reconciler) NewReconciler(ctx context.Context, mgr manager.Manager) reconcile.Reconciler { + if r.mgr == nil && mgr != nil { + r.mgr = mgr + } + v1.AddToScheme(r.mgr.GetScheme()) + + return r +} + +func (r *reconciler) For() (client.Object, []builder.ForOption) { + return new(v1.ValdBenchmarkScenario), nil +} + +func (r *reconciler) Owns() (client.Object, []builder.OwnsOption) { + return nil, nil +} + +func (r *reconciler) Watches() (*source.Kind, handler.EventHandler, []builder.WatchesOption) { + // return &source.Kind{Type: new(corev1.Pod)}, &handler.EnqueueRequestForObject{} + return &source.Kind{Type: new(v1.ValdBenchmarkScenario)}, &handler.EnqueueRequestForObject{}, nil +} diff --git a/internal/test/data/hdf5/doc.go b/internal/test/data/hdf5/doc.go new file mode 100644 index 0000000000..f8650c005e --- /dev/null +++ b/internal/test/data/hdf5/doc.go @@ -0,0 +1,18 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 hdf5 is load hdf5 file +package hdf5 diff --git a/internal/test/data/hdf5/hdf5.go b/internal/test/data/hdf5/hdf5.go new file mode 100644 index 0000000000..e83965ec8c --- /dev/null +++ b/internal/test/data/hdf5/hdf5.go @@ -0,0 +1,268 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 hdf5 is load hdf5 file +package hdf5 + +import ( + "os" + "reflect" + + "gonum.org/v1/hdf5" + + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/io" + "github.com/vdaas/vald/internal/net/http/client" +) + +type Data interface { + Download() error + Read() error + GetName() DatasetName + GetPath() string + GetTrain() [][]float32 + GetTest() [][]float32 + GetNeighbors() [][]int +} + +type DatasetName int + +const ( + FashionMNIST784Euclidean DatasetName = iota +) + +func (d DatasetName) String() string { + switch d { + case FashionMNIST784Euclidean: + return "fashion-mnist-784-euc" + default: + return "" + } +} + +type DatasetUrl int + +const ( + FashionMNIST784EuclideanUrl DatasetUrl = iota +) + +func (d DatasetUrl) String() string { + switch d { + case FashionMNIST784EuclideanUrl: + return "http://ann-benchmarks.com/fashion-mnist-784-euclidean.hdf5" + default: + return "" + } +} + +type hdf5Key int + +const ( + Train hdf5Key = iota + Test + Neighors +) + +func (h hdf5Key) String() string { + switch h { + case Train: + return "train" + case Test: + return "test" + case Neighors: + return "neighbors" + default: + return "" + } +} + +type data struct { + name DatasetName + path string + train [][]float32 + test [][]float32 + neighbors [][]int +} + +func New(opts ...Option) (Data, error) { + d := new(data) + for _, opt := range append(defaultOptions, opts...) { + if err := opt(d); err != nil { + return nil, errors.ErrOptionFailed(err, reflect.ValueOf(opt)) + } + } + return d, nil +} + +// Get downloads the hdf5 file. +// https://github.com/erikbern/ann-benchmarks/#data-sets +func (d *data) Download() error { + switch d.name { + case FashionMNIST784Euclidean: + return downloadFile(FashionMNIST784EuclideanUrl.String(), d.path) + default: + return errors.NewErrInvalidOption("name", d.name) + } +} + +func (d *data) Read() error { + f, err := hdf5.OpenFile(d.path, hdf5.F_ACC_RDONLY) + if err != nil { + return err + } + defer f.Close() + + // load training data + train, err := ReadDatasetF32(f, Train) + if err != nil { + return err + } + d.train = train + + // load test data + test, err := ReadDatasetF32(f, Test) + if err != nil { + return err + } + d.test = test + + // load neighbors + neighbors32, err := ReadDatasetI32(f, Neighors) + if err != nil { + return err + } + neighbors := make([][]int, len(neighbors32)) + for i, ns := range neighbors32 { + neighbors[i] = make([]int, len(ns)) + for j, n := range ns { + neighbors[i][j] = int(n) + } + } + d.neighbors = neighbors + + return nil +} + +func (d *data) GetName() DatasetName { + return d.name +} + +func (d *data) GetPath() string { + return d.path +} + +func (d *data) GetTrain() [][]float32 { + return d.train +} + +func (d *data) GetTest() [][]float32 { + return d.test +} + +func (d *data) GetNeighbors() [][]int { + return d.neighbors +} + +func downloadFile(url, path string) error { + if len(path) == 0 { + return errors.NewErrInvalidOption("no path is specified", path) + } + cli, err := client.New() + if err != nil { + return err + } + + resp, err := cli.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return errors.ErrInvalidStatusCode(resp.StatusCode) + } + + file, err := os.Create(path) + if err != nil { + return err + } + defer file.Close() + + _, err = io.Copy(file, resp.Body) + if err != nil { + return err + } + + return nil +} + +func ReadDatasetF32(file *hdf5.File, key hdf5Key) ([][]float32, error) { + data, err := file.OpenDataset(key.String()) + if err != nil { + return nil, err + } + defer data.Close() + + dataspace := data.Space() + defer dataspace.Close() + + dims, _, err := dataspace.SimpleExtentDims() + if err != nil { + return nil, err + } + height, width := int(dims[0]), int(dims[1]) + + rawFloats := make([]float32, dataspace.SimpleExtentNPoints()) + if err := data.Read(&rawFloats); err != nil { + return nil, err + } + + vecs := make([][]float32, height) + for i := 0; i < height; i++ { + vecs[i] = rawFloats[i*width : i*width+width] + } + + return vecs, nil +} + +func ReadDatasetI32(file *hdf5.File, key hdf5Key) ([][]int32, error) { + data, err := file.OpenDataset(key.String()) + if err != nil { + return nil, err + } + defer data.Close() + + dataspace := data.Space() + defer dataspace.Close() + + dims, _, err := dataspace.SimpleExtentDims() + if err != nil { + return nil, err + } + height, width := int(dims[0]), int(dims[1]) + + rawFloats := make([]int32, dataspace.SimpleExtentNPoints()) + if err := data.Read(&rawFloats); err != nil { + return nil, err + } + + vecs := make([][]int32, height) + for i := 0; i < height; i++ { + vecs[i] = rawFloats[i*width : i*width+width] + } + + return vecs, nil +} diff --git a/internal/test/data/hdf5/option.go b/internal/test/data/hdf5/option.go new file mode 100644 index 0000000000..c18bcbd5e4 --- /dev/null +++ b/internal/test/data/hdf5/option.go @@ -0,0 +1,59 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 hdf5 is load hdf5 file +package hdf5 + +import ( + "github.com/vdaas/vald/internal/errors" +) + +type Option func(d *data) error + +var defaultOptions = []Option{ + WithName(FashionMNIST784Euclidean), + WithFilePath("./data"), +} + +func WithNameByString(n string) Option { + var name DatasetName + switch n { + case FashionMNIST784Euclidean.String(): + name = FashionMNIST784Euclidean + } + return WithName(name) +} + +func WithName(dn DatasetName) Option { + return func(d *data) error { + switch dn { + case FashionMNIST784Euclidean: + d.name = dn + default: + return errors.NewErrInvalidOption("dataname", dn) + } + return nil + } +} + +func WithFilePath(f string) Option { + return func(d *data) error { + if len(f) != 0 { + d.path = f + } + return nil + } +} diff --git a/pkg/tools/benchmark/job/README.md b/pkg/tools/benchmark/job/README.md new file mode 100644 index 0000000000..75caad4fed --- /dev/null +++ b/pkg/tools/benchmark/job/README.md @@ -0,0 +1 @@ +# vald benchmark job diff --git a/pkg/tools/benchmark/job/config/config.go b/pkg/tools/benchmark/job/config/config.go new file mode 100644 index 0000000000..36620e1dbc --- /dev/null +++ b/pkg/tools/benchmark/job/config/config.go @@ -0,0 +1,309 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 config stores all server application settings +package config + +import ( + "context" + "os" + + "github.com/vdaas/vald/internal/config" + "github.com/vdaas/vald/internal/k8s/client" + v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" + "github.com/vdaas/vald/internal/log" +) + +// GlobalConfig is type alias for config.GlobalConfig +type GlobalConfig = config.GlobalConfig + +// Config represent a application setting data content (config.yaml). +// In K8s environment, this configuration is stored in K8s ConfigMap. +type Config struct { + config.GlobalConfig `json:",inline" yaml:",inline"` + + // Server represent all server configuration + Server *config.Servers `json:"server_config" yaml:"server_config"` + + // Observability represent observability configurations + Observability *config.Observability `json:"observability" yaml:"observability"` + + // Job represents benchmark job configurations + Job *config.BenchmarkJob `json:"job" yaml:"job"` +} + +var ( + NAMESPACE = os.Getenv("CRD_NAMESPACE") + NAME = os.Getenv("CRD_NAME") +) + +// NewConfig represents the set config from the given setting file path. +func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { + err = config.Read(path, &cfg) + if err != nil { + return nil, err + } + + if cfg != nil { + cfg.Bind() + } + + if cfg.Server != nil { + cfg.Server = cfg.Server.Bind() + } else { + cfg.Server = new(config.Servers) + } + + if cfg.Observability != nil { + cfg.Observability = cfg.Observability.Bind() + } else { + cfg.Observability = new(config.Observability) + } + + if cfg.Job != nil { + cfg.Job = cfg.Job.Bind() + } else { + cfg.Job = new(config.BenchmarkJob) + } + + if cfg.Job.ClientConfig == nil { + cfg.Job.ClientConfig = new(config.GRPCClient) + } + + // Get config from applied ValdBenchmarkJob custom resource + var jobResource v1.ValdBenchmarkJob + c, err := client.New(client.WithSchemeBuilder(*v1.SchemeBuilder)) + if err != nil { + log.Warn(err.Error()) + } + err = c.Get(ctx, NAME, NAMESPACE, &jobResource) + if err != nil { + log.Warn(err.Error()) + } else { + cfg.Job.Target = (*config.BenchmarkTarget)(jobResource.Spec.Target) + cfg.Job.Dataset = (*config.BenchmarkDataset)(jobResource.Spec.Dataset) + cfg.Job.Replica = jobResource.Spec.Replica + cfg.Job.Repetition = jobResource.Spec.Repetition + cfg.Job.JobType = jobResource.Spec.JobType + cfg.Job.Rules = jobResource.Spec.Rules + cfg.Job.InsertConfig = jobResource.Spec.InsertConfig + cfg.Job.UpdateConfig = jobResource.Spec.UpdateConfig + cfg.Job.UpsertConfig = jobResource.Spec.UpsertConfig + cfg.Job.SearchConfig = jobResource.Spec.SearchConfig + cfg.Job.RemoveConfig = jobResource.Spec.RemoveConfig + cfg.Job.ClientConfig = jobResource.Spec.ClientConfig + } + + return cfg, nil +} + +// func FakeData() { +// d := Config{ +// Version: "v0.0.1", +// Server: &config.Servers{ +// Servers: []*config.Server{ +// { +// Name: "agent-rest", +// Host: "127.0.0.1", +// Port: 8080, +// Mode: "REST", +// ProbeWaitTime: "3s", +// HTTP: &config.HTTP{ +// ShutdownDuration: "5s", +// HandlerTimeout: "5s", +// IdleTimeout: "2s", +// ReadHeaderTimeout: "1s", +// ReadTimeout: "1s", +// WriteTimeout: "1s", +// }, +// }, +// { +// Name: "agent-grpc", +// Host: "127.0.0.1", +// Port: 8082, +// Mode: "GRPC", +// }, +// }, +// MetricsServers: []*config.Server{ +// { +// Name: "pprof", +// Host: "127.0.0.1", +// Port: 6060, +// Mode: "REST", +// ProbeWaitTime: "3s", +// HTTP: &config.HTTP{ +// ShutdownDuration: "5s", +// HandlerTimeout: "5s", +// IdleTimeout: "2s", +// ReadHeaderTimeout: "1s", +// ReadTimeout: "1s", +// WriteTimeout: "1s", +// }, +// }, +// }, +// HealthCheckServers: []*config.Server{ +// { +// Name: "livenesss", +// Host: "127.0.0.1", +// Port: 3000, +// }, +// { +// Name: "readiness", +// Host: "127.0.0.1", +// Port: 3001, +// }, +// }, +// StartUpStrategy: []string{ +// "livenesss", +// "pprof", +// "agent-grpc", +// "agent-rest", +// "readiness", +// }, +// ShutdownStrategy: []string{ +// "readiness", +// "agent-rest", +// "agent-grpc", +// "pprof", +// "livenesss", +// }, +// FullShutdownDuration: "30s", +// TLS: &config.TLS{ +// Enabled: false, +// Cert: "/path/to/cert", +// Key: "/path/to/key", +// CA: "/path/to/ca", +// }, +// }, +// Job: &config.BenchmarkJob{ +// Target: &config.BenchmarkTarget{ +// Host: "vald-lb-gateway.svc.local", +// Port: 8081, +// }, +// Dataset: &config.BenchmarkDataset{ +// Name: "fashion-mnist", +// Group: "train", +// Indexes: 10000, +// Range: &config.BenchmarkDatasetRange{ +// Start: 0, +// End: 10000, +// }, +// }, +// Replica: 1, +// Repetition: 1, +// JobType: "search", +// InsertConfig: &config.InsertConfig{}, +// UpdateConfig: &config.UpdateConfig{}, +// UpsertConfig: &config.UpsertConfig{}, +// SearchConfig: &config.SearchConfig{}, +// RemoveConfig: &config.RemoveConfig{}, +// ClientConfig: &config.GRPCClient{ +// Addrs: []string{}, +// HealthCheckDuration: "1s", +// ConnectionPool: &config.ConnectionPool{ +// ResolveDNS: true, +// EnableRebalance: true, +// RebalanceDuration: "30m", +// Size: 3, +// OldConnCloseDuration: "2m", +// }, +// Backoff: &config.Backoff{ +// InitialDuration: "5ms", +// BackoffTimeLimit: "5s", +// MaximumDuration: "5s", +// JitterLimit: "100ms", +// BackoffFactor: 1.1, +// RetryCount: 100, +// EnableErrorLog: true, +// }, +// CircuitBreaker: &config.CircuitBreaker{ +// ClosedErrorRate: 0.7, +// HalfOpenErrorRate: 0.5, +// MinSamples: 1000, +// OpenTimeout: "1s", +// ClosedRefreshTimeout: "10s", +// }, +// CallOption: &config.CallOption{ +// WaitForReady: true, +// MaxRetryRPCBufferSize: 0, +// MaxRecvMsgSize: 0, +// MaxSendMsgSize: 0, +// }, +// DialOption: &config.DialOption{ +// WriteBufferSize: 0, +// ReadBufferSize: 0, +// InitialWindowSize: 0, +// InitialConnectionWindowSize: 0, +// MaxMsgSize: 0, +// BackoffMaxDelay: "120s", +// BackoffBaseDelay: "1s", +// BackoffJitter: 0.2, +// BackoffMultiplier: 1.6, +// MinimumConnectionTimeout: "20s", +// EnableBackoff: true, +// Insecure: true, +// Timeout: "", +// Interceptors: []string{}, +// Net: &config.Net{ +// DNS: &config.DNS{ +// CacheEnabled: true, +// RefreshDuration: "30m", +// CacheExpiration: "1h", +// }, +// Dialer: &config.Dialer{ +// Timeout: "", +// Keepalive: "", +// FallbackDelay: "", +// DualStackEnabled: true, +// }, +// TLS: &config.TLS{ +// Enabled: false, +// Cert: "path/to/cert", +// Key: "path/to/key", +// CA: "path/to/ca", +// InsecureSkipVerify: false, +// }, +// SocketOption: &config.SocketOption{ +// ReusePort: true, +// ReuseAddr: true, +// TCPFastOpen: true, +// TCPNoDelay: true, +// TCPQuickAck: true, +// TCPCork: false, +// TCPDeferAccept: true, +// IPTransparent: false, +// IPRecoverDestinationAddr: false, +// }, +// }, +// Keepalive: &config.GRPCClientKeepalive{ +// Time: "120s", +// Timeout: "30s", +// PermitWithoutStream: true, +// }, +// }, +// TLS: &config.TLS{ +// Enabled: false, +// Cert: "path/to/cert", +// Key: "path/to/key", +// CA: "path/to/ca", +// InsecureSkipVerify: false, +// }, +// }, +// Rules: []*config.BenchmarkJobRule{}, +// }, +// } +// fmt.Println(config.ToRawYaml(d)) +// } diff --git a/pkg/tools/benchmark/job/config/config_test.go b/pkg/tools/benchmark/job/config/config_test.go new file mode 100644 index 0000000000..dc35aae48b --- /dev/null +++ b/pkg/tools/benchmark/job/config/config_test.go @@ -0,0 +1,127 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 setting stores all server application settings +package config + +import ( + "context" + "io/fs" + "os" + "testing" + + "github.com/vdaas/vald/internal/config" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/file" + "github.com/vdaas/vald/internal/test/comparator" + "github.com/vdaas/vald/internal/test/goleak" +) + +func TestNewConfig(t *testing.T) { + t.Parallel() + type args struct { + path string + } + type want struct { + wantCfg *Config + err error + } + type test struct { + name string + args args + want want + checkFunc func(want, *Config, error) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, gotCfg *Config, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + if diff := comparator.Diff(gotCfg, w.wantCfg, + comparator.IgnoreTypes(config.Observability{})); diff != "" { + return errors.New(diff) + } + return nil + } + tests := []test{ + func() test { + name := "/home/vankichi/Documents/vald-read-test.yaml" + path := name + return test{ + name: "return error when can't read file", + args: args{ + path: path, + }, + beforeFunc: func(t *testing.T, a args) { + t.Helper() + f, err := file.Open(a.path, os.O_CREATE, fs.ModeIrregular) + if err != nil { + if errors.Is(err, fs.ErrPermission) { + return + } + t.Error(err) + } + if err := f.Close(); err != nil { + t.Error(err) + } + }, + checkFunc: func(w want, gotCfg *Config, err error) error { + if errors.Is(err, fs.ErrPermission) { + return nil + } + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + return nil + }, + afterFunc: func(t *testing.T, a args) { + t.Helper() + if err := os.Remove(a.path); err != nil { + t.Fatal(err) + } + }, + want: want{ + wantCfg: nil, + err: errors.ErrUnsupportedConfigFileType(".yaml"), + }, + } + }(), + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + gotCfg, err := NewConfig(context.Background(), test.args.path) + if err := checkFunc(test.want, gotCfg, err); err != nil { + tt.Errorf("error = %v, got = %#v", err, gotCfg) + } + }) + } +} diff --git a/pkg/tools/benchmark/job/config/doc.go b/pkg/tools/benchmark/job/config/doc.go new file mode 100644 index 0000000000..57ef74c464 --- /dev/null +++ b/pkg/tools/benchmark/job/config/doc.go @@ -0,0 +1,18 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 config stores all server application settings +package config diff --git a/pkg/tools/benchmark/job/handler/doc.go b/pkg/tools/benchmark/job/handler/doc.go new file mode 100644 index 0000000000..f1014141ea --- /dev/null +++ b/pkg/tools/benchmark/job/handler/doc.go @@ -0,0 +1,17 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 handler diff --git a/pkg/tools/benchmark/job/handler/grpc/handler.go b/pkg/tools/benchmark/job/handler/grpc/handler.go new file mode 100644 index 0000000000..85be642c7d --- /dev/null +++ b/pkg/tools/benchmark/job/handler/grpc/handler.go @@ -0,0 +1,56 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 grpc provides grpc server logic +package grpc + +import ( + "context" + + "github.com/vdaas/vald/apis/grpc/v1/benchmark" + "github.com/vdaas/vald/internal/singleflight" + "github.com/vdaas/vald/pkg/tools/benchmark/job/service" +) + +type Benchmark interface { + benchmark.JobServer + Start(context.Context) +} + +type server struct { + benchmark.UnimplementedJobServer + + job service.Job + group singleflight.Group +} + +func New(opts ...Option) (bm Benchmark, err error) { + b := new(server) + + for _, opt := range append(defaultOpts, opts...) { + err = opt(b) + if err != nil { + return nil, err + } + } + + b.group = singleflight.New() + + return b, nil +} + +func (s *server) Start(ctx context.Context) { +} diff --git a/pkg/tools/benchmark/job/handler/grpc/option.go b/pkg/tools/benchmark/job/handler/grpc/option.go new file mode 100644 index 0000000000..4319ef3a74 --- /dev/null +++ b/pkg/tools/benchmark/job/handler/grpc/option.go @@ -0,0 +1,22 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 grpc provides grpc server logic +package grpc + +type Option func(*server) error + +var defaultOpts = []Option{} diff --git a/pkg/tools/benchmark/job/handler/rest/handler.go b/pkg/tools/benchmark/job/handler/rest/handler.go new file mode 100644 index 0000000000..f752dbbbbf --- /dev/null +++ b/pkg/tools/benchmark/job/handler/rest/handler.go @@ -0,0 +1,38 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 rest provides rest api logic +package rest + +import ( + "github.com/vdaas/vald/apis/grpc/v1/benchmark" +) + +type Handler interface{} + +type handler struct { + js benchmark.JobServer +} + +func New(opts ...Option) Handler { + h := new(handler) + + for _, opt := range append(defaultOpts, opts...) { + opt(h) + } + + return h +} diff --git a/pkg/tools/benchmark/job/handler/rest/option.go b/pkg/tools/benchmark/job/handler/rest/option.go new file mode 100644 index 0000000000..ca3aee436d --- /dev/null +++ b/pkg/tools/benchmark/job/handler/rest/option.go @@ -0,0 +1,22 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 rest provides rest api logic +package rest + +type Option func(*handler) + +var defaultOpts = []Option{} diff --git a/pkg/tools/benchmark/job/router/doc.go b/pkg/tools/benchmark/job/router/doc.go new file mode 100644 index 0000000000..053c9aeb49 --- /dev/null +++ b/pkg/tools/benchmark/job/router/doc.go @@ -0,0 +1,18 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 router provides implementation of Go API for routing http Handler wrapped by rest.Func +package router diff --git a/pkg/tools/benchmark/job/router/option.go b/pkg/tools/benchmark/job/router/option.go new file mode 100644 index 0000000000..063f5ce6f2 --- /dev/null +++ b/pkg/tools/benchmark/job/router/option.go @@ -0,0 +1,47 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 router provides implementation of Go API for routing http Handler wrapped by rest.Func +package router + +import ( + "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/pkg/tools/benchmark/job/handler/rest" +) + +type Option func(*router) + +var defaultOpts = []Option{ + WithTimeout("3s"), +} + +func WithHandler(h rest.Handler) Option { + return func(r *router) { + r.handler = h + } +} + +func WithTimeout(timeout string) Option { + return func(r *router) { + r.timeout = timeout + } +} + +func WithErrGroup(eg errgroup.Group) Option { + return func(r *router) { + r.eg = eg + } +} diff --git a/pkg/tools/benchmark/job/router/router.go b/pkg/tools/benchmark/job/router/router.go new file mode 100644 index 0000000000..e032578e97 --- /dev/null +++ b/pkg/tools/benchmark/job/router/router.go @@ -0,0 +1,52 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 router provides implementation of Go API for routing http Handler wrapped by rest.Func +package router + +import ( + "net/http" + + "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/internal/net/http/middleware" + "github.com/vdaas/vald/internal/net/http/routing" + "github.com/vdaas/vald/pkg/tools/benchmark/job/handler/rest" +) + +type router struct { + handler rest.Handler + eg errgroup.Group + timeout string +} + +// New returns REST route&method information from handler interface +func New(opts ...Option) http.Handler { + r := new(router) + + for _, opt := range append(defaultOpts, opts...) { + opt(r) + } + + return routing.New( + routing.WithMiddleware( + middleware.NewTimeout( + middleware.WithTimeout(r.timeout), + middleware.WithErrorGroup(r.eg), + )), + routing.WithRoutes([]routing.Route{ + // TODO add REST API interface here + }...)) +} diff --git a/pkg/tools/benchmark/job/service/doc.go b/pkg/tools/benchmark/job/service/doc.go new file mode 100644 index 0000000000..eb86a28c5b --- /dev/null +++ b/pkg/tools/benchmark/job/service/doc.go @@ -0,0 +1,18 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service diff --git a/pkg/tools/benchmark/job/service/job.go b/pkg/tools/benchmark/job/service/job.go new file mode 100644 index 0000000000..5287d9c7f1 --- /dev/null +++ b/pkg/tools/benchmark/job/service/job.go @@ -0,0 +1,188 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service + +import ( + "context" + "os" + "reflect" + "syscall" + + "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/client/v1/client/vald" + "github.com/vdaas/vald/internal/config" + "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/test/data/hdf5" +) + +type Job interface { + PreStart(context.Context) error + Start(context.Context) (<-chan error, error) + Stop(context.Context) error +} + +type jobType int + +const ( + USERDEFINED jobType = iota + SEARCH +) + +func (jt jobType) String() string { + switch jt { + case USERDEFINED: + return "userdefined" + case SEARCH: + return "search" + } + return "" +} + +type job struct { + eg errgroup.Group + dimension int + dataset *config.BenchmarkDataset + jobType jobType + jobFunc func(context.Context, chan error) error + insertConfig *config.InsertConfig + updateConfig *config.UpdateConfig + upsertConfig *config.UpsertConfig + searchConfig *config.SearchConfig + removeConfig *config.RemoveConfig + client vald.Client + hdf5 hdf5.Data +} + +func New(opts ...Option) (Job, error) { + j := new(job) + for _, opt := range append(defaultOpts, opts...) { + if err := opt(j); err != nil { + return nil, errors.ErrOptionFailed(err, reflect.ValueOf(opt)) + } + } + if j.jobFunc == nil { + switch j.jobType { + case USERDEFINED: + opt := WithJobFunc(j.jobFunc) + err := opt(j) + return nil, errors.ErrOptionFailed(err, reflect.ValueOf(opt)) + case SEARCH: + j.jobFunc = j.search + } + } else if j.jobType != USERDEFINED { + log.Warnf("[benchmark job] userdefined jobFunc is set but jobType is set %s", j.jobType.String()) + } + return j, nil +} + +func (j *job) PreStart(ctx context.Context) error { + log.Infof("[benchmark job] start download dataset of %s", j.hdf5.GetName().String()) + if err := j.hdf5.Download(); err != nil { + return err + } + log.Infof("[benchmark job] success download dataset of %s", j.hdf5.GetName().String()) + log.Infof("[benchmark job] start load dataset of %s", j.hdf5.GetName().String()) + if err := j.hdf5.Read(); err != nil { + return err + } + log.Infof("[benchmark job] success load dataset of %s", j.hdf5.GetName().String()) + return nil +} + +func (j *job) Start(ctx context.Context) (<-chan error, error) { + ech := make(chan error, 3) + cech, err := j.client.Start(ctx) + if err != nil { + log.Error("[benchmark job] failed to start connection monitor") + return nil, err + } + j.eg.Go(func() error { + for { + select { + case <-ctx.Done(): + return nil + case ech <- <-cech: + } + } + }) + + j.eg.Go(func() (err error) { + defer func() { + p, perr := os.FindProcess(os.Getpid()) + if perr != nil { + log.Error(perr) + return + } + if err != nil { + select { + case <-ctx.Done(): + ech <- errors.Wrap(err, ctx.Err().Error()) + case ech <- err: + } + } + if err := p.Signal(syscall.SIGTERM); err != nil { + log.Error(err) + } + }() + err = j.jobFunc(ctx, ech) + if err != nil { + log.Errorf("[benchmark job] failed to job: %v", err) + } + return + }) + + return ech, nil +} + +func (j *job) Stop(ctx context.Context) (err error) { + err = j.client.Stop(ctx) + return +} + +func calcRecall(linearRes, searchRes []*payload.Object_Distance) (recall float64) { + if len(linearRes) == 0 || len(searchRes) == 0 { + return + } + linearIds := map[string]struct{}{} + for _, v := range linearRes { + linearIds[v.Id] = struct{}{} + } + for _, v := range searchRes { + if _, ok := linearIds[v.Id]; ok { + recall++ + } + } + return recall / float64(len(linearRes)) +} + +func genVec(data [][]float32, cfg *config.BenchmarkDataset) [][]float32 { + start := cfg.Range.Start + end := cfg.Range.End + if (end - start) < cfg.Indexes { + end = cfg.Indexes + } + num := end - start + 1 + if len(data) < num { + num = len(data) + end = start + num + 1 + } + vectors := data[start : end+1] + return vectors +} diff --git a/pkg/tools/benchmark/job/service/option.go b/pkg/tools/benchmark/job/service/option.go new file mode 100644 index 0000000000..38013805eb --- /dev/null +++ b/pkg/tools/benchmark/job/service/option.go @@ -0,0 +1,164 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service + +import ( + "context" + + "github.com/vdaas/vald/internal/client/v1/client/vald" + "github.com/vdaas/vald/internal/config" + "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/test/data/hdf5" +) + +type Option func(j *job) error + +var defaultOpts = []Option{ + WithDimension(748), + // TODO: set default config for client +} + +func WithDimension(dim int) Option { + return func(j *job) error { + if dim > 0 { + j.dimension = dim + } + return nil + } +} + +func WithInsertConfig(c *config.InsertConfig) Option { + return func(j *job) error { + if c != nil { + j.insertConfig = c + } + return nil + } +} + +func WithUpdateConfig(c *config.UpdateConfig) Option { + return func(j *job) error { + if c != nil { + j.updateConfig = c + } + return nil + } +} + +func WithUpsertConfig(c *config.UpsertConfig) Option { + return func(j *job) error { + if c != nil { + j.upsertConfig = c + } + return nil + } +} + +func WithSearchConfig(c *config.SearchConfig) Option { + return func(j *job) error { + if c != nil { + j.searchConfig = c + } + return nil + } +} + +func WithRemoveConfig(c *config.RemoveConfig) Option { + return func(j *job) error { + if c != nil { + j.removeConfig = c + } + return nil + } +} + +func WithValdClient(c vald.Client) Option { + return func(j *job) error { + if c == nil { + return errors.NewErrInvalidOption("client", c) + } + j.client = c + return nil + } +} + +func WithErrGroup(eg errgroup.Group) Option { + return func(j *job) error { + if eg == nil { + return errors.NewErrInvalidOption("client", eg) + } + j.eg = eg + return nil + } +} + +func WithHdf5(d hdf5.Data) Option { + return func(j *job) error { + if d == nil { + return errors.NewErrInvalidOption("hdf5", d) + } + j.hdf5 = d + return nil + } +} + +func WithDataset(d *config.BenchmarkDataset) Option { + return func(j *job) error { + if d == nil { + return errors.NewErrInvalidOption("dataset", d) + } + j.dataset = d + return nil + } +} + +func WithJobTypeByString(t string) Option { + var jt jobType + switch t { + case "userdefined": + jt = USERDEFINED + case "search": + jt = SEARCH + } + return WithJobType(jt) +} + +func WithJobType(jt jobType) Option { + return func(j *job) error { + switch jt { + case USERDEFINED: + j.jobType = jt + case SEARCH: + j.jobType = jt + default: + return errors.NewErrInvalidOption("jobType", jt) + } + return nil + } +} + +func WithJobFunc(jf func(context.Context, chan error) error) Option { + return func(j *job) error { + if jf == nil { + return errors.NewErrInvalidOption("jobFunc", jf) + } + j.jobFunc = jf + return nil + } +} diff --git a/pkg/tools/benchmark/job/service/search.go b/pkg/tools/benchmark/job/service/search.go new file mode 100644 index 0000000000..2d14a6d84b --- /dev/null +++ b/pkg/tools/benchmark/job/service/search.go @@ -0,0 +1,105 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service + +import ( + "context" + "testing" + "time" + + "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/log" +) + +func (j *job) search(ctx context.Context, ech chan error) error { + log.Info("[benchmark job] Start benchmarking search") + if j.searchConfig == nil { + err := errors.NewErrInvalidOption("searchConfig", j.searchConfig) + select { + case <-ctx.Done(): + if err != context.Canceled { + ech <- errors.Wrap(err, ctx.Err().Error()) + } else { + ech <- err + } + case ech <- err: + } + return err + } + + // create data + vecs := genVec(j.hdf5.GetTest(), j.dataset) + timeout, _ := time.ParseDuration(j.searchConfig.Timeout) + cfg := &payload.Search_Config{ + Num: uint32(j.searchConfig.Num), + MinNum: uint32(j.searchConfig.MinNum), + Radius: float32(j.searchConfig.Radius), + Epsilon: float32(j.searchConfig.Epsilon), + Timeout: timeout.Nanoseconds(), + } + for i := 0; i < len(vecs); i++ { + log.Infof("[benchmark job] Start search: iter = %d", i) + lres, err := j.client.LinearSearch(ctx, &payload.Search_Request{ + Vector: vecs[i], + Config: cfg, + }) + if err != nil { + select { + case <-ctx.Done(): + if !errors.Is(err, context.Canceled) { + ech <- errors.Wrap(err, ctx.Err().Error()) + } else { + ech <- err + } + case ech <- err: + } + return err + } + bres := testing.Benchmark(func(b *testing.B) { + b.Helper() + b.ResetTimer() + start := time.Now() + sres, err := j.client.Search(ctx, &payload.Search_Request{ + Vector: vecs[i], + Config: cfg, + }) + if err != nil { + select { + case <-ctx.Done(): + if errors.Is(err, context.Canceled) { + ech <- errors.Wrap(err, ctx.Err().Error()) + } else { + ech <- err + } + case ech <- err: + break + } + } + latency := time.Since(start) + recall := calcRecall(lres.Results, sres.Results) + b.ReportMetric(recall, "recall") + b.ReportMetric(float64(latency.Microseconds()), "latency") + }) + // TODO: send metrics to the Prometeus + log.Infof("[benchmark job] Finish search bench: iter= %d \n%#v\n", i, bres) + } + + log.Info("[benchmark job] Finish benchmarking search") + return nil +} diff --git a/pkg/tools/benchmark/job/usecase/benchmarkd.go b/pkg/tools/benchmark/job/usecase/benchmarkd.go new file mode 100644 index 0000000000..4ceafcbfa9 --- /dev/null +++ b/pkg/tools/benchmark/job/usecase/benchmarkd.go @@ -0,0 +1,245 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 usecase provides usecases +package usecase + +import ( + "context" + "strconv" + + "github.com/vdaas/vald/internal/client/v1/client/vald" + iconf "github.com/vdaas/vald/internal/config" + "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/net/grpc" + "github.com/vdaas/vald/internal/net/grpc/interceptor/server/recover" + "github.com/vdaas/vald/internal/observability" + infometrics "github.com/vdaas/vald/internal/observability/metrics/info" + "github.com/vdaas/vald/internal/runner" + "github.com/vdaas/vald/internal/safety" + "github.com/vdaas/vald/internal/servers/server" + "github.com/vdaas/vald/internal/servers/starter" + "github.com/vdaas/vald/internal/test/data/hdf5" + "github.com/vdaas/vald/pkg/tools/benchmark/job/config" + handler "github.com/vdaas/vald/pkg/tools/benchmark/job/handler/grpc" + "github.com/vdaas/vald/pkg/tools/benchmark/job/handler/rest" + "github.com/vdaas/vald/pkg/tools/benchmark/job/router" + "github.com/vdaas/vald/pkg/tools/benchmark/job/service" +) + +type run struct { + eg errgroup.Group + cfg *config.Config + job service.Job + h handler.Benchmark + server starter.Server + observability observability.Observability +} + +func New(cfg *config.Config) (r runner.Runner, err error) { + log.Info("pkg/tools/benchmark/job/cmd start") + eg := errgroup.Get() + + if cfg.Job.Target != nil { + addr := cfg.Job.Target.Host + ":" + strconv.Itoa(cfg.Job.Target.Port) + if cfg.Job.ClientConfig.Addrs == nil { + cfg.Job.ClientConfig.Addrs = []string{addr} + } else { + cfg.Job.ClientConfig.Addrs = append(cfg.Job.ClientConfig.Addrs, addr) + } + } + + copts, err := cfg.Job.ClientConfig.Opts() + if err != nil { + return nil, err + } + if cfg.Job.ClientConfig.DialOption == nil { + copts = append(copts, grpc.WithInsecure(true)) + } + gcli := grpc.New(copts...) + vcli, err := vald.New( + vald.WithAddrs(cfg.Job.ClientConfig.Addrs...), + vald.WithClient(gcli), + ) + if err != nil { + return nil, err + } + + d, err := hdf5.New( + hdf5.WithNameByString(cfg.Job.Dataset.Name), + ) + if err != nil { + return nil, err + } + log.Info("pkg/tools/benchmark/job/cmd success d") + + job, err := service.New( + service.WithErrGroup(eg), + service.WithValdClient(vcli), + service.WithDataset(cfg.Job.Dataset), + service.WithJobTypeByString(cfg.Job.JobType), + service.WithDimension(cfg.Job.Dimension), + service.WithInsertConfig(cfg.Job.InsertConfig), + service.WithUpdateConfig(cfg.Job.UpdateConfig), + service.WithUpsertConfig(cfg.Job.UpsertConfig), + service.WithSearchConfig(cfg.Job.SearchConfig), + service.WithRemoveConfig(cfg.Job.RemoveConfig), + service.WithHdf5(d), + ) + if err != nil { + return nil, err + } + + h, err := handler.New() + if err != nil { + return nil, err + } + + grpcServerOptions := []server.Option{ + server.WithGRPCRegistFunc(func(srv *grpc.Server) { + // TODO register grpc server handler here + }), + server.WithGRPCOption( + grpc.ChainUnaryInterceptor(recover.RecoverInterceptor()), + grpc.ChainStreamInterceptor(recover.RecoverStreamInterceptor()), + ), + server.WithPreStartFunc(func() error { + // TODO check unbackupped upstream + return nil + }), + server.WithPreStopFunction(func() error { + // TODO backup all index data here + return nil + }), + } + + var obs observability.Observability + if cfg.Observability.Enabled { + obs, err = observability.NewWithConfig( + cfg.Observability, + infometrics.New("vald_benchmark_job_info", "Benchmark Job info", *cfg.Job), + ) + if err != nil { + return nil, err + } + } + + srv, err := starter.New( + starter.WithConfig(cfg.Server), + starter.WithREST(func(sc *iconf.Server) []server.Option { + return []server.Option{ + server.WithHTTPHandler( + router.New( + router.WithTimeout(sc.HTTP.HandlerTimeout), + router.WithErrGroup(eg), + router.WithHandler( + rest.New( + // TODO pass grpc handler to REST option + ), + ), + )), + } + }), + starter.WithGRPC(func(sc *iconf.Server) []server.Option { + return grpcServerOptions + }), + ) + if err != nil { + return nil, err + } + log.Info("pkg/tools/benchmark/job/cmd end") + + return &run{ + eg: eg, + cfg: cfg, + job: job, + h: h, + server: srv, + observability: obs, + }, nil +} + +func (r *run) PreStart(ctx context.Context) error { + if r.observability != nil { + if err := r.observability.PreStart(ctx); err != nil { + return err + } + } + if r.job != nil { + return r.job.PreStart(ctx) + } + return nil +} + +func (r *run) Start(ctx context.Context) (<-chan error, error) { + ech := make(chan error, 3) + var oech, dech, sech <-chan error + r.eg.Go(safety.RecoverFunc(func() (err error) { + defer close(ech) + if r.observability != nil { + oech = r.observability.Start(ctx) + } + dech, err = r.job.Start(ctx) + + if err != nil { + ech <- err + return err + } + + r.h.Start(ctx) + + sech = r.server.ListenAndServe(ctx) + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case err = <-oech: + case err = <-dech: + case err = <-sech: + } + if err != nil { + select { + case <-ctx.Done(): + log.Error(err) + return errors.Wrap(ctx.Err(), err.Error()) + case ech <- err: + } + } + } + })) + return ech, nil +} + +func (r *run) PreStop(ctx context.Context) error { + return nil +} + +func (r *run) Stop(ctx context.Context) error { + if r.observability != nil { + r.observability.Stop(ctx) + } + if r.job != nil { + r.job.Stop(ctx) + } + return r.server.Shutdown(ctx) +} + +func (r *run) PostStop(ctx context.Context) error { + return nil +} diff --git a/pkg/tools/benchmark/operator/README.md b/pkg/tools/benchmark/operator/README.md new file mode 100644 index 0000000000..3300ab85a9 --- /dev/null +++ b/pkg/tools/benchmark/operator/README.md @@ -0,0 +1 @@ +# vald benchmark operator diff --git a/pkg/tools/benchmark/operator/config/config.go b/pkg/tools/benchmark/operator/config/config.go new file mode 100644 index 0000000000..915ec1251a --- /dev/null +++ b/pkg/tools/benchmark/operator/config/config.go @@ -0,0 +1,175 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 config stores all server application settings +package config + +import ( + "github.com/vdaas/vald/internal/config" +) + +// GlobalConfig is type alias for config.GlobalConfig +type GlobalConfig = config.GlobalConfig + +// Config represent a application setting data content (config.yaml). +// In K8s environment, this configuration is stored in K8s ConfigMap. +type Config struct { + config.GlobalConfig `json:",inline" yaml:",inline"` + + // Server represent all server configurations + Server *config.Servers `json:"server_config" yaml:"server_config"` + + // Observability represent observability configurations + Observability *config.Observability `json:"observability" yaml:"observability"` + + // Scenario represents benchmark scenario configurations + Scenario *config.BenchmarkScenario `json:"scenario" yaml:"scenario"` +} + +// NewConfig represents the set config from the given setting file path. +func NewConfig(path string) (cfg *Config, err error) { + err = config.Read(path, &cfg) + if err != nil { + return nil, err + } + + if cfg != nil { + cfg.Bind() + } + + if cfg.Server != nil { + cfg.Server = cfg.Server.Bind() + } + + if cfg.Observability != nil { + cfg.Observability = cfg.Observability.Bind() + } + + if cfg.Scenario != nil { + cfg.Scenario = cfg.Scenario.Bind() + } else { + cfg.Scenario = new(config.BenchmarkScenario) + } + + return cfg, nil +} + +// func FakeData() { +// d := Config{ +// Version: "v0.0.1", +// Server: &config.Servers{ +// Servers: []*config.Server{ +// { +// Name: "agent-rest", +// Host: "127.0.0.1", +// Port: 8080, +// Mode: "REST", +// ProbeWaitTime: "3s", +// ShutdownDuration: "5s", +// HandlerTimeout: "5s", +// IdleTimeout: "2s", +// ReadHeaderTimeout: "1s", +// ReadTimeout: "1s", +// WriteTimeout: "1s", +// }, +// { +// Name: "agent-grpc", +// Host: "127.0.0.1", +// Port: 8082, +// Mode: "GRPC", +// }, +// }, +// MetricsServers: []*config.Server{ +// { +// Name: "pprof", +// Host: "127.0.0.1", +// Port: 6060, +// Mode: "REST", +// ProbeWaitTime: "3s", +// ShutdownDuration: "5s", +// HandlerTimeout: "5s", +// IdleTimeout: "2s", +// ReadHeaderTimeout: "1s", +// ReadTimeout: "1s", +// WriteTimeout: "1s", +// }, +// }, +// HealthCheckServers: []*config.Server{ +// { +// Name: "livenesss", +// Host: "127.0.0.1", +// Port: 3000, +// }, +// { +// Name: "readiness", +// Host: "127.0.0.1", +// Port: 3001, +// }, +// }, +// StartUpStrategy: []string{ +// "livenesss", +// "pprof", +// "agent-grpc", +// "agent-rest", +// "readiness", +// }, +// ShutdownStrategy: []string{ +// "readiness", +// "agent-rest", +// "agent-grpc", +// "pprof", +// "livenesss", +// }, +// FullShutdownDuration: "30s", +// TLS: &config.TLS{ +// Enabled: false, +// Cert: "/path/to/cert", +// Key: "/path/to/key", +// CA: "/path/to/ca", +// }, +// }, +// Scenario: &config.BenchmarkScenario{ +// Target: &config.BenchmarkTarget{ +// Host: "localhost", +// Port: 8081, +// }, +// Dataset: &config.BenchmarkDataset{ +// Name: "fashion-mnist-784-euc", +// Group: "Train", +// Indexes: 10000, +// Range: &config.BenchmarkDatasetRange{ +// Start: 100000, +// End: 200000, +// }, +// }, +// Jobs: []*config.BenchmarkJob{ +// { +// JobType: "search", +// Replica: 1, +// Repetition: 1, +// Dimension: 784, +// Iter: 10000, +// Num: 10, +// MinNum: 100, +// Radius: -1, +// Epsilon: 0.1, +// Timeout: "5s", +// }, +// }, +// }, +// } +// fmt.Println(config.ToRawYaml(d)) +// } diff --git a/pkg/tools/benchmark/operator/config/doc.go b/pkg/tools/benchmark/operator/config/doc.go new file mode 100644 index 0000000000..57ef74c464 --- /dev/null +++ b/pkg/tools/benchmark/operator/config/doc.go @@ -0,0 +1,18 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 config stores all server application settings +package config diff --git a/pkg/tools/benchmark/operator/handler/doc.go b/pkg/tools/benchmark/operator/handler/doc.go new file mode 100644 index 0000000000..f1014141ea --- /dev/null +++ b/pkg/tools/benchmark/operator/handler/doc.go @@ -0,0 +1,17 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 handler diff --git a/pkg/tools/benchmark/operator/handler/grpc/handler.go b/pkg/tools/benchmark/operator/handler/grpc/handler.go new file mode 100644 index 0000000000..2ac0cd1346 --- /dev/null +++ b/pkg/tools/benchmark/operator/handler/grpc/handler.go @@ -0,0 +1,56 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 grpc provides grpc server logic +package grpc + +import ( + "context" + + "github.com/vdaas/vald/apis/grpc/v1/benchmark" + "github.com/vdaas/vald/internal/singleflight" + "github.com/vdaas/vald/pkg/tools/benchmark/operator/service" +) + +type Benchmark interface { + benchmark.JobServer + Start(context.Context) +} + +type server struct { + benchmark.UnimplementedJobServer + + scenario service.Scenario + group singleflight.Group +} + +func New(opts ...Option) (bm Benchmark, err error) { + b := new(server) + + for _, opt := range append(defaultOpts, opts...) { + err = opt(b) + if err != nil { + return nil, err + } + } + + b.group = singleflight.New() + + return b, nil +} + +func (s *server) Start(ctx context.Context) { +} diff --git a/pkg/tools/benchmark/operator/handler/grpc/option.go b/pkg/tools/benchmark/operator/handler/grpc/option.go new file mode 100644 index 0000000000..4319ef3a74 --- /dev/null +++ b/pkg/tools/benchmark/operator/handler/grpc/option.go @@ -0,0 +1,22 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 grpc provides grpc server logic +package grpc + +type Option func(*server) error + +var defaultOpts = []Option{} diff --git a/pkg/tools/benchmark/operator/handler/rest/handler.go b/pkg/tools/benchmark/operator/handler/rest/handler.go new file mode 100644 index 0000000000..f752dbbbbf --- /dev/null +++ b/pkg/tools/benchmark/operator/handler/rest/handler.go @@ -0,0 +1,38 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 rest provides rest api logic +package rest + +import ( + "github.com/vdaas/vald/apis/grpc/v1/benchmark" +) + +type Handler interface{} + +type handler struct { + js benchmark.JobServer +} + +func New(opts ...Option) Handler { + h := new(handler) + + for _, opt := range append(defaultOpts, opts...) { + opt(h) + } + + return h +} diff --git a/pkg/tools/benchmark/operator/handler/rest/option.go b/pkg/tools/benchmark/operator/handler/rest/option.go new file mode 100644 index 0000000000..ca3aee436d --- /dev/null +++ b/pkg/tools/benchmark/operator/handler/rest/option.go @@ -0,0 +1,22 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 rest provides rest api logic +package rest + +type Option func(*handler) + +var defaultOpts = []Option{} diff --git a/pkg/tools/benchmark/operator/router/doc.go b/pkg/tools/benchmark/operator/router/doc.go new file mode 100644 index 0000000000..053c9aeb49 --- /dev/null +++ b/pkg/tools/benchmark/operator/router/doc.go @@ -0,0 +1,18 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 router provides implementation of Go API for routing http Handler wrapped by rest.Func +package router diff --git a/pkg/tools/benchmark/operator/router/option.go b/pkg/tools/benchmark/operator/router/option.go new file mode 100644 index 0000000000..fec24bfa26 --- /dev/null +++ b/pkg/tools/benchmark/operator/router/option.go @@ -0,0 +1,47 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 router provides implementation of Go API for routing http Handler wrapped by rest.Func +package router + +import ( + "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/pkg/tools/benchmark/operator/handler/rest" +) + +type Option func(*router) + +var defaultOpts = []Option{ + WithTimeout("3s"), +} + +func WithHandler(h rest.Handler) Option { + return func(r *router) { + r.handler = h + } +} + +func WithTimeout(timeout string) Option { + return func(r *router) { + r.timeout = timeout + } +} + +func WithErrGroup(eg errgroup.Group) Option { + return func(r *router) { + r.eg = eg + } +} diff --git a/pkg/tools/benchmark/operator/router/router.go b/pkg/tools/benchmark/operator/router/router.go new file mode 100644 index 0000000000..9ca7f60c95 --- /dev/null +++ b/pkg/tools/benchmark/operator/router/router.go @@ -0,0 +1,52 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 router provides implementation of Go API for routing http Handler wrapped by rest.Func +package router + +import ( + "net/http" + + "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/internal/net/http/middleware" + "github.com/vdaas/vald/internal/net/http/routing" + "github.com/vdaas/vald/pkg/tools/benchmark/operator/handler/rest" +) + +type router struct { + handler rest.Handler + eg errgroup.Group + timeout string +} + +// New returns REST route&method information from handler interface +func New(opts ...Option) http.Handler { + r := new(router) + + for _, opt := range append(defaultOpts, opts...) { + opt(r) + } + + return routing.New( + routing.WithMiddleware( + middleware.NewTimeout( + middleware.WithTimeout(r.timeout), + middleware.WithErrorGroup(r.eg), + )), + routing.WithRoutes([]routing.Route{ + // TODO add REST API interface here + }...)) +} diff --git a/pkg/tools/benchmark/operator/service/doc.go b/pkg/tools/benchmark/operator/service/doc.go new file mode 100644 index 0000000000..eb86a28c5b --- /dev/null +++ b/pkg/tools/benchmark/operator/service/doc.go @@ -0,0 +1,18 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service diff --git a/pkg/tools/benchmark/operator/service/option.go b/pkg/tools/benchmark/operator/service/option.go new file mode 100644 index 0000000000..43b10ec581 --- /dev/null +++ b/pkg/tools/benchmark/operator/service/option.go @@ -0,0 +1,55 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service + +import ( + "time" + + "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/internal/errors" +) + +// Option represents the functional option for scenario struct. +type Option func(sc *scenario) error + +var defaultOpts = []Option{ + WithReconcileCheckDuration("10s"), +} + +// WithErrGroup sets the error group to scenario. +func WithErrGroup(eg errgroup.Group) Option { + return func(sc *scenario) error { + if eg == nil { + return errors.NewErrInvalidOption("client", eg) + } + sc.eg = eg + return nil + } +} + +// WithReconcileCheckDuration sets the reconcile check duration from input string. +func WithReconcileCheckDuration(ts string) Option { + return func(sc *scenario) error { + t, err := time.ParseDuration(ts) + if err != nil { + return err + } + sc.rcd = t + return nil + } +} diff --git a/pkg/tools/benchmark/operator/service/scenario.go b/pkg/tools/benchmark/operator/service/scenario.go new file mode 100644 index 0000000000..dcd15ceab0 --- /dev/null +++ b/pkg/tools/benchmark/operator/service/scenario.go @@ -0,0 +1,372 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service + +import ( + "context" + "reflect" + "strconv" + "sync/atomic" + "time" + + "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/k8s" + "github.com/vdaas/vald/internal/k8s/job" + v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" + benchjob "github.com/vdaas/vald/internal/k8s/vald/benchmark/job" + benchscenario "github.com/vdaas/vald/internal/k8s/vald/benchmark/scenario" + "github.com/vdaas/vald/internal/log" + corev1 "k8s.io/api/core/v1" + k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type Scenario interface { + PreStart(context.Context) error + Start(context.Context) (<-chan error, error) +} + +type scenario struct { + jobs atomic.Value + jobName string + jobNamespace string + jobTemplate string // row manifest template data of rebalance job. + jobObject *job.Job // object generated from template. + currentDeviationJobName atomic.Value + + scenarios atomic.Value + benchjobs atomic.Value + + rcd time.Duration // reconcile check duration + eg errgroup.Group + ctrl k8s.Controller +} + +// New creates the new scenario struct to handle vald benchmark job scenario. +// When the input options are invalid, the error will be returned. +func New(opts ...Option) (Scenario, error) { + sc := new(scenario) + for _, opt := range append(defaultOpts, opts...) { + if err := opt(sc); err != nil { + return nil, errors.ErrOptionFailed(err, reflect.ValueOf(opt)) + } + } + + err := sc.initCtrl() + if err != nil { + return nil, err + } + return sc, nil +} + +// initCtrl creates the controller for reconcile k8s objects. +func (sc *scenario) initCtrl() (err error) { + // watcher of vald benchmark scenario resource + bs, err := benchscenario.New( + benchscenario.WithControllerName("benchmark scenario resource"), + benchscenario.WithNamespaces(sc.jobNamespace), + benchscenario.WithOnErrorFunc(func(err error) { + log.Errorf("failed to reconcile:", err) + }), + benchscenario.WithOnReconcileFunc(sc.benchScenarioReconcile), + ) + if err != nil { + return + } + + // watcher of vald benchmark job resource + bj, err := benchjob.New( + benchjob.WithControllerName("benchmark job resource"), + benchjob.WithOnErrorFunc(func(err error) { + log.Errorf("failed to reconcile:", err) + }), + benchjob.WithNamespaces(sc.jobNamespace), + benchjob.WithOnErrorFunc(func(err error) { + log.Error(err) + }), + benchjob.WithOnReconcileFunc(sc.benchJobReconcile), + ) + if err != nil { + return + } + + // watcher of job resource + job, err := job.New( + job.WithControllerName("benchmark job"), + job.WithNamespaces(sc.jobNamespace), + job.WithOnErrorFunc(func(err error) { + log.Errorf("failed to reconcile:", err) + }), + job.WithOnReconcileFunc(sc.jobReconcile), + ) + if err != nil { + return + } + + // create reconcile controller which watches valdbenchmarkscenario resource, valdbenchmarkjob resource, and job resource. + sc.ctrl, err = k8s.New( + k8s.WithControllerName("vald benchmark operator"), + k8s.WithResourceController(bs), + k8s.WithResourceController(bj), + k8s.WithResourceController(job), + ) + return +} + +// jobReconcile gets k8s job list and watches theirs STATUS. +// Then, it processes according STATUS. +func (sc *scenario) jobReconcile(ctx context.Context, jobList map[string][]job.Job) { + // TODO: impl logic + // for k, v := range jobList { + // log.Warnf("key: %s, value: %v", k, v) + // } + return +} + +// benchmarkJobReconcile gets the vald benchmark job resource list and create Job for running benchmark job. +func (sc *scenario) benchJobReconcile(ctx context.Context, jobList map[string]v1.ValdBenchmarkJob) { + log.Debugf("[reconcile benchmark job resource] job list: %#v", jobList) + if len(jobList) == 0 { + sc.benchjobs.Store(make(map[string]*v1.ValdBenchmarkJob, 0)) + log.Info("[reconcile benchmark job resource] job resource not found") + return + } + var cbjl map[string]*v1.ValdBenchmarkJob + if ok := sc.benchjobs.Load(); ok == nil { + cbjl = make(map[string]*v1.ValdBenchmarkJob, 0) + } else { + cbjl = ok.(map[string]*v1.ValdBenchmarkJob) + } + for k, job := range jobList { + if oldJob := cbjl[k]; oldJob != nil { + if oldJob.GetGeneration() != job.GetGeneration() { + // TODO: delete old version job + cbjl[k] = &job + } + } else { + log.Info("create job: ", k) + err := sc.createJob(ctx, job) + if err != nil { + log.Errorf("[reconcile benchmark job resource] failed to create job: %s", err.Error()) + } + cbjl[k] = &job + } + } + sc.benchjobs.Store(cbjl) +} + +// benchScenarioReconcile gets the vald benchmark scenario list and create vald benchmark job resource according to it. +func (sc *scenario) benchScenarioReconcile(ctx context.Context, scenarioList map[string]v1.ValdBenchmarkScenario) { + log.Debugf("[reconcile benchmark scenario resource] scenario list: %#v", scenarioList) + if len(scenarioList) == 0 { + sc.scenarios.Store(make(map[string]*v1.ValdBenchmarkScenario, 0)) + sc.benchjobs.Store(make(map[string]*v1.ValdBenchmarkJob, 0)) + log.Info("[reconcile benchmark scenario resource]: scenario not found") + return + } + var cbsl map[string]*v1.ValdBenchmarkScenario + if ok := sc.scenarios.Load(); ok == nil { + cbsl = make(map[string]*v1.ValdBenchmarkScenario, len(scenarioList)) + } else { + cbsl = ok.(map[string]*v1.ValdBenchmarkScenario) + } + for k, scenario := range scenarioList { + if oldScenario := cbsl[k]; oldScenario == nil { + err := sc.createBenchmarkJob(ctx, scenario) + if err != nil { + log.Errorf("[reconcile scenario] failed to create job: %s", err.Error()) + } + cbsl[k] = &scenario + } else { + // TODO delete old jobresource and job + if oldScenario.GetGeneration() != scenario.GetGeneration() { + cbsl[k] = &scenario + } + } + } + sc.scenarios.Store(cbsl) +} + +// createBenchmarkJob creates the ValdBenchmarkJob crd for running job. +func (sc *scenario) createBenchmarkJob(ctx context.Context, scenario v1.ValdBenchmarkScenario) error { + ownerRef := []k8smeta.OwnerReference{ + { + APIVersion: scenario.APIVersion, + Kind: scenario.Kind, + Name: scenario.Name, + UID: scenario.UID, + }, + } + for _, job := range scenario.Spec.Jobs { + bj := new(v1.ValdBenchmarkJob) + // set metadata.name, metadata.namespace + bj.Name = scenario.GetName() + "-" + job.JobType + "-" + strconv.FormatInt(time.Now().UnixNano(), 10) + bj.Namespace = scenario.GetNamespace() + bj.SetOwnerReferences(ownerRef) + + // set specs + bj.Spec = *job + if bj.Spec.Target == nil { + bj.Spec.Target = scenario.Spec.Target + } + if bj.Spec.Dataset == nil { + bj.Spec.Dataset = scenario.Spec.Dataset + } + // create benchmark job resource + c := sc.ctrl.GetManager().GetClient() + if err := c.Create(ctx, bj); err != nil { + // TODO: create new custom error + return err + } + } + return nil +} + +// createJobTemplate creates the job template for crating k8s job resource. +// ns and name are required to set job environment value. +func createJobTemplate(ns, name string) job.Job { + j := new(job.Job) + backoffLimit := int32(0) + j.Spec.BackoffLimit = &backoffLimit + j.Spec.Template.Spec.ServiceAccountName = "vald-benchmark-operator" + j.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyNever + j.Spec.Template.Spec.Containers = []corev1.Container{ + { + Name: "vald-benchmark-job", + Image: "local-registry:5000/vdaas/vald-benchmark-job:latest", + ImagePullPolicy: corev1.PullAlways, + LivenessProbe: &corev1.Probe{ + InitialDelaySeconds: int32(60), + PeriodSeconds: int32(10), + TimeoutSeconds: int32(300), + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{ + "/go/bin/job", + "-v", + }, + }, + }, + }, + StartupProbe: &corev1.Probe{ + FailureThreshold: int32(30), + PeriodSeconds: int32(10), + TimeoutSeconds: int32(300), + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{ + "/go/bin/job", + "-v", + }, + }, + }, + }, + Ports: []corev1.ContainerPort{ + { + Name: "liveness", + Protocol: corev1.ProtocolTCP, + ContainerPort: int32(3000), + }, + { + Name: "readiness", + Protocol: corev1.ProtocolTCP, + ContainerPort: int32(3001), + }, + }, + Env: []corev1.EnvVar{ + { + Name: "CRD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, + { + Name: "CRD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.labels['job-name']", + }, + }, + }, + }, + }, + } + return *j +} + +// createJob creates benchmark job from benchmark job resource. +func (sc *scenario) createJob(ctx context.Context, bjr v1.ValdBenchmarkJob) (err error) { + bj := createJobTemplate(bjr.Namespace, bjr.Name) + bj.Name = bjr.Name + bj.Namespace = bjr.Namespace + bj.SetOwnerReferences(bjr.GetOwnerReferences()) + // create job + c := sc.ctrl.GetManager().GetClient() + if err = c.Create(ctx, &bj); err != nil { + // TODO: create new custom error + return err + } + if ok := sc.jobs.Load(); ok == nil { + sc.jobs.Store([]string{bj.Name}) + return + } else { + jobs := sc.jobs.Load().([]string) + jobs = append(jobs, bj.Name) + sc.jobs.Swap(jobs) + } + return +} + +func (sc *scenario) PreStart(ctx context.Context) error { + log.Infof("[benchmark scenario] start vald benchmark scenario") + return nil +} + +func (sc *scenario) Start(ctx context.Context) (<-chan error, error) { + scch, err := sc.ctrl.Start(ctx) + if err != nil { + return nil, err + } + ech := make(chan error, 2) + sc.eg.Go(func() error { + defer close(ech) + dt := time.NewTicker(sc.rcd) + defer dt.Stop() + for { + select { + case <-ctx.Done(): + return nil + case <-dt.C: + // TODO: Get Resource + _, ok := sc.scenarios.Load().(map[string]*v1.ValdBenchmarkScenario) + if !ok { + log.Info("benchmark scenario resource is empty") + continue + } + case err = <-scch: + if err != nil { + ech <- err + } + } + } + }) + + return ech, nil +} diff --git a/pkg/tools/benchmark/operator/usecase/benchmarkd.go b/pkg/tools/benchmark/operator/usecase/benchmarkd.go new file mode 100644 index 0000000000..116dfd6cd7 --- /dev/null +++ b/pkg/tools/benchmark/operator/usecase/benchmarkd.go @@ -0,0 +1,200 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 usecase provides usecases +package usecase + +import ( + "context" + + iconf "github.com/vdaas/vald/internal/config" + "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/net/grpc" + "github.com/vdaas/vald/internal/net/grpc/interceptor/server/recover" + + "github.com/vdaas/vald/internal/observability" + infometrics "github.com/vdaas/vald/internal/observability/metrics/info" + "github.com/vdaas/vald/internal/runner" + "github.com/vdaas/vald/internal/safety" + "github.com/vdaas/vald/internal/servers/server" + "github.com/vdaas/vald/internal/servers/starter" + "github.com/vdaas/vald/pkg/tools/benchmark/operator/config" + handler "github.com/vdaas/vald/pkg/tools/benchmark/operator/handler/grpc" + "github.com/vdaas/vald/pkg/tools/benchmark/operator/handler/rest" + "github.com/vdaas/vald/pkg/tools/benchmark/operator/router" + "github.com/vdaas/vald/pkg/tools/benchmark/operator/service" +) + +type run struct { + eg errgroup.Group + cfg *config.Config + scenario service.Scenario + h handler.Benchmark + server starter.Server + observability observability.Observability +} + +func New(cfg *config.Config) (r runner.Runner, err error) { + log.Info("pkg/tools/benchmark/scenario/cmd start") + + eg := errgroup.Get() + + log.Info("pkg/tools/benchmark/scenario/cmd success d") + + sc, err := service.New( + service.WithErrGroup(eg), + ) + if err != nil { + return nil, err + } + + h, err := handler.New() + if err != nil { + return nil, err + } + + grpcServerOptions := []server.Option{ + server.WithGRPCRegistFunc(func(srv *grpc.Server) { + // TODO register grpc server handler here + }), + server.WithGRPCOption( + grpc.ChainUnaryInterceptor(recover.RecoverInterceptor()), + grpc.ChainStreamInterceptor(recover.RecoverStreamInterceptor()), + ), + server.WithPreStartFunc(func() error { + // TODO check unbackupped upstream + return nil + }), + server.WithPreStopFunction(func() error { + // TODO backup all index data here + return nil + }), + } + + var obs observability.Observability + if cfg.Observability.Enabled { + obs, err = observability.NewWithConfig( + cfg.Observability, + infometrics.New("vald_benchmark_scenario_info", "Benchmark Scenario info", *cfg.Scenario), + ) + if err != nil { + return nil, err + } + } + + srv, err := starter.New( + starter.WithConfig(cfg.Server), + starter.WithREST(func(sc *iconf.Server) []server.Option { + return []server.Option{ + server.WithHTTPHandler( + router.New( + router.WithTimeout(sc.HTTP.HandlerTimeout), + router.WithErrGroup(eg), + router.WithHandler( + rest.New( + // TODO pass grpc handler to REST option + ), + ), + )), + } + }), + starter.WithGRPC(func(sc *iconf.Server) []server.Option { + return grpcServerOptions + }), + ) + if err != nil { + return nil, err + } + log.Info("pkg/tools/benchmark/scenario/cmd end") + + return &run{ + eg: eg, + cfg: cfg, + scenario: sc, + h: h, + server: srv, + observability: obs, + }, nil +} + +func (r *run) PreStart(ctx context.Context) error { + if r.observability != nil { + if err := r.observability.PreStart(ctx); err != nil { + return err + } + } + if r.scenario != nil { + return r.scenario.PreStart(ctx) + } + return nil +} + +func (r *run) Start(ctx context.Context) (<-chan error, error) { + ech := make(chan error, 3) + var oech, dech, sech <-chan error + r.eg.Go(safety.RecoverFunc(func() (err error) { + defer close(ech) + if r.observability != nil { + oech = r.observability.Start(ctx) + } + + dech, err = r.scenario.Start(ctx) + if err != nil { + ech <- err + return err + } + + r.h.Start(ctx) + + sech = r.server.ListenAndServe(ctx) + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case err = <-oech: + case err = <-dech: + case err = <-sech: + } + if err != nil { + select { + case <-ctx.Done(): + log.Error(err) + return errors.Wrap(ctx.Err(), err.Error()) + case ech <- err: + } + } + } + })) + return ech, nil +} + +func (r *run) PreStop(ctx context.Context) error { + return nil +} + +func (r *run) Stop(ctx context.Context) error { + if r.observability != nil { + r.observability.Stop(ctx) + } + return r.server.Shutdown(ctx) +} + +func (r *run) PostStop(ctx context.Context) error { + return nil +} From 558864d166f9b0b013ade61e91003f818c26b8f8 Mon Sep 17 00:00:00 2001 From: Kiichiro YUKAWA Date: Wed, 8 Mar 2023 18:08:11 +0900 Subject: [PATCH 02/11] :sparkles: impl status handle of continuous benchmark crds (#1955) Signed-off-by: vankichi --- internal/config/benchmark.go | 31 +- internal/errors/benchmark.go | 14 +- internal/k8s/client/client.go | 9 + .../k8s/crd/benchmark/valdbenchmarkjob.yaml | 1 + .../crd/benchmark/valdbenchmarkscenario.yaml | 1 + internal/k8s/reconciler.go | 6 +- .../k8s/vald/benchmark/api/v1/job_types.go | 1 + .../vald/benchmark/api/v1/scenario_types.go | 1 + .../k8s/vald/benchmark/job/job_template.go | 110 ++++ .../vald/benchmark/job/job_template_option.go | 114 ++++ pkg/tools/benchmark/job/config/config.go | 16 +- pkg/tools/benchmark/job/service/job.go | 60 +- pkg/tools/benchmark/job/service/option.go | 65 +- pkg/tools/benchmark/job/usecase/benchmarkd.go | 3 + .../operator/handler/grpc/handler.go | 2 +- .../benchmark/operator/service/operator.go | 612 ++++++++++++++++++ .../benchmark/operator/service/option.go | 23 +- .../benchmark/operator/service/scenario.go | 372 ----------- .../benchmark/operator/usecase/benchmarkd.go | 12 +- 19 files changed, 1036 insertions(+), 417 deletions(-) create mode 100644 internal/k8s/vald/benchmark/job/job_template.go create mode 100644 internal/k8s/vald/benchmark/job/job_template_option.go create mode 100644 pkg/tools/benchmark/operator/service/operator.go delete mode 100644 pkg/tools/benchmark/operator/service/scenario.go diff --git a/internal/config/benchmark.go b/internal/config/benchmark.go index 37bcba94b9..83fb41b2dd 100644 --- a/internal/config/benchmark.go +++ b/internal/config/benchmark.go @@ -17,21 +17,26 @@ // Package config providers configuration type and load configuration logic package config +import "github.com/vdaas/vald/internal/k8s/client" + // BenchmarkJob represents the configuration for the internal benchmark search job. type BenchmarkJob struct { - Target *BenchmarkTarget `json:"target,omitempty" yaml:"target"` - Dataset *BenchmarkDataset `json:"dataset,omitempty" yaml:"dataset"` - Dimension int `json:"dimension,omitempty" yaml:"dimension"` - Replica int `json:"replica,omitempty" yaml:"replica"` - Repetition int `json:"repetition,omitempty" yaml:"repetition"` - JobType string `json:"job_type,omitempty" yaml:"job_type"` - InsertConfig *InsertConfig `json:"insert_config,omitempty" yaml:"insert_config"` - UpdateConfig *UpdateConfig `json:"update_config,omitempty" yaml:"update_config"` - UpsertConfig *UpsertConfig `json:"upsert_config,omitempty" yaml:"upsert_config"` - SearchConfig *SearchConfig `json:"search_config,omitempty" yaml:"search_config"` - RemoveConfig *RemoveConfig `json:"remove_config,omitempty" yaml:"remove_config"` - ClientConfig *GRPCClient `json:"client_config,omitempty" yaml:"client_config"` - Rules []*BenchmarkJobRule `json:"rules,omitempty" yaml:"rules"` + Target *BenchmarkTarget `json:"target,omitempty" yaml:"target"` + Dataset *BenchmarkDataset `json:"dataset,omitempty" yaml:"dataset"` + Dimension int `json:"dimension,omitempty" yaml:"dimension"` + Replica int `json:"replica,omitempty" yaml:"replica"` + Repetition int `json:"repetition,omitempty" yaml:"repetition"` + JobType string `json:"job_type,omitempty" yaml:"job_type"` + InsertConfig *InsertConfig `json:"insert_config,omitempty" yaml:"insert_config"` + UpdateConfig *UpdateConfig `json:"update_config,omitempty" yaml:"update_config"` + UpsertConfig *UpsertConfig `json:"upsert_config,omitempty" yaml:"upsert_config"` + SearchConfig *SearchConfig `json:"search_config,omitempty" yaml:"search_config"` + RemoveConfig *RemoveConfig `json:"remove_config,omitempty" yaml:"remove_config"` + ClientConfig *GRPCClient `json:"client_config,omitempty" yaml:"client_config"` + Rules []*BenchmarkJobRule `json:"rules,omitempty" yaml:"rules"` + BeforeJobName string `json:"before_job_name,omitempty" yaml:"before_job_name"` + BeforeJobNamespace string `json:"before_job_namespace,omitempty" yaml:"before_job_namespace"` + Client client.Client `json:"client,omitempty" yaml:"client"` } // BenchmarkScenario represents the configuration for the internal benchmark scenario. diff --git a/internal/errors/benchmark.go b/internal/errors/benchmark.go index 4223ee8bef..d4fa5cd3c4 100644 --- a/internal/errors/benchmark.go +++ b/internal/errors/benchmark.go @@ -17,4 +17,16 @@ // Package errors provides benchmark error package errors -var ErrInvalidCoreMode = New("invalid core mode") +var ( + ErrInvalidCoreMode = New("invalid core mode") + + // ErrFailedToCreateBenchmarkJob represents a function to generate an error that failed to create benchmark job crd. + ErrFailedToCreateBenchmarkJob = func(err error, jn string) error { + return Wrapf(err, "could not create benchmark job resource: %s ", jn) + } + + // ErrFailedToCreateJob represents a function to generate an error that failed to create job resource. + ErrFailedToCreateJob = func(err error, jn string) error { + return Wrapf(err, "could not create job: %s ", jn) + } +) diff --git a/internal/k8s/client/client.go b/internal/k8s/client/client.go index 9822d36afe..8cd8f7f6f8 100644 --- a/internal/k8s/client/client.go +++ b/internal/k8s/client/client.go @@ -26,6 +26,15 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" ) +type ( + Object = cli.Object + ObjectKey = cli.ObjectKey + DeleteAllOfOptions = cli.DeleteAllOfOptions + ListOptions = cli.ListOptions + MatchingLabels = cli.MatchingLabels + InNamespace = cli.InNamespace +) + type Client interface { // Get retrieves an obj for the given object key from the Kubernetes Cluster. // obj must be a struct pointer so that obj can be updated with the response diff --git a/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml b/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml index 34a92c68ca..cdefc9dcaa 100644 --- a/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml +++ b/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml @@ -64,6 +64,7 @@ spec: description: ValdBenchmarkJobStatus defines the observed state of ValdBenchmarkJob enum: - NotReady + - Completed - Available - Healthy type: string diff --git a/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml b/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml index 09a73f17cc..135dee3c30 100644 --- a/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml +++ b/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml @@ -61,6 +61,7 @@ spec: description: ValdBenchmarkScenarioStatus defines the observed state of ValdBenchmarkScenario enum: - NotReady + - Completed - Available - Healthy type: string diff --git a/internal/k8s/reconciler.go b/internal/k8s/reconciler.go index 08f3a5df49..894c9cae11 100644 --- a/internal/k8s/reconciler.go +++ b/internal/k8s/reconciler.go @@ -25,6 +25,7 @@ import ( "github.com/vdaas/vald/internal/net" "github.com/vdaas/vald/internal/safety" "github.com/vdaas/vald/internal/sync/errgroup" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -34,7 +35,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -type Manager = manager.Manager +type ( + Manager = manager.Manager + OwnerReference = v1.OwnerReference +) type Controller interface { Start(ctx context.Context) (<-chan error, error) diff --git a/internal/k8s/vald/benchmark/api/v1/job_types.go b/internal/k8s/vald/benchmark/api/v1/job_types.go index dc3996dbb0..40c71232d7 100644 --- a/internal/k8s/vald/benchmark/api/v1/job_types.go +++ b/internal/k8s/vald/benchmark/api/v1/job_types.go @@ -42,6 +42,7 @@ type BenchmarkJobStatus string const ( BenchmarkJobNotReady = BenchmarkJobStatus("NotReady") + BenchmarkJobCompleted = BenchmarkJobStatus("Completed") BenchmarkJobAvailable = BenchmarkJobStatus("Available") BenchmarkJobHealthy = BenchmarkJobStatus("Healthy") ) diff --git a/internal/k8s/vald/benchmark/api/v1/scenario_types.go b/internal/k8s/vald/benchmark/api/v1/scenario_types.go index 0ef2adb341..8973a1e6b5 100644 --- a/internal/k8s/vald/benchmark/api/v1/scenario_types.go +++ b/internal/k8s/vald/benchmark/api/v1/scenario_types.go @@ -31,6 +31,7 @@ type ValdBenchmarkScenarioStatus string const ( BenchmarkScenarioNotReady ValdBenchmarkScenarioStatus = "NotReady" + BenchmarkScenarioCompleted ValdBenchmarkScenarioStatus = "Completed" BenchmarkScenarioAvailable ValdBenchmarkScenarioStatus = "Available" BenchmarkScenarioHealthy ValdBenchmarkScenarioStatus = "Healthy" ) diff --git a/internal/k8s/vald/benchmark/job/job_template.go b/internal/k8s/vald/benchmark/job/job_template.go new file mode 100644 index 0000000000..2830e23931 --- /dev/null +++ b/internal/k8s/vald/benchmark/job/job_template.go @@ -0,0 +1,110 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 job manages the main logic of benchmark job. +package job + +import ( + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" +) + +type benchmarkJobTemplate = batchv1.Job + +const ( + SvcAccountName = "vald-benchmark-operator" + ContainerName = "vald-benchmark-job" + ContainerImage = "local-registry:5000/vdaas/vald-benchmark-job:latest" + + RestartPolicyAlways corev1.RestartPolicy = "Always" + RestartPolicyOnFailure corev1.RestartPolicy = "OnFailure" + RestartPolicyNever corev1.RestartPolicy = "Never" +) + +// NewBenchmarkJobTemplate creates the job template for crating k8s job resource. +func NewBenchmarkJobTemplate(opts ...BenchmarkJobOption) (benchmarkJobTemplate, error) { + jobTmpl := new(benchmarkJobTemplate) + for _, opt := range append(defaultBenchmarkJobOpts, opts...) { + err := opt(jobTmpl) + if err != nil { + return *jobTmpl, err + } + } + jobTmpl.Spec.Template.Spec.Containers = []corev1.Container{ + { + Name: ContainerName, + Image: ContainerImage, + ImagePullPolicy: corev1.PullAlways, + LivenessProbe: &corev1.Probe{ + InitialDelaySeconds: int32(60), + PeriodSeconds: int32(10), + TimeoutSeconds: int32(300), + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{ + "/go/bin/job", + "-v", + }, + }, + }, + }, + StartupProbe: &corev1.Probe{ + FailureThreshold: int32(30), + PeriodSeconds: int32(10), + TimeoutSeconds: int32(300), + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{ + "/go/bin/job", + "-v", + }, + }, + }, + }, + Ports: []corev1.ContainerPort{ + { + Name: "liveness", + Protocol: corev1.ProtocolTCP, + ContainerPort: int32(3000), + }, + { + Name: "readiness", + Protocol: corev1.ProtocolTCP, + ContainerPort: int32(3001), + }, + }, + Env: []corev1.EnvVar{ + { + Name: "CRD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, + { + Name: "CRD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.labels['job-name']", + }, + }, + }, + }, + }, + } + return *jobTmpl, nil +} diff --git a/internal/k8s/vald/benchmark/job/job_template_option.go b/internal/k8s/vald/benchmark/job/job_template_option.go new file mode 100644 index 0000000000..79b29a81b8 --- /dev/null +++ b/internal/k8s/vald/benchmark/job/job_template_option.go @@ -0,0 +1,114 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 job + +import ( + "github.com/vdaas/vald/internal/k8s" + corev1 "k8s.io/api/core/v1" +) + +// BenchmarkJobOption represents the option for create benchmark job template. +type BenchmarkJobOption func(b *benchmarkJobTemplate) error + +var defaultBenchmarkJobOpts = []BenchmarkJobOption{ + WithSvcAccountName(SvcAccountName), + WithRestartPolicy(RestartPolicyNever), +} + +// WithSvcAccountName sets the service account name for benchmark job. +func WithSvcAccountName(name string) BenchmarkJobOption { + return func(b *benchmarkJobTemplate) error { + if len(name) > 0 { + b.Spec.Template.Spec.ServiceAccountName = name + } + return nil + } +} + +// WithRestartPolicy sets the job restart policy for benchmark job. +func WithRestartPolicy(rp corev1.RestartPolicy) BenchmarkJobOption { + return func(b *benchmarkJobTemplate) error { + if len(rp) > 0 { + b.Spec.Template.Spec.RestartPolicy = rp + } + return nil + } +} + +// WithBackoffLimit sets the job backoff limit for benchmark job. +func WithBackoffLimit(bo int32) BenchmarkJobOption { + return func(b *benchmarkJobTemplate) error { + b.Spec.BackoffLimit = &bo + return nil + } +} + +// WithName sets the job name of benchmark job. +func WithName(name string) BenchmarkJobOption { + return func(b *benchmarkJobTemplate) error { + b.Name = name + return nil + } +} + +// WithNamespace specify namespace where job will execute. +func WithNamespace(ns string) BenchmarkJobOption { + return func(b *benchmarkJobTemplate) error { + b.Namespace = ns + return nil + } +} + +// WithOwnerRef sets the OwnerReference to the job resource. +func WithOwnerRef(refs []k8s.OwnerReference) BenchmarkJobOption { + return func(b *benchmarkJobTemplate) error { + if len(refs) > 0 { + b.OwnerReferences = refs + } + return nil + } +} + +// WithCompletions sets the job completion. +func WithCompletions(com int32) BenchmarkJobOption { + return func(b *benchmarkJobTemplate) error { + if com > 1 { + b.Spec.Completions = &com + } + return nil + } +} + +// WithParallelism sets the job parallelism. +func WithParallelism(parallelism int32) BenchmarkJobOption { + return func(b *benchmarkJobTemplate) error { + if parallelism > 1 { + b.Spec.Parallelism = ¶llelism + } + return nil + } +} + +// WithLabel sets the label to the job resource. +func WithLabel(label map[string]string) BenchmarkJobOption { + return func(b *benchmarkJobTemplate) error { + if len(label) > 0 { + b.Labels = label + } + return nil + } +} diff --git a/pkg/tools/benchmark/job/config/config.go b/pkg/tools/benchmark/job/config/config.go index 36620e1dbc..34aa8e34fe 100644 --- a/pkg/tools/benchmark/job/config/config.go +++ b/pkg/tools/benchmark/job/config/config.go @@ -85,11 +85,15 @@ func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { // Get config from applied ValdBenchmarkJob custom resource var jobResource v1.ValdBenchmarkJob - c, err := client.New(client.WithSchemeBuilder(*v1.SchemeBuilder)) - if err != nil { - log.Warn(err.Error()) + if cfg.Job.Client == nil { + c, err := client.New(client.WithSchemeBuilder(*v1.SchemeBuilder)) + if err != nil { + log.Error(err.Error()) + return nil, err + } + cfg.Job.Client = c } - err = c.Get(ctx, NAME, NAMESPACE, &jobResource) + err = cfg.Job.Client.Get(ctx, NAME, NAMESPACE, &jobResource) if err != nil { log.Warn(err.Error()) } else { @@ -105,6 +109,10 @@ func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { cfg.Job.SearchConfig = jobResource.Spec.SearchConfig cfg.Job.RemoveConfig = jobResource.Spec.RemoveConfig cfg.Job.ClientConfig = jobResource.Spec.ClientConfig + if annotations := jobResource.GetAnnotations(); annotations != nil { + cfg.Job.BeforeJobName = annotations["before-job-name"] + cfg.Job.BeforeJobNamespace = annotations["before-job-namespace"] + } } return cfg, nil diff --git a/pkg/tools/benchmark/job/service/job.go b/pkg/tools/benchmark/job/service/job.go index 5287d9c7f1..69c0d43f0e 100644 --- a/pkg/tools/benchmark/job/service/job.go +++ b/pkg/tools/benchmark/job/service/job.go @@ -22,13 +22,17 @@ import ( "os" "reflect" "syscall" + "time" "github.com/vdaas/vald/apis/grpc/v1/payload" "github.com/vdaas/vald/internal/client/v1/client/vald" "github.com/vdaas/vald/internal/config" "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/k8s/client" + v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/safety" "github.com/vdaas/vald/internal/test/data/hdf5" ) @@ -56,18 +60,22 @@ func (jt jobType) String() string { } type job struct { - eg errgroup.Group - dimension int - dataset *config.BenchmarkDataset - jobType jobType - jobFunc func(context.Context, chan error) error - insertConfig *config.InsertConfig - updateConfig *config.UpdateConfig - upsertConfig *config.UpsertConfig - searchConfig *config.SearchConfig - removeConfig *config.RemoveConfig - client vald.Client - hdf5 hdf5.Data + eg errgroup.Group + dimension int + dataset *config.BenchmarkDataset + jobType jobType + jobFunc func(context.Context, chan error) error + insertConfig *config.InsertConfig + updateConfig *config.UpdateConfig + upsertConfig *config.UpsertConfig + searchConfig *config.SearchConfig + removeConfig *config.RemoveConfig + client vald.Client + hdf5 hdf5.Data + beforeJobName string + beforeJobNamespace string + k8sClient client.Client + beforeJobDur time.Duration } func New(opts ...Option) (Job, error) { @@ -93,6 +101,34 @@ func New(opts ...Option) (Job, error) { } func (j *job) PreStart(ctx context.Context) error { + if len(j.beforeJobName) != 0 { + var jobResource v1.ValdBenchmarkJob + log.Info("[benchmark job] check before benchjob is completed or not...") + j.eg.Go(safety.RecoverFunc(func() error { + dt := time.NewTicker(j.beforeJobDur) + defer dt.Stop() + for { + select { + case <-ctx.Done(): + return nil + case <-dt.C: + err := j.k8sClient.Get(ctx, j.beforeJobName, j.beforeJobNamespace, &jobResource) + if err != nil { + return err + } + if jobResource.Status == v1.BenchmarkJobCompleted { + log.Infof("[benchmark job ] before job (%s) is completed, job service will start soon.", j.beforeJobName) + return nil + } + log.Infof("[benchmark job] before job (%s) is not completed...", j.beforeJobName) + } + } + })) + if err := j.eg.Wait(); err != nil { + return err + } + } + log.Infof("[benchmark job] start download dataset of %s", j.hdf5.GetName().String()) if err := j.hdf5.Download(); err != nil { return err diff --git a/pkg/tools/benchmark/job/service/option.go b/pkg/tools/benchmark/job/service/option.go index 38013805eb..998f9f8700 100644 --- a/pkg/tools/benchmark/job/service/option.go +++ b/pkg/tools/benchmark/job/service/option.go @@ -24,16 +24,20 @@ import ( "github.com/vdaas/vald/internal/config" "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/k8s/client" "github.com/vdaas/vald/internal/test/data/hdf5" + "github.com/vdaas/vald/internal/timeutil" ) type Option func(j *job) error var defaultOpts = []Option{ - WithDimension(748), // TODO: set default config for client + WithDimension(748), + WithBeforeJobDuration("30s"), } +// WithDimension sets the vector's dimension for running benchmark job with dataset. func WithDimension(dim int) Option { return func(j *job) error { if dim > 0 { @@ -43,6 +47,7 @@ func WithDimension(dim int) Option { } } +// WithInsertConfig sets the insert API config for running insert request job. func WithInsertConfig(c *config.InsertConfig) Option { return func(j *job) error { if c != nil { @@ -52,6 +57,7 @@ func WithInsertConfig(c *config.InsertConfig) Option { } } +// WithUpdateConfig sets the update API config for running update request job. func WithUpdateConfig(c *config.UpdateConfig) Option { return func(j *job) error { if c != nil { @@ -61,6 +67,7 @@ func WithUpdateConfig(c *config.UpdateConfig) Option { } } +// WithUpsertConfig sets the upsert API config for running upsert request job. func WithUpsertConfig(c *config.UpsertConfig) Option { return func(j *job) error { if c != nil { @@ -70,6 +77,7 @@ func WithUpsertConfig(c *config.UpsertConfig) Option { } } +// WithSearchConfig sets the search API config for running search request job. func WithSearchConfig(c *config.SearchConfig) Option { return func(j *job) error { if c != nil { @@ -79,6 +87,7 @@ func WithSearchConfig(c *config.SearchConfig) Option { } } +// WithRemoveConfig sets the remove API config for running remove request job. func WithRemoveConfig(c *config.RemoveConfig) Option { return func(j *job) error { if c != nil { @@ -88,6 +97,7 @@ func WithRemoveConfig(c *config.RemoveConfig) Option { } } +// WithValdClient sets the Vald client for sending request to the target Vald cluster. func WithValdClient(c vald.Client) Option { return func(j *job) error { if c == nil { @@ -98,16 +108,18 @@ func WithValdClient(c vald.Client) Option { } } +// WithErrGroup sets the errgroup to the job struct to handle errors. func WithErrGroup(eg errgroup.Group) Option { return func(j *job) error { if eg == nil { - return errors.NewErrInvalidOption("client", eg) + return errors.NewErrInvalidOption("error group", eg) } j.eg = eg return nil } } +// WithHdf5 sets the hdf5.Data which is used for benchmark job dataset. func WithHdf5(d hdf5.Data) Option { return func(j *job) error { if d == nil { @@ -118,6 +130,7 @@ func WithHdf5(d hdf5.Data) Option { } } +// WithDataset sets the config.BenchmarkDataset including benchmakr dataset name, group name of hdf5.Data, the number of index, start range and end range. func WithDataset(d *config.BenchmarkDataset) Option { return func(j *job) error { if d == nil { @@ -128,6 +141,7 @@ func WithDataset(d *config.BenchmarkDataset) Option { } } +// WithJobTypeByString converts given string to JobType. func WithJobTypeByString(t string) Option { var jt jobType switch t { @@ -139,6 +153,7 @@ func WithJobTypeByString(t string) Option { return WithJobType(jt) } +// WithJobType sets the jobType for running benchmark job. func WithJobType(jt jobType) Option { return func(j *job) error { switch jt { @@ -153,6 +168,7 @@ func WithJobType(jt jobType) Option { } } +// WithJobFunc sets the job function. func WithJobFunc(jf func(context.Context, chan error) error) Option { return func(j *job) error { if jf == nil { @@ -162,3 +178,48 @@ func WithJobFunc(jf func(context.Context, chan error) error) Option { return nil } } + +// WithBeforeJobName sets the beforeJobName which we should wait for until finish before running job. +func WithBeforeJobName(bjn string) Option { + return func(j *job) error { + if len(bjn) > 0 { + j.beforeJobName = bjn + } + return nil + } +} + +// WithBeforeJobNamespace sets the beforeJobNamespace of the beforeJobName which we should wait for until finish before running job. +func WithBeforeJobNamespace(bjns string) Option { + return func(j *job) error { + if len(bjns) > 0 { + j.beforeJobNamespace = bjns + } + return nil + } +} + +// WithBeforeJobDuration sets the duration for watching beforeJobName's status. +func WithBeforeJobDuration(dur string) Option { + return func(j *job) error { + if len(dur) == 0 { + return nil + } + dur, err := timeutil.Parse(dur) + if err != nil { + return err + } + j.beforeJobDur = dur + return nil + } +} + +// WithK8sClient binds the k8s client to the job struct which is used for get BenchmarkJobResource from Kubernetes API server. +func WithK8sClient(cli client.Client) Option { + return func(j *job) error { + if cli != nil { + j.k8sClient = cli + } + return nil + } +} diff --git a/pkg/tools/benchmark/job/usecase/benchmarkd.go b/pkg/tools/benchmark/job/usecase/benchmarkd.go index 4ceafcbfa9..4bd9917b32 100644 --- a/pkg/tools/benchmark/job/usecase/benchmarkd.go +++ b/pkg/tools/benchmark/job/usecase/benchmarkd.go @@ -100,6 +100,9 @@ func New(cfg *config.Config) (r runner.Runner, err error) { service.WithSearchConfig(cfg.Job.SearchConfig), service.WithRemoveConfig(cfg.Job.RemoveConfig), service.WithHdf5(d), + service.WithBeforeJobName(cfg.Job.BeforeJobName), + service.WithBeforeJobNamespace(cfg.Job.BeforeJobNamespace), + service.WithK8sClient(cfg.Job.Client), ) if err != nil { return nil, err diff --git a/pkg/tools/benchmark/operator/handler/grpc/handler.go b/pkg/tools/benchmark/operator/handler/grpc/handler.go index 2ac0cd1346..8120e778b0 100644 --- a/pkg/tools/benchmark/operator/handler/grpc/handler.go +++ b/pkg/tools/benchmark/operator/handler/grpc/handler.go @@ -33,7 +33,7 @@ type Benchmark interface { type server struct { benchmark.UnimplementedJobServer - scenario service.Scenario + operator service.Operator group singleflight.Group } diff --git a/pkg/tools/benchmark/operator/service/operator.go b/pkg/tools/benchmark/operator/service/operator.go new file mode 100644 index 0000000000..9dc749c386 --- /dev/null +++ b/pkg/tools/benchmark/operator/service/operator.go @@ -0,0 +1,612 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service + +import ( + "context" + "reflect" + "strconv" + "sync/atomic" + "time" + + "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/k8s" + "github.com/vdaas/vald/internal/k8s/client" + "github.com/vdaas/vald/internal/k8s/job" + v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" + benchjob "github.com/vdaas/vald/internal/k8s/vald/benchmark/job" + benchscenario "github.com/vdaas/vald/internal/k8s/vald/benchmark/scenario" + "github.com/vdaas/vald/internal/log" +) + +type Operator interface { + PreStart(context.Context) error + Start(context.Context) (<-chan error, error) +} + +type scenario struct { + Crd *v1.ValdBenchmarkScenario + BenchJobStatus map[string]v1.BenchmarkJobStatus +} + +const ( + Scenario = "scenario" + BenchmarkName = "benchmark-name" + BeforeJobName = "before-job-name" + BeforeJobNamespace = "before-job-namespace" +) + +type operator struct { + jobNamespace string + scenarios atomic.Pointer[map[string]*scenario] + benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + jobs atomic.Pointer[map[string]string] + rcd time.Duration // reconcile check duration + eg errgroup.Group + ctrl k8s.Controller +} + +// New creates the new scenario struct to handle vald benchmark job scenario. +// When the input options are invalid, the error will be returned. +func New(opts ...Option) (Operator, error) { + operator := new(operator) + for _, opt := range append(defaultOpts, opts...) { + if err := opt(operator); err != nil { + return nil, errors.ErrOptionFailed(err, reflect.ValueOf(opt)) + } + } + + err := operator.initCtrl() + if err != nil { + return nil, err + } + return operator, nil +} + +// initCtrl creates the controller for reconcile k8s objects. +func (o *operator) initCtrl() error { + // watcher of vald benchmark scenario resource + benchScenario, err := benchscenario.New( + benchscenario.WithControllerName("benchmark scenario resource"), + benchscenario.WithNamespaces(o.jobNamespace), + benchscenario.WithOnErrorFunc(func(err error) { + log.Errorf("failed to reconcile benchmark scenario resource:", err) + }), + benchscenario.WithOnReconcileFunc(o.benchScenarioReconcile), + ) + if err != nil { + return err + } + + // watcher of vald benchmark job resource + benchJob, err := benchjob.New( + benchjob.WithControllerName("benchmark job resource"), + benchjob.WithOnErrorFunc(func(err error) { + log.Errorf("failed to reconcile benchmark job resource:", err) + }), + benchjob.WithNamespaces(o.jobNamespace), + benchjob.WithOnErrorFunc(func(err error) { + log.Error(err) + }), + benchjob.WithOnReconcileFunc(o.benchJobReconcile), + ) + if err != nil { + return err + } + + // watcher of job resource + job, err := job.New( + job.WithControllerName("benchmark job"), + job.WithNamespaces(o.jobNamespace), + job.WithOnErrorFunc(func(err error) { + log.Errorf("failed to reconcile job resource:", err) + }), + job.WithOnReconcileFunc(o.jobReconcile), + ) + if err != nil { + return err + } + + // create reconcile controller which watches valdbenchmarkscenario resource, valdbenchmarkjob resource, and job resource. + o.ctrl, err = k8s.New( + k8s.WithControllerName("vald benchmark scenario operator"), + k8s.WithResourceController(benchScenario), + k8s.WithResourceController(benchJob), + k8s.WithResourceController(job), + ) + return err +} + +func (o *operator) getAtomicScenario() map[string]*scenario { + if v := o.scenarios.Load(); v != nil { + return *(v) + } + return nil +} + +func (o *operator) getAtomicBenchJob() map[string]*v1.ValdBenchmarkJob { + if v := o.benchjobs.Load(); v != nil { + return *(v) + } + return nil +} + +func (o *operator) getAtomicJob() map[string]string { + if v := o.jobs.Load(); v != nil { + return *(v) + } + return nil +} + +// jobReconcile gets k8s job list and watches theirs STATUS. +// Then, it processes according STATUS. +func (o *operator) jobReconcile(ctx context.Context, jobList map[string][]job.Job) { + log.Debug("[reconcile job] start") + if len(jobList) == 0 { + log.Info("[reconcile job] no job is founded") + o.jobs.Store(&map[string]string{}) + return + } + cjobs := o.getAtomicJob() + if cjobs == nil { + cjobs = map[string]string{} + } + // jobStatus is used for update benchmark job resource status + benchmarkJobStatus := make(map[string]v1.BenchmarkJobStatus) + // jobNames is used for check whether cjobs has delted job. + // If cjobs has the delted job, it will be remove the end of jobReconcile function. + jobNames := map[string]struct{}{} + for _, jobs := range jobList { + cnt := len(jobs) + var name string + for _, job := range jobs { + if job.GetNamespace() != o.jobNamespace { + continue + } + jobNames[job.GetName()] = struct{}{} + if _, ok := cjobs[job.Name]; !ok && job.Status.CompletionTime == nil { + cjobs[job.GetName()] = job.Namespace + benchmarkJobStatus[job.GetName()] = v1.BenchmarkJobAvailable + continue + } else { + name = job.GetName() + } + if job.Status.Active == 0 && job.Status.Succeeded != 0 { + cnt-- + } + } + if cnt == 0 && len(name) != 0 { + benchmarkJobStatus[name] = v1.BenchmarkJobCompleted + } + } + if len(benchmarkJobStatus) != 0 { + _, err := o.updateBenchmarkJobStatus(ctx, benchmarkJobStatus) + if err != nil { + log.Error(err.Error) + } + } + // delete job which is not be in `jobList` from cj. + for k := range cjobs { + if _, ok := jobNames[k]; !ok { + delete(cjobs, k) + } + } + o.jobs.Store(&cjobs) + log.Debug("[reconcile job] finish") +} + +// benchmarkJobReconcile gets the vald benchmark job resource list and create Job for running benchmark job. +func (o *operator) benchJobReconcile(ctx context.Context, benchJobList map[string]v1.ValdBenchmarkJob) { + log.Debugf("[reconcile benchmark job resource] job list: %#v", benchJobList) + if len(benchJobList) == 0 { + o.benchjobs.Store(&(map[string]*v1.ValdBenchmarkJob{})) + log.Info("[reconcile benchmark job resource] job resource not found") + return + } + cbjl := o.getAtomicBenchJob() + if cbjl == nil { + cbjl = make(map[string]*v1.ValdBenchmarkJob, 0) + } + jobStatus := make(map[string]v1.BenchmarkJobStatus) + for k := range benchJobList { + // update scenario status + job := benchJobList[k] + if scenarios := o.getAtomicScenario(); scenarios != nil { + on := job.GetOwnerReferences()[0].Name + if _, ok := scenarios[on]; ok { + if scenarios[on].BenchJobStatus == nil { + scenarios[on].BenchJobStatus = map[string]v1.BenchmarkJobStatus{} + } + scenarios[on].BenchJobStatus[job.Name] = job.Status + } + o.scenarios.Store(&scenarios) + } + if oldJob := cbjl[k]; oldJob != nil { + if oldJob.GetGeneration() != job.GetGeneration() { + if job.Status != "" && oldJob.Status != v1.BenchmarkJobCompleted { + // delete old version job + err := o.deleteJob(ctx, oldJob.GetName(), oldJob.GetGeneration()) + if err != nil { + log.Warnf("[reconcile benchmark job resource] failed to delete old version job: job name=%s, version=%d\t%s", oldJob.GetName(), oldJob.GetGeneration(), err.Error()) + } + cbjl[k] = &job + } + } else if oldJob.Status == "" { + jobStatus[oldJob.GetName()] = v1.BenchmarkJobAvailable + } + } else if len(job.Status) == 0 || job.Status == v1.BenchmarkJobNotReady { + log.Info("[reconcile benchmark job resource] create job: ", k) + err := o.createJob(ctx, job) + if err != nil { + log.Errorf("[reconcile benchmark job resource] failed to create job: %s", err.Error()) + } + jobStatus[job.Name] = v1.BenchmarkJobAvailable + cbjl[k] = &job + } + } + // delete benchmark job which is not be in `benchJobList` from cbjl. + for k := range cbjl { + if _, ok := benchJobList[k]; !ok { + delete(cbjl, k) + } + } + o.benchjobs.Store(&cbjl) + if len(jobStatus) != 0 { + _, err := o.updateBenchmarkJobStatus(ctx, jobStatus) + if err != nil { + log.Errorf("[reconcile benchmark job resource] failed update job status: %s", err) + } + } + log.Debug("[reconcile benchmark job resource] finish") +} + +// benchScenarioReconcile gets the vald benchmark scenario list and create vald benchmark job resource according to it. +func (o *operator) benchScenarioReconcile(ctx context.Context, scenarioList map[string]v1.ValdBenchmarkScenario) { + log.Debugf("[reconcile benchmark scenario resource] scenario list: %#v", scenarioList) + if len(scenarioList) == 0 { + log.Info("[reconcile benchmark scenario resource]: scenario not found") + o.scenarios.Store(&(map[string]*scenario{})) + o.benchjobs.Store(&(map[string]*v1.ValdBenchmarkJob{})) + o.jobs.Store(&(map[string]string{})) + return + } + cbsl := o.getAtomicScenario() + if cbsl == nil { + cbsl = map[string]*scenario{} + } + scenarioStatus := make(map[string]v1.ValdBenchmarkScenarioStatus) + for name := range scenarioList { + sc := scenarioList[name] + if oldScenario := cbsl[name]; oldScenario == nil { + // apply new crd which is not set yet. + jobNames, err := o.createBenchmarkJob(ctx, sc) + if err != nil { + log.Errorf("[reconcile benchmark scenario resource] failed to create benchmark job resource: %s", err.Error()) + } + cbsl[name] = &scenario{ + Crd: &sc, + BenchJobStatus: func() map[string]v1.BenchmarkJobStatus { + s := map[string]v1.BenchmarkJobStatus{} + for _, v := range jobNames { + s[v] = v1.BenchmarkJobNotReady + } + return s + }(), + } + scenarioStatus[sc.GetName()] = v1.BenchmarkScenarioHealthy + } else { + // apply updated crd which is already applied. + if oldScenario.Crd.GetGeneration() < sc.GetGeneration() { + // delete old job resource. If it is succeeded, job pod will be deleted automatically because of OwnerReference. + err := o.deleteBenchmarkJob(ctx, oldScenario.Crd.GetName(), oldScenario.Crd.Generation) + if err != nil { + log.Warnf("[reconcile benchmark scenario resource] failed to delete old version benchmark jobs: scenario name=%s, version=%d\t%s", oldScenario.Crd.GetName(), oldScenario.Crd.Generation, err.Error()) + } + // create new benchmark job resources of new version. + jobNames, err := o.createBenchmarkJob(ctx, sc) + if err != nil { + log.Errorf("[reconcile benchmark scenario resource] failed to create benchmark job resource: %s", err.Error()) + } + cbsl[name] = &scenario{ + Crd: &sc, + BenchJobStatus: func() map[string]v1.BenchmarkJobStatus { + s := map[string]v1.BenchmarkJobStatus{} + for _, v := range jobNames { + s[v] = v1.BenchmarkJobNotReady + } + return s + }(), + } + } else { + // only update status + if oldScenario.Crd.Status != sc.Status { + cbsl[name].Crd.Status = sc.Status + } + } + } + } + // delete stored crd which is not be in `scenarioList` from cbsl. + for k := range cbsl { + if _, ok := scenarioList[k]; !ok { + delete(cbsl, k) + } + } + o.scenarios.Store(&cbsl) + // Update scenario status + _, err := o.updateBenchmarkScenarioStatus(ctx, scenarioStatus) + if err != nil { + log.Errorf("[reconcile benchmark scenario resource] failed to update benchmark scenario resource status: %s", err.Error()) + } + log.Debug("[reconcile benchmark scenario resource] finish") +} + +// deleteBenchmarkJob deletes benchmark job resource according to given scenario name and generation. +func (o *operator) deleteBenchmarkJob(ctx context.Context, name string, generation int64) error { + opts := new(client.DeleteAllOfOptions) + client.MatchingLabels(map[string]string{ + Scenario: name + strconv.Itoa(int(generation)), + }).ApplyToDeleteAllOf(opts) + client.InNamespace(o.jobNamespace).ApplyToDeleteAllOf(opts) + return o.ctrl.GetManager().GetClient().DeleteAllOf(ctx, &v1.ValdBenchmarkJob{}, opts) +} + +// deleteJob deletes job resource according to given benchmark job name and generation. +func (o *operator) deleteJob(ctx context.Context, name string, generation int64) error { + opts := new(client.DeleteAllOfOptions) + client.MatchingLabels(map[string]string{ + BenchmarkName: name + strconv.Itoa(int(generation)), + }).ApplyToDeleteAllOf(opts) + client.InNamespace(o.jobNamespace).ApplyToDeleteAllOf(opts) + return o.ctrl.GetManager().GetClient().DeleteAllOf(ctx, &job.Job{}, opts) +} + +// createBenchmarkJob creates the ValdBenchmarkJob crd for running job. +func (o *operator) createBenchmarkJob(ctx context.Context, scenario v1.ValdBenchmarkScenario) ([]string, error) { + ownerRef := []k8s.OwnerReference{ + { + APIVersion: scenario.APIVersion, + Kind: scenario.Kind, + Name: scenario.Name, + UID: scenario.UID, + }, + } + jobNames := make([]string, 0) + var beforeJobName string + for _, job := range scenario.Spec.Jobs { + bj := new(v1.ValdBenchmarkJob) + // set metadata.name, metadata.namespace, OwnerReference + bj.Name = scenario.GetName() + "-" + job.JobType + "-" + strconv.FormatInt(time.Now().UnixNano(), 10) + bj.Namespace = scenario.GetNamespace() + bj.SetOwnerReferences(ownerRef) + // set label + labels := map[string]string{ + Scenario: scenario.GetName() + strconv.Itoa(int(scenario.Generation)), + } + bj.SetLabels(labels) + // set annotations for wating before job + annotations := map[string]string{ + BeforeJobName: beforeJobName, + BeforeJobNamespace: o.jobNamespace, + } + bj.SetAnnotations(annotations) + // set specs + bj.Spec = *job + if bj.Spec.Target == nil { + bj.Spec.Target = scenario.Spec.Target + } + if bj.Spec.Dataset == nil { + bj.Spec.Dataset = scenario.Spec.Dataset + } + // set status + bj.Status = v1.BenchmarkJobNotReady + // create benchmark job resource + c := o.ctrl.GetManager().GetClient() + if err := c.Create(ctx, bj); err != nil { + return nil, errors.ErrFailedToCreateBenchmarkJob(err, bj.GetName()) + } + jobNames = append(jobNames, bj.Name) + beforeJobName = bj.Name + } + return jobNames, nil +} + +// createJob creates benchmark job from benchmark job resource. +func (o *operator) createJob(ctx context.Context, bjr v1.ValdBenchmarkJob) error { + label := map[string]string{ + BenchmarkName: bjr.GetName() + strconv.Itoa(int(bjr.Generation)), + } + job, err := benchjob.NewBenchmarkJobTemplate( + benchjob.WithName(bjr.Name), + benchjob.WithNamespace(bjr.Namespace), + benchjob.WithLabel(label), + benchjob.WithCompletions(int32(bjr.Spec.Repetition)), + benchjob.WithParallelism(int32(bjr.Spec.Replica)), + benchjob.WithOwnerRef([]k8s.OwnerReference{ + { + APIVersion: bjr.APIVersion, + Kind: bjr.Kind, + Name: bjr.Name, + UID: bjr.UID, + }, + }), + ) + if err != nil { + return err + } + // create job + c := o.ctrl.GetManager().GetClient() + if err = c.Create(ctx, &job); err != nil { + return errors.ErrFailedToCreateJob(err, job.GetName()) + } + return nil +} + +// updateBenchmarkScenarioStatus updates status of ValdBenchmarkScenarioResource +func (o *operator) updateBenchmarkScenarioStatus(ctx context.Context, ss map[string]v1.ValdBenchmarkScenarioStatus) ([]string, error) { + var sns []string + if cbsl := o.getAtomicScenario(); cbsl != nil { + for name, status := range ss { + if scenario, ok := cbsl[name]; ok { + if scenario.Crd.Status == status { + continue + } + scenario.Crd.Status = status + cli := o.ctrl.GetManager().GetClient() + err := cli.Status().Update(ctx, scenario.Crd) + if err != nil { + log.Error(err.Error()) + continue + } + sns = append(sns, name) + } + } + } + return sns, nil +} + +// updateBenchmarkJobStatus updates status of ValdBenchmarkJobResource +func (o *operator) updateBenchmarkJobStatus(ctx context.Context, js map[string]v1.BenchmarkJobStatus) ([]string, error) { + var jns []string + if cbjl := o.getAtomicBenchJob(); cbjl != nil { + for name, status := range js { + if bjob, ok := cbjl[name]; ok { + if bjob.Status == status { + continue + } + bjob.Status = status + cli := o.ctrl.GetManager().GetClient() + err := cli.Status().Update(ctx, bjob) + if err != nil { + log.Error(err.Error()) + continue + } + jns = append(jns, name) + } + } + } + return jns, nil +} + +func (o *operator) checkJobsStatus(ctx context.Context, jobs map[string]string) error { + cbjl := o.getAtomicBenchJob() + if jobs == nil || cbjl == nil { + log.Infof("[check job status] no job launched") + return nil + } + job := new(job.Job) + c := o.ctrl.GetManager().GetClient() + jobStatus := map[string]v1.BenchmarkJobStatus{} + for name, ns := range jobs { + err := c.Get(ctx, client.ObjectKey{ + Namespace: ns, + Name: name, + }, job) + if err != nil { + return err + } + if job.Status.Active != 0 || job.Status.Failed != 0 { + continue + } + if job.Status.Succeeded != 0 { + if job, ok := cbjl[name]; ok { + if job.Status != v1.BenchmarkJobCompleted { + jobStatus[name] = v1.BenchmarkJobCompleted + } + } + } + + } + _, err := o.updateBenchmarkJobStatus(ctx, jobStatus) + return err +} + +func (o *operator) initAtomics() { + if cbsl := o.getAtomicScenario(); len(cbsl) > 0 { + o.scenarios.Store(&(map[string]*scenario{})) + } + if cbjl := o.getAtomicBenchJob(); len(cbjl) > 0 { + o.benchjobs.Store(&(map[string]*v1.ValdBenchmarkJob{})) + } + if cjl := o.getAtomicJob(); len(cjl) > 0 { + o.jobs.Store(&(map[string]string{})) + } +} + +func (o *operator) PreStart(ctx context.Context) error { + log.Infof("[benchmark scenario operator] start vald benchmark scenario operator") + return nil +} + +func (o *operator) Start(ctx context.Context) (<-chan error, error) { + scch, err := o.ctrl.Start(ctx) + if err != nil { + return nil, err + } + ech := make(chan error, 2) + o.eg.Go(func() error { + defer close(ech) + rcticker := time.NewTicker(o.rcd) + defer rcticker.Stop() + for { + select { + case <-ctx.Done(): + return nil + case <-rcticker.C: + cbsl := o.getAtomicScenario() + if cbsl == nil { + log.Info("benchmark scenario resource is empty") + // clear atomic pointer + o.initAtomics() + continue + } else { + scenarioStatus := make(map[string]v1.ValdBenchmarkScenarioStatus) + for name, scenario := range cbsl { + if scenario.Crd.Status != v1.BenchmarkScenarioCompleted { + cnt := len(scenario.BenchJobStatus) + for _, bjob := range scenario.BenchJobStatus { + if bjob == v1.BenchmarkJobCompleted { + cnt-- + } + } + if cnt == 0 { + scenarioStatus[name] = v1.BenchmarkScenarioCompleted + } + } + } + if _, err := o.updateBenchmarkScenarioStatus(ctx, scenarioStatus); err != nil { + log.Errorf("failed to update benchmark scenario to %s\terror: %s", v1.BenchmarkJobCompleted, err.Error()) + } + + } + // get job and check status + if jobs := o.getAtomicJob(); jobs != nil { + err = o.checkJobsStatus(ctx, jobs) + if err != nil { + log.Error(err.Error()) + } + } + case err = <-scch: + if err != nil { + ech <- err + } + } + } + }) + return ech, nil +} diff --git a/pkg/tools/benchmark/operator/service/option.go b/pkg/tools/benchmark/operator/service/option.go index 43b10ec581..da106afa7f 100644 --- a/pkg/tools/benchmark/operator/service/option.go +++ b/pkg/tools/benchmark/operator/service/option.go @@ -25,31 +25,44 @@ import ( ) // Option represents the functional option for scenario struct. -type Option func(sc *scenario) error +type Option func(o *operator) error var defaultOpts = []Option{ WithReconcileCheckDuration("10s"), + WithJobNamespace("default"), } // WithErrGroup sets the error group to scenario. func WithErrGroup(eg errgroup.Group) Option { - return func(sc *scenario) error { + return func(o *operator) error { if eg == nil { return errors.NewErrInvalidOption("client", eg) } - sc.eg = eg + o.eg = eg return nil } } // WithReconcileCheckDuration sets the reconcile check duration from input string. func WithReconcileCheckDuration(ts string) Option { - return func(sc *scenario) error { + return func(o *operator) error { t, err := time.ParseDuration(ts) if err != nil { return err } - sc.rcd = t + o.rcd = t + return nil + } +} + +// WithJobNamespace sets the namespace for running benchmark job. +func WithJobNamespace(ns string) Option { + return func(o *operator) error { + if len(ns) == 0 { + o.jobNamespace = "default" + } else { + o.jobNamespace = ns + } return nil } } diff --git a/pkg/tools/benchmark/operator/service/scenario.go b/pkg/tools/benchmark/operator/service/scenario.go deleted file mode 100644 index dcd15ceab0..0000000000 --- a/pkg/tools/benchmark/operator/service/scenario.go +++ /dev/null @@ -1,372 +0,0 @@ -// -// Copyright (C) 2019-2022 vdaas.org vald team -// -// 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 -// -// https://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 service manages the main logic of benchmark job. -package service - -import ( - "context" - "reflect" - "strconv" - "sync/atomic" - "time" - - "github.com/vdaas/vald/internal/errgroup" - "github.com/vdaas/vald/internal/errors" - "github.com/vdaas/vald/internal/k8s" - "github.com/vdaas/vald/internal/k8s/job" - v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" - benchjob "github.com/vdaas/vald/internal/k8s/vald/benchmark/job" - benchscenario "github.com/vdaas/vald/internal/k8s/vald/benchmark/scenario" - "github.com/vdaas/vald/internal/log" - corev1 "k8s.io/api/core/v1" - k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type Scenario interface { - PreStart(context.Context) error - Start(context.Context) (<-chan error, error) -} - -type scenario struct { - jobs atomic.Value - jobName string - jobNamespace string - jobTemplate string // row manifest template data of rebalance job. - jobObject *job.Job // object generated from template. - currentDeviationJobName atomic.Value - - scenarios atomic.Value - benchjobs atomic.Value - - rcd time.Duration // reconcile check duration - eg errgroup.Group - ctrl k8s.Controller -} - -// New creates the new scenario struct to handle vald benchmark job scenario. -// When the input options are invalid, the error will be returned. -func New(opts ...Option) (Scenario, error) { - sc := new(scenario) - for _, opt := range append(defaultOpts, opts...) { - if err := opt(sc); err != nil { - return nil, errors.ErrOptionFailed(err, reflect.ValueOf(opt)) - } - } - - err := sc.initCtrl() - if err != nil { - return nil, err - } - return sc, nil -} - -// initCtrl creates the controller for reconcile k8s objects. -func (sc *scenario) initCtrl() (err error) { - // watcher of vald benchmark scenario resource - bs, err := benchscenario.New( - benchscenario.WithControllerName("benchmark scenario resource"), - benchscenario.WithNamespaces(sc.jobNamespace), - benchscenario.WithOnErrorFunc(func(err error) { - log.Errorf("failed to reconcile:", err) - }), - benchscenario.WithOnReconcileFunc(sc.benchScenarioReconcile), - ) - if err != nil { - return - } - - // watcher of vald benchmark job resource - bj, err := benchjob.New( - benchjob.WithControllerName("benchmark job resource"), - benchjob.WithOnErrorFunc(func(err error) { - log.Errorf("failed to reconcile:", err) - }), - benchjob.WithNamespaces(sc.jobNamespace), - benchjob.WithOnErrorFunc(func(err error) { - log.Error(err) - }), - benchjob.WithOnReconcileFunc(sc.benchJobReconcile), - ) - if err != nil { - return - } - - // watcher of job resource - job, err := job.New( - job.WithControllerName("benchmark job"), - job.WithNamespaces(sc.jobNamespace), - job.WithOnErrorFunc(func(err error) { - log.Errorf("failed to reconcile:", err) - }), - job.WithOnReconcileFunc(sc.jobReconcile), - ) - if err != nil { - return - } - - // create reconcile controller which watches valdbenchmarkscenario resource, valdbenchmarkjob resource, and job resource. - sc.ctrl, err = k8s.New( - k8s.WithControllerName("vald benchmark operator"), - k8s.WithResourceController(bs), - k8s.WithResourceController(bj), - k8s.WithResourceController(job), - ) - return -} - -// jobReconcile gets k8s job list and watches theirs STATUS. -// Then, it processes according STATUS. -func (sc *scenario) jobReconcile(ctx context.Context, jobList map[string][]job.Job) { - // TODO: impl logic - // for k, v := range jobList { - // log.Warnf("key: %s, value: %v", k, v) - // } - return -} - -// benchmarkJobReconcile gets the vald benchmark job resource list and create Job for running benchmark job. -func (sc *scenario) benchJobReconcile(ctx context.Context, jobList map[string]v1.ValdBenchmarkJob) { - log.Debugf("[reconcile benchmark job resource] job list: %#v", jobList) - if len(jobList) == 0 { - sc.benchjobs.Store(make(map[string]*v1.ValdBenchmarkJob, 0)) - log.Info("[reconcile benchmark job resource] job resource not found") - return - } - var cbjl map[string]*v1.ValdBenchmarkJob - if ok := sc.benchjobs.Load(); ok == nil { - cbjl = make(map[string]*v1.ValdBenchmarkJob, 0) - } else { - cbjl = ok.(map[string]*v1.ValdBenchmarkJob) - } - for k, job := range jobList { - if oldJob := cbjl[k]; oldJob != nil { - if oldJob.GetGeneration() != job.GetGeneration() { - // TODO: delete old version job - cbjl[k] = &job - } - } else { - log.Info("create job: ", k) - err := sc.createJob(ctx, job) - if err != nil { - log.Errorf("[reconcile benchmark job resource] failed to create job: %s", err.Error()) - } - cbjl[k] = &job - } - } - sc.benchjobs.Store(cbjl) -} - -// benchScenarioReconcile gets the vald benchmark scenario list and create vald benchmark job resource according to it. -func (sc *scenario) benchScenarioReconcile(ctx context.Context, scenarioList map[string]v1.ValdBenchmarkScenario) { - log.Debugf("[reconcile benchmark scenario resource] scenario list: %#v", scenarioList) - if len(scenarioList) == 0 { - sc.scenarios.Store(make(map[string]*v1.ValdBenchmarkScenario, 0)) - sc.benchjobs.Store(make(map[string]*v1.ValdBenchmarkJob, 0)) - log.Info("[reconcile benchmark scenario resource]: scenario not found") - return - } - var cbsl map[string]*v1.ValdBenchmarkScenario - if ok := sc.scenarios.Load(); ok == nil { - cbsl = make(map[string]*v1.ValdBenchmarkScenario, len(scenarioList)) - } else { - cbsl = ok.(map[string]*v1.ValdBenchmarkScenario) - } - for k, scenario := range scenarioList { - if oldScenario := cbsl[k]; oldScenario == nil { - err := sc.createBenchmarkJob(ctx, scenario) - if err != nil { - log.Errorf("[reconcile scenario] failed to create job: %s", err.Error()) - } - cbsl[k] = &scenario - } else { - // TODO delete old jobresource and job - if oldScenario.GetGeneration() != scenario.GetGeneration() { - cbsl[k] = &scenario - } - } - } - sc.scenarios.Store(cbsl) -} - -// createBenchmarkJob creates the ValdBenchmarkJob crd for running job. -func (sc *scenario) createBenchmarkJob(ctx context.Context, scenario v1.ValdBenchmarkScenario) error { - ownerRef := []k8smeta.OwnerReference{ - { - APIVersion: scenario.APIVersion, - Kind: scenario.Kind, - Name: scenario.Name, - UID: scenario.UID, - }, - } - for _, job := range scenario.Spec.Jobs { - bj := new(v1.ValdBenchmarkJob) - // set metadata.name, metadata.namespace - bj.Name = scenario.GetName() + "-" + job.JobType + "-" + strconv.FormatInt(time.Now().UnixNano(), 10) - bj.Namespace = scenario.GetNamespace() - bj.SetOwnerReferences(ownerRef) - - // set specs - bj.Spec = *job - if bj.Spec.Target == nil { - bj.Spec.Target = scenario.Spec.Target - } - if bj.Spec.Dataset == nil { - bj.Spec.Dataset = scenario.Spec.Dataset - } - // create benchmark job resource - c := sc.ctrl.GetManager().GetClient() - if err := c.Create(ctx, bj); err != nil { - // TODO: create new custom error - return err - } - } - return nil -} - -// createJobTemplate creates the job template for crating k8s job resource. -// ns and name are required to set job environment value. -func createJobTemplate(ns, name string) job.Job { - j := new(job.Job) - backoffLimit := int32(0) - j.Spec.BackoffLimit = &backoffLimit - j.Spec.Template.Spec.ServiceAccountName = "vald-benchmark-operator" - j.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyNever - j.Spec.Template.Spec.Containers = []corev1.Container{ - { - Name: "vald-benchmark-job", - Image: "local-registry:5000/vdaas/vald-benchmark-job:latest", - ImagePullPolicy: corev1.PullAlways, - LivenessProbe: &corev1.Probe{ - InitialDelaySeconds: int32(60), - PeriodSeconds: int32(10), - TimeoutSeconds: int32(300), - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{ - "/go/bin/job", - "-v", - }, - }, - }, - }, - StartupProbe: &corev1.Probe{ - FailureThreshold: int32(30), - PeriodSeconds: int32(10), - TimeoutSeconds: int32(300), - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{ - "/go/bin/job", - "-v", - }, - }, - }, - }, - Ports: []corev1.ContainerPort{ - { - Name: "liveness", - Protocol: corev1.ProtocolTCP, - ContainerPort: int32(3000), - }, - { - Name: "readiness", - Protocol: corev1.ProtocolTCP, - ContainerPort: int32(3001), - }, - }, - Env: []corev1.EnvVar{ - { - Name: "CRD_NAMESPACE", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.namespace", - }, - }, - }, - { - Name: "CRD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.labels['job-name']", - }, - }, - }, - }, - }, - } - return *j -} - -// createJob creates benchmark job from benchmark job resource. -func (sc *scenario) createJob(ctx context.Context, bjr v1.ValdBenchmarkJob) (err error) { - bj := createJobTemplate(bjr.Namespace, bjr.Name) - bj.Name = bjr.Name - bj.Namespace = bjr.Namespace - bj.SetOwnerReferences(bjr.GetOwnerReferences()) - // create job - c := sc.ctrl.GetManager().GetClient() - if err = c.Create(ctx, &bj); err != nil { - // TODO: create new custom error - return err - } - if ok := sc.jobs.Load(); ok == nil { - sc.jobs.Store([]string{bj.Name}) - return - } else { - jobs := sc.jobs.Load().([]string) - jobs = append(jobs, bj.Name) - sc.jobs.Swap(jobs) - } - return -} - -func (sc *scenario) PreStart(ctx context.Context) error { - log.Infof("[benchmark scenario] start vald benchmark scenario") - return nil -} - -func (sc *scenario) Start(ctx context.Context) (<-chan error, error) { - scch, err := sc.ctrl.Start(ctx) - if err != nil { - return nil, err - } - ech := make(chan error, 2) - sc.eg.Go(func() error { - defer close(ech) - dt := time.NewTicker(sc.rcd) - defer dt.Stop() - for { - select { - case <-ctx.Done(): - return nil - case <-dt.C: - // TODO: Get Resource - _, ok := sc.scenarios.Load().(map[string]*v1.ValdBenchmarkScenario) - if !ok { - log.Info("benchmark scenario resource is empty") - continue - } - case err = <-scch: - if err != nil { - ech <- err - } - } - } - }) - - return ech, nil -} diff --git a/pkg/tools/benchmark/operator/usecase/benchmarkd.go b/pkg/tools/benchmark/operator/usecase/benchmarkd.go index 116dfd6cd7..6aa069b0c0 100644 --- a/pkg/tools/benchmark/operator/usecase/benchmarkd.go +++ b/pkg/tools/benchmark/operator/usecase/benchmarkd.go @@ -43,7 +43,7 @@ import ( type run struct { eg errgroup.Group cfg *config.Config - scenario service.Scenario + operator service.Operator h handler.Benchmark server starter.Server observability observability.Observability @@ -56,7 +56,7 @@ func New(cfg *config.Config) (r runner.Runner, err error) { log.Info("pkg/tools/benchmark/scenario/cmd success d") - sc, err := service.New( + operator, err := service.New( service.WithErrGroup(eg), ) if err != nil { @@ -125,7 +125,7 @@ func New(cfg *config.Config) (r runner.Runner, err error) { return &run{ eg: eg, cfg: cfg, - scenario: sc, + operator: operator, h: h, server: srv, observability: obs, @@ -138,8 +138,8 @@ func (r *run) PreStart(ctx context.Context) error { return err } } - if r.scenario != nil { - return r.scenario.PreStart(ctx) + if r.operator != nil { + return r.operator.PreStart(ctx) } return nil } @@ -153,7 +153,7 @@ func (r *run) Start(ctx context.Context) (<-chan error, error) { oech = r.observability.Start(ctx) } - dech, err = r.scenario.Start(ctx) + dech, err = r.operator.Start(ctx) if err != nil { ech <- err return err From 0121e7f58f532e6abb4817b18f894bdbc85b6844 Mon Sep 17 00:00:00 2001 From: Kiichiro YUKAWA Date: Wed, 26 Apr 2023 17:32:07 +0900 Subject: [PATCH 03/11] Impl benchmark jobs (#1977) * Implement base of continuous benchmark tool (#1776) * Create Continuous Bench Search Job tool (#1733) * :sparkles: create bench job search tools Signed-off-by: vankichi * :sparkles: add load hdf5 functions Signed-off-by: vankichi * :recycle: fix format Signed-off-by: vankichi * :recycle: fix docker and use hdf5 data Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: refactor benchmark job Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: fix proto Signed-off-by: vankichi * :green_heart: add benchmark job image build ci Signed-off-by: vankichi * :green_heart: invest Signed-off-by: vankichi * Revert ":green_heart: invest" This reverts commit f0f585ccf71b1c95a88559941557a27774096e69. * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply code review Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Hiroto Funakoshi * :sparkles: apply from feedback Signed-off-by: vankichi * Update internal/config/benchmark.go Co-authored-by: Yusuke Kato * :recycle: change directory path Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi * Add crds for continuous benchmark tools (#1789) * :sparkles: add crds for continuous benchmark operator Signed-off-by: vankichi * :sparkles: add benchmark operator/job scheme Signed-off-by: vankichi * :sparkles: rename package names and add doc.go Signed-off-by: vankichi * :sparkles: create runtime object Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply feedback Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato * Add Job reconciler & Change directory constitution of internal/k8s for benchmark (#1825) * :sparkles: :recycle: add Job reconciler & use scenario instead of operator Signed-off-by: vankichi * :recycle: fix format & rename file Signed-off-by: vankichi Signed-off-by: vankichi * Add benchmark operator framework (#1916) * :sparkles: impl benchmark reconciler Signed-off-by: vankichi * :sparkles: create benchmark operator framework Signed-off-by: vankichi * :recycle: remove unness changes Signed-off-by: vankichi Signed-off-by: vankichi * Format code with prettier and gofumpt * impl reconcile logic for create benchmark job (#1923) * :sparkles: impl reconcile logic for create benchmark job Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: fix Signed-off-by: vankichi * :recycle: refactor continuous benchmark's crds Signed-off-by: vankichi * :recycle: resolve error due to update conn bench crds for pkg/tools/benchmark/job Signed-off-by: vankichi * :recycle: refactor continuous benchmark job logic Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: update charts Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: rafactor con bench config and bug fix reconcile logic Signed-off-by: vankichi * :bug: Bugfix: fix typo and recall function logic Signed-off-by: vankichi * :recycle: refactor pkg benchmark job Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :sparkles: impl benchmark jobs Signed-off-by: vankichi * :recycle: apply feedback Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --- .../workflows/dockers-benchmark-job-image.yml | 2 +- apis/grpc/v1/benchmark/benchmark.pb.go | 2 +- .../grpc/v1/benchmark/benchmark_vtproto.pb.go | 2 +- apis/proto/v1/benchmark/benchmark.proto | 2 +- cmd/tools/benchmark/job/main.go | 2 +- cmd/tools/benchmark/job/sample.yaml | 2 +- cmd/tools/benchmark/operator/main.go | 2 +- go.mod | 3 + go.sum | 2 + internal/config/benchmark.go | 156 +++++++++++++--- internal/errors/benchmark.go | 5 + internal/k8s/client/client.go | 2 +- internal/k8s/client/option.go | 2 +- .../k8s/crd/benchmark/valdbenchmarkjob.yaml | 8 +- .../crd/benchmark/valdbenchmarkscenario.yaml | 8 +- internal/k8s/job/job.go | 9 +- internal/k8s/job/option.go | 2 +- internal/k8s/vald/benchmark/api/v1/info.go | 2 +- .../k8s/vald/benchmark/api/v1/job_types.go | 30 ++-- .../vald/benchmark/api/v1/scenario_types.go | 2 +- internal/k8s/vald/benchmark/job/doc.go | 2 +- internal/k8s/vald/benchmark/job/job.go | 4 +- .../k8s/vald/benchmark/job/job_template.go | 2 +- .../vald/benchmark/job/job_template_option.go | 2 +- internal/k8s/vald/benchmark/job/option.go | 2 +- internal/k8s/vald/benchmark/scenario/doc.go | 2 +- .../k8s/vald/benchmark/scenario/option.go | 2 +- .../k8s/vald/benchmark/scenario/scenario.go | 2 +- internal/test/data/hdf5/doc.go | 2 +- internal/test/data/hdf5/hdf5.go | 27 ++- internal/test/data/hdf5/option.go | 2 +- internal/timeutil/rate/rate.go | 61 +++++++ .../tools}/benchmark/job/clusterrole.yaml | 7 +- .../benchmark/job/clusterrolebinding.yaml | 8 +- .../tools}/benchmark/job/serviceaccount.yaml | 6 +- .../rbac => k8s/tools}/benchmark/job/svc.yaml | 8 +- .../benchmark/operator/clusterrole.yaml | 19 +- .../operator/clusterrolebinding.yaml | 10 +- k8s/tools/benchmark/operator/configmap.yaml | 166 ++++++++++++++++++ k8s/tools/benchmark/operator/deployment.yaml | 51 ++++++ .../benchmark/operator/serviceaccount.yaml | 6 +- pkg/tools/benchmark/job/config/config.go | 23 ++- pkg/tools/benchmark/job/config/config_test.go | 31 +--- pkg/tools/benchmark/job/config/doc.go | 2 +- pkg/tools/benchmark/job/handler/doc.go | 2 +- .../benchmark/job/handler/grpc/handler.go | 2 +- .../benchmark/job/handler/grpc/option.go | 2 +- .../benchmark/job/handler/rest/handler.go | 2 +- .../benchmark/job/handler/rest/option.go | 2 +- pkg/tools/benchmark/job/router/doc.go | 2 +- pkg/tools/benchmark/job/router/option.go | 2 +- pkg/tools/benchmark/job/router/router.go | 2 +- pkg/tools/benchmark/job/service/doc.go | 2 +- pkg/tools/benchmark/job/service/insert.go | 77 ++++++++ pkg/tools/benchmark/job/service/job.go | 150 +++++++++++++--- pkg/tools/benchmark/job/service/object.go | 119 +++++++++++++ pkg/tools/benchmark/job/service/option.go | 45 ++++- pkg/tools/benchmark/job/service/remove.go | 76 ++++++++ pkg/tools/benchmark/job/service/search.go | 116 ++++++------ pkg/tools/benchmark/job/service/update.go | 79 +++++++++ pkg/tools/benchmark/job/service/upsert.go | 80 +++++++++ pkg/tools/benchmark/job/usecase/benchmarkd.go | 6 +- pkg/tools/benchmark/operator/config/config.go | 2 +- pkg/tools/benchmark/operator/config/doc.go | 2 +- pkg/tools/benchmark/operator/handler/doc.go | 2 +- .../operator/handler/grpc/handler.go | 2 +- .../benchmark/operator/handler/grpc/option.go | 2 +- .../operator/handler/rest/handler.go | 2 +- .../benchmark/operator/handler/rest/option.go | 2 +- pkg/tools/benchmark/operator/router/doc.go | 2 +- pkg/tools/benchmark/operator/router/option.go | 2 +- pkg/tools/benchmark/operator/router/router.go | 2 +- pkg/tools/benchmark/operator/service/doc.go | 2 +- .../benchmark/operator/service/operator.go | 73 +++++--- .../benchmark/operator/service/option.go | 2 +- .../benchmark/operator/usecase/benchmarkd.go | 3 +- 76 files changed, 1294 insertions(+), 264 deletions(-) create mode 100644 internal/timeutil/rate/rate.go rename {internal/k8s/rbac => k8s/tools}/benchmark/job/clusterrole.yaml (91%) rename {internal/k8s/rbac => k8s/tools}/benchmark/job/clusterrolebinding.yaml (80%) rename {internal/k8s/rbac => k8s/tools}/benchmark/job/serviceaccount.yaml (81%) rename {internal/k8s/rbac => k8s/tools}/benchmark/job/svc.yaml (81%) rename {internal/k8s/rbac => k8s/tools}/benchmark/operator/clusterrole.yaml (86%) rename {internal/k8s/rbac => k8s/tools}/benchmark/operator/clusterrolebinding.yaml (76%) create mode 100644 k8s/tools/benchmark/operator/configmap.yaml create mode 100644 k8s/tools/benchmark/operator/deployment.yaml rename {internal/k8s/rbac => k8s/tools}/benchmark/operator/serviceaccount.yaml (80%) create mode 100644 pkg/tools/benchmark/job/service/insert.go create mode 100644 pkg/tools/benchmark/job/service/object.go create mode 100644 pkg/tools/benchmark/job/service/remove.go create mode 100644 pkg/tools/benchmark/job/service/update.go create mode 100644 pkg/tools/benchmark/job/service/upsert.go diff --git a/.github/workflows/dockers-benchmark-job-image.yml b/.github/workflows/dockers-benchmark-job-image.yml index b45c698aab..28a239f96f 100644 --- a/.github/workflows/dockers-benchmark-job-image.yml +++ b/.github/workflows/dockers-benchmark-job-image.yml @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2022 vdaas.org vald team +# Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/apis/grpc/v1/benchmark/benchmark.pb.go b/apis/grpc/v1/benchmark/benchmark.pb.go index 62c2735b80..69a3a42d39 100644 --- a/apis/grpc/v1/benchmark/benchmark.pb.go +++ b/apis/grpc/v1/benchmark/benchmark.pb.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/apis/grpc/v1/benchmark/benchmark_vtproto.pb.go b/apis/grpc/v1/benchmark/benchmark_vtproto.pb.go index 450737e354..d0c6147ab2 100644 --- a/apis/grpc/v1/benchmark/benchmark_vtproto.pb.go +++ b/apis/grpc/v1/benchmark/benchmark_vtproto.pb.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/apis/proto/v1/benchmark/benchmark.proto b/apis/proto/v1/benchmark/benchmark.proto index f798b27e05..cc1bbc9596 100644 --- a/apis/proto/v1/benchmark/benchmark.proto +++ b/apis/proto/v1/benchmark/benchmark.proto @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cmd/tools/benchmark/job/main.go b/cmd/tools/benchmark/job/main.go index b63331da0f..c7e31931de 100644 --- a/cmd/tools/benchmark/job/main.go +++ b/cmd/tools/benchmark/job/main.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cmd/tools/benchmark/job/sample.yaml b/cmd/tools/benchmark/job/sample.yaml index 76dd83f8ea..b9ee53ab51 100644 --- a/cmd/tools/benchmark/job/sample.yaml +++ b/cmd/tools/benchmark/job/sample.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2022 vdaas.org vald team +# Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmd/tools/benchmark/operator/main.go b/cmd/tools/benchmark/operator/main.go index 13660db983..a6799e2922 100644 --- a/cmd/tools/benchmark/operator/main.go +++ b/cmd/tools/benchmark/operator/main.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/go.mod b/go.mod index 8dee173bf2..d4c5284809 100755 --- a/go.mod +++ b/go.mod @@ -386,6 +386,7 @@ require ( go.opentelemetry.io/otel/trace v1.11.2 go.uber.org/automaxprocs v0.0.0-00010101000000-000000000000 go.uber.org/goleak v1.2.1 + go.uber.org/ratelimit v0.2.0 go.uber.org/zap v1.24.0 gocloud.dev v0.0.0-00010101000000-000000000000 golang.org/x/net v0.17.0 @@ -394,6 +395,7 @@ require ( golang.org/x/sys v0.13.0 golang.org/x/text v0.13.0 golang.org/x/tools v0.14.0 + golang.org/x/time v0.3.0 gonum.org/v1/hdf5 v0.0.0-00010101000000-000000000000 gonum.org/v1/plot v0.10.1 google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a @@ -418,6 +420,7 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect + github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/campoy/embedmd v1.0.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect diff --git a/go.sum b/go.sum index de5cead015..81b12244a5 100644 --- a/go.sum +++ b/go.sum @@ -170,6 +170,8 @@ github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyR github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= +github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= +github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/stroke v0.0.0-20221221101821-bd29b49d73f0/go.mod h1:ccdDYaY5+gO+cbnQdFxEXqfy0RkoV25H3jLXUDNM3wg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= diff --git a/internal/config/benchmark.go b/internal/config/benchmark.go index 83fb41b2dd..0361ddb294 100644 --- a/internal/config/benchmark.go +++ b/internal/config/benchmark.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,33 +17,32 @@ // Package config providers configuration type and load configuration logic package config -import "github.com/vdaas/vald/internal/k8s/client" - // BenchmarkJob represents the configuration for the internal benchmark search job. type BenchmarkJob struct { - Target *BenchmarkTarget `json:"target,omitempty" yaml:"target"` - Dataset *BenchmarkDataset `json:"dataset,omitempty" yaml:"dataset"` - Dimension int `json:"dimension,omitempty" yaml:"dimension"` - Replica int `json:"replica,omitempty" yaml:"replica"` - Repetition int `json:"repetition,omitempty" yaml:"repetition"` - JobType string `json:"job_type,omitempty" yaml:"job_type"` - InsertConfig *InsertConfig `json:"insert_config,omitempty" yaml:"insert_config"` - UpdateConfig *UpdateConfig `json:"update_config,omitempty" yaml:"update_config"` - UpsertConfig *UpsertConfig `json:"upsert_config,omitempty" yaml:"upsert_config"` - SearchConfig *SearchConfig `json:"search_config,omitempty" yaml:"search_config"` - RemoveConfig *RemoveConfig `json:"remove_config,omitempty" yaml:"remove_config"` - ClientConfig *GRPCClient `json:"client_config,omitempty" yaml:"client_config"` - Rules []*BenchmarkJobRule `json:"rules,omitempty" yaml:"rules"` - BeforeJobName string `json:"before_job_name,omitempty" yaml:"before_job_name"` + Target *BenchmarkTarget `json:"target,omitempty" yaml:"target"` + Dataset *BenchmarkDataset `json:"dataset,omitempty" yaml:"dataset"` + Dimension int `json:"dimension,omitempty" yaml:"dimension"` + Replica int `json:"replica,omitempty" yaml:"replica"` + Repetition int `json:"repetition,omitempty" yaml:"repetition"` + JobType string `json:"job_type,omitempty" yaml:"job_type"` + InsertConfig *InsertConfig `json:"insert_config,omitempty" yaml:"insert_config"` + UpdateConfig *UpdateConfig `json:"update_config,omitempty" yaml:"update_config"` + UpsertConfig *UpsertConfig `json:"upsert_config,omitempty" yaml:"upsert_config"` + SearchConfig *SearchConfig `json:"search_config,omitempty" yaml:"search_config"` + RemoveConfig *RemoveConfig `json:"remove_config,omitempty" yaml:"remove_config"` + ObjectConfig *ObjectConfig `json:"object_config,omitempty" yaml:"object_config"` + ClientConfig *GRPCClient `json:"client_config,omitempty" yaml:"client_config"` + Rules []*BenchmarkJobRule `json:"rules,omitempty" yaml:"rules"` + BeforeJobName string `json:"before_job_name,omitempty" yaml:"before_job_name"` BeforeJobNamespace string `json:"before_job_namespace,omitempty" yaml:"before_job_namespace"` - Client client.Client `json:"client,omitempty" yaml:"client"` + RPS int `json:"rps,omitempty" yaml:"rps"` } // BenchmarkScenario represents the configuration for the internal benchmark scenario. type BenchmarkScenario struct { - Target *BenchmarkTarget `json:"target" yaml:"target"` - Dataset *BenchmarkDataset `jon:"dataset" yaml:"dataset"` - Jobs []*BenchmarkJob `job:"jobs" yaml:jobs` + Target *BenchmarkTarget `json:"target,omitempty" yaml:"target"` + Dataset *BenchmarkDataset `json:"dataset,omitempty" yaml:"dataset"` + Jobs []*BenchmarkJob `json:"jobs,omitempty" yaml:"jobs"` } // BenchmarkTarget defines the desired state of BenchmarkTarget @@ -52,6 +51,11 @@ type BenchmarkTarget struct { Port int `json:"port,omitempty"` } +func (t *BenchmarkTarget) Bind() *BenchmarkTarget { + t.Host = GetActualValue(t.Host) + return t +} + // BenchmarkDataset defines the desired state of BenchmarkDateset type BenchmarkDataset struct { Name string `json:"name,omitempty"` @@ -60,6 +64,11 @@ type BenchmarkDataset struct { Range *BenchmarkDatasetRange `json:"range,omitempty"` } +func (d *BenchmarkDataset) Bind() *BenchmarkDataset { + d.Name = GetActualValue(d.Name) + return d +} + // BenchmarkDatasetRange defines the desired state of BenchmarkDatesetRange type BenchmarkDatasetRange struct { Start int `json:"start,omitempty"` @@ -72,22 +81,45 @@ type BenchmarkJobRule struct { Type string `json:"type,omitempty"` } +func (r *BenchmarkJobRule) Bind() *BenchmarkJobRule { + r.Name = GetActualValue(r.Name) + r.Type = GetActualValue(r.Type) + return r +} + // InsertConfig defines the desired state of insert config type InsertConfig struct { SkipStrictExistCheck bool `json:"skip_strict_exist_check,omitempty"` Timestamp string `json:"timestamp,omitempty"` } +func (cfg *InsertConfig) Bind() *InsertConfig { + cfg.Timestamp = GetActualValue(cfg.Timestamp) + return cfg +} + // UpdateConfig defines the desired state of update config type UpdateConfig struct { - SkipStrictExistCheck bool `json:"skip_strict_exist_check,omitempty"` - Timestamp string `json:"timestamp,omitempty"` + SkipStrictExistCheck bool `json:"skip_strict_exist_check,omitempty"` + Timestamp string `json:"timestamp,omitempty"` + DisableBalancedUpdate bool `json:"disable_balanced_update,omitempty"` +} + +func (cfg *UpdateConfig) Bind() *UpdateConfig { + cfg.Timestamp = GetActualValue(cfg.Timestamp) + return cfg } // UpsertConfig defines the desired state of upsert config type UpsertConfig struct { - SkipStrictExistCheck bool `json:"skip_strict_exist_check,omitempty"` - Timestamp string `json:"timestamp,omitempty"` + SkipStrictExistCheck bool `json:"skip_strict_exist_check,omitempty"` + Timestamp string `json:"timestamp,omitempty"` + DisableBalancedUpdate bool `json:"disable_balanced_update,omitempty"` +} + +func (cfg *UpsertConfig) Bind() *UpsertConfig { + cfg.Timestamp = GetActualValue(cfg.Timestamp) + return cfg } // SearchConfig defines the desired state of search config @@ -99,19 +131,93 @@ type SearchConfig struct { Timeout string `json:"timeout,omitempty"` } +func (cfg *SearchConfig) Bind() *SearchConfig { + cfg.Timeout = GetActualValue(cfg.Timeout) + return cfg +} + // RemoveConfig defines the desired state of remove config type RemoveConfig struct { SkipStrictExistCheck bool `json:"skip_strict_exist_check,omitempty"` Timestamp string `json:"timestamp,omitempty"` } +func (cfg *RemoveConfig) Bind() *RemoveConfig { + cfg.Timestamp = GetActualValue(cfg.Timestamp) + return cfg +} + +// ObjectConfig defines the desired state of object config +type ObjectConfig struct { + FilterConfig FilterConfig `json:"filter_config,omitempty" yaml:"filter_config"` +} + +func (cfg *ObjectConfig) Bind() *ObjectConfig { + cfg.FilterConfig = *cfg.FilterConfig.Bind() + return cfg +} + +// FilterTarget defines the desired state of filter target +type FilterTarget struct { + Host string `json:"host,omitempty" yaml:"host"` + Port int32 `json:"port,omitempty" yaml:"port"` +} + +func (cfg *FilterTarget) Bind() *FilterTarget { + cfg.Host = GetActualValue(cfg.Host) + return cfg +} + +// FilterConfig defines the desired state of filter config +type FilterConfig struct { + Targets []*FilterTarget `json:"target,omitempty" yaml:"target"` +} + +func (cfg *FilterConfig) Bind() *FilterConfig { + for i := 0; i < len(cfg.Targets); i++ { + cfg.Targets[i] = cfg.Targets[i].Bind() + } + return cfg +} + // Bind binds the actual data from the Job receiver fields. func (b *BenchmarkJob) Bind() *BenchmarkJob { b.JobType = GetActualValue(b.JobType) + b.BeforeJobName = GetActualValue(b.BeforeJobName) + b.BeforeJobNamespace = GetActualValue(b.BeforeJobNamespace) + if b.Target != nil { + b.Target = b.Target.Bind() + } + if b.Dataset != nil { + b.Dataset = b.Dataset.Bind() + } + if b.InsertConfig != nil { + b.InsertConfig = b.InsertConfig.Bind() + } + if b.UpdateConfig != nil { + b.UpdateConfig = b.UpdateConfig.Bind() + } + if b.UpsertConfig != nil { + b.UpsertConfig = b.UpsertConfig.Bind() + } + if b.SearchConfig != nil { + b.SearchConfig = b.SearchConfig.Bind() + } + if b.RemoveConfig != nil { + b.RemoveConfig = b.RemoveConfig.Bind() + } + if b.ObjectConfig != nil { + b.ObjectConfig = b.ObjectConfig.Bind() + } if b.ClientConfig != nil { b.ClientConfig = b.ClientConfig.Bind() } + if len(b.Rules) > 0 { + for i := 0; i < len(b.Rules); i++ { + b.Rules[i] = b.Rules[i].Bind() + } + } return b } diff --git a/internal/errors/benchmark.go b/internal/errors/benchmark.go index d4fa5cd3c4..aa5d5c4b87 100644 --- a/internal/errors/benchmark.go +++ b/internal/errors/benchmark.go @@ -29,4 +29,9 @@ var ( ErrFailedToCreateJob = func(err error, jn string) error { return Wrapf(err, "could not create job: %s ", jn) } + + // ErrMismatchBenchmarkAtomics represents a function to generate an error that mismatch each atomic.Pointer stored corresponding to benchmark tasks. + ErrMismatchBenchmarkAtomics = func(job, benchjob, benchscenario interface{}) error { + return Errorf("mismatch atomics: job=%v\tbenchjob=%v\tbenchscenario=%v", job, benchjob, benchscenario) + } ) diff --git a/internal/k8s/client/client.go b/internal/k8s/client/client.go index 8cd8f7f6f8..2b3647ebd1 100644 --- a/internal/k8s/client/client.go +++ b/internal/k8s/client/client.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/k8s/client/option.go b/internal/k8s/client/option.go index cae27f6775..6d2d9a624b 100644 --- a/internal/k8s/client/option.go +++ b/internal/k8s/client/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml b/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml index cdefc9dcaa..dbaf7547cc 100644 --- a/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml +++ b/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2022 vdaas.org vald team +# Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -129,6 +129,8 @@ spec: - type type: object type: array + rps: + type: integer insert_config: description: InsertConfig defines the desired state of insert config properties: @@ -168,6 +170,8 @@ spec: type: boolean timestamp: type: string + disable_balanced_update: + type: boolean type: object upsert_config: description: UpsertConfig defines the desired state of upsert config @@ -176,6 +180,8 @@ spec: type: boolean timestamp: type: string + disable_balanced_update: + type: boolean type: object client_config: description: ClientConfig represents the configurations for gRPC client. diff --git a/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml b/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml index 135dee3c30..32010539ef 100644 --- a/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml +++ b/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2022 vdaas.org vald team +# Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -167,6 +167,8 @@ spec: - type type: object type: array + rps: + type: integer insert_config: description: InsertConfig defines the desired state of insert config properties: @@ -206,6 +208,8 @@ spec: type: boolean timestamp: type: string + disable_balanced_update: + type: boolean type: object upsert_config: description: UpsertConfig defines the desired state of upsert config @@ -214,6 +218,8 @@ spec: type: boolean timestamp: type: string + disable_balanced_update: + type: boolean type: object client_config: description: ClientConfig represents the configurations for gRPC client. diff --git a/internal/k8s/job/job.go b/internal/k8s/job/job.go index 026baa69e2..aa07449d08 100644 --- a/internal/k8s/job/job.go +++ b/internal/k8s/job/job.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,6 +20,9 @@ import ( "sync" "time" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/k8s" + "github.com/vdaas/vald/internal/log" batchv1 "k8s.io/api/batch/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" "sigs.k8s.io/controller-runtime/pkg/builder" @@ -28,10 +31,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - - "github.com/vdaas/vald/internal/errors" - "github.com/vdaas/vald/internal/k8s" - "github.com/vdaas/vald/internal/log" ) // JobWatcher is a type alias for k8s resource controller. diff --git a/internal/k8s/job/option.go b/internal/k8s/job/option.go index 283f4b156c..7752092de4 100644 --- a/internal/k8s/job/option.go +++ b/internal/k8s/job/option.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/k8s/vald/benchmark/api/v1/info.go b/internal/k8s/vald/benchmark/api/v1/info.go index 1ad2f03a4a..5fd71cf8cc 100644 --- a/internal/k8s/vald/benchmark/api/v1/info.go +++ b/internal/k8s/vald/benchmark/api/v1/info.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/k8s/vald/benchmark/api/v1/job_types.go b/internal/k8s/vald/benchmark/api/v1/job_types.go index 40c71232d7..135d1e42e9 100644 --- a/internal/k8s/vald/benchmark/api/v1/job_types.go +++ b/internal/k8s/vald/benchmark/api/v1/job_types.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,19 +23,21 @@ import ( ) type BenchmarkJobSpec struct { - Target *BenchmarkTarget `json:"target,omitempty"` - Dataset *BenchmarkDataset `json:"dataset,omitempty"` - Dimension int `json:"dimension,omitempty"` - Replica int `json:"replica,omitempty"` - Repetition int `json:"repetition,omitempty"` - JobType string `json:"job_type,omitempty"` - InsertConfig *config.InsertConfig `json:"insert_config,omitempty"` - UpdateConfig *config.UpdateConfig `json:"update_config,omitempty"` - UpsertConfig *config.UpsertConfig `json:"upsert_config,omitempty"` - SearchConfig *config.SearchConfig `json:"search_config,omitempty"` - RemoveConfig *config.RemoveConfig `json:"remove_config,omitempty"` - ClientConfig *config.GRPCClient `json:"client_config,omitempty"` - Rules []*config.BenchmarkJobRule `json:"rules,omitempty"` + Target *BenchmarkTarget `json:"target,omitempty" yaml:"target"` + Dataset *BenchmarkDataset `json:"dataset,omitempty" yaml:"dataset"` + Dimension int `json:"dimension,omitempty" yaml:"dimension"` + Replica int `json:"replica,omitempty" yaml:"replica"` + Repetition int `json:"repetition,omitempty" yaml:"repetition"` + JobType string `json:"job_type,omitempty" yaml:"job_type"` + InsertConfig *config.InsertConfig `json:"insert_config,omitempty" yaml:"insert_config"` + UpdateConfig *config.UpdateConfig `json:"update_config,omitempty" yaml:"update_config"` + UpsertConfig *config.UpsertConfig `json:"upsert_config,omitempty" yaml:"upsert_config"` + SearchConfig *config.SearchConfig `json:"search_config,omitempty" yaml:"search_config"` + RemoveConfig *config.RemoveConfig `json:"remove_config,omitempty" yaml:"remove_config"` + ObjectConfig *config.ObjectConfig `json:"object_config,omitempty" yaml:"object_config"` + ClientConfig *config.GRPCClient `json:"client_config,omitempty" yaml:"client_config"` + Rules []*config.BenchmarkJobRule `json:"rules,omitempty" yaml:"rules"` + RPS int `json:"rps,omitempty" yaml:"rps"` } type BenchmarkJobStatus string diff --git a/internal/k8s/vald/benchmark/api/v1/scenario_types.go b/internal/k8s/vald/benchmark/api/v1/scenario_types.go index 8973a1e6b5..ddedb49371 100644 --- a/internal/k8s/vald/benchmark/api/v1/scenario_types.go +++ b/internal/k8s/vald/benchmark/api/v1/scenario_types.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/k8s/vald/benchmark/job/doc.go b/internal/k8s/vald/benchmark/job/doc.go index 678bce94ab..0567d4f35b 100644 --- a/internal/k8s/vald/benchmark/job/doc.go +++ b/internal/k8s/vald/benchmark/job/doc.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/k8s/vald/benchmark/job/job.go b/internal/k8s/vald/benchmark/job/job.go index 3cfed17ed6..87a7efe22a 100644 --- a/internal/k8s/vald/benchmark/job/job.go +++ b/internal/k8s/vald/benchmark/job/job.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ type BenchmarkJobWatcher k8s.ResourceController var ( // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "vald.benchmark.job", Version: "v1"} + GroupVersion = schema.GroupVersion{Group: "vald.vdaas.org", Version: "v1"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} // AddToScheme adds the types in this group-version to the given scheme. diff --git a/internal/k8s/vald/benchmark/job/job_template.go b/internal/k8s/vald/benchmark/job/job_template.go index 2830e23931..884addcde5 100644 --- a/internal/k8s/vald/benchmark/job/job_template.go +++ b/internal/k8s/vald/benchmark/job/job_template.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/k8s/vald/benchmark/job/job_template_option.go b/internal/k8s/vald/benchmark/job/job_template_option.go index 79b29a81b8..f5cc78f875 100644 --- a/internal/k8s/vald/benchmark/job/job_template_option.go +++ b/internal/k8s/vald/benchmark/job/job_template_option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/k8s/vald/benchmark/job/option.go b/internal/k8s/vald/benchmark/job/option.go index bf834ec347..aece8a869d 100644 --- a/internal/k8s/vald/benchmark/job/option.go +++ b/internal/k8s/vald/benchmark/job/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/k8s/vald/benchmark/scenario/doc.go b/internal/k8s/vald/benchmark/scenario/doc.go index c3800f6723..b93a52b69b 100644 --- a/internal/k8s/vald/benchmark/scenario/doc.go +++ b/internal/k8s/vald/benchmark/scenario/doc.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/k8s/vald/benchmark/scenario/option.go b/internal/k8s/vald/benchmark/scenario/option.go index fcbc83af47..adda6adfee 100644 --- a/internal/k8s/vald/benchmark/scenario/option.go +++ b/internal/k8s/vald/benchmark/scenario/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/k8s/vald/benchmark/scenario/scenario.go b/internal/k8s/vald/benchmark/scenario/scenario.go index c3e99b4987..cde92a808d 100644 --- a/internal/k8s/vald/benchmark/scenario/scenario.go +++ b/internal/k8s/vald/benchmark/scenario/scenario.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/test/data/hdf5/doc.go b/internal/test/data/hdf5/doc.go index f8650c005e..0db75d556e 100644 --- a/internal/test/data/hdf5/doc.go +++ b/internal/test/data/hdf5/doc.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/test/data/hdf5/hdf5.go b/internal/test/data/hdf5/hdf5.go index e83965ec8c..f178a52c2e 100644 --- a/internal/test/data/hdf5/hdf5.go +++ b/internal/test/data/hdf5/hdf5.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,11 +21,10 @@ import ( "os" "reflect" - "gonum.org/v1/hdf5" - "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/io" "github.com/vdaas/vald/internal/net/http/client" + "gonum.org/v1/hdf5" ) type Data interface { @@ -33,6 +32,7 @@ type Data interface { Read() error GetName() DatasetName GetPath() string + GetByGroupName(name string) [][]float32 GetTrain() [][]float32 GetTest() [][]float32 GetNeighbors() [][]int @@ -164,6 +164,27 @@ func (d *data) GetPath() string { return d.path } +// TODO: Apply generics +func (d *data) GetByGroupName(name string) [][]float32 { + switch name { + case "train": + return d.GetTrain() + case "test": + return d.GetTest() + case "neighbors": + l := d.GetNeighbors() + r := make([][]float32, 0) + for x := range l { + for y, z := range l[x] { + r[x][y] = float32(z) + } + } + return r + default: + return nil + } +} + func (d *data) GetTrain() [][]float32 { return d.train } diff --git a/internal/test/data/hdf5/option.go b/internal/test/data/hdf5/option.go index c18bcbd5e4..6fee9d0ae3 100644 --- a/internal/test/data/hdf5/option.go +++ b/internal/test/data/hdf5/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/timeutil/rate/rate.go b/internal/timeutil/rate/rate.go new file mode 100644 index 0000000000..045b999a9a --- /dev/null +++ b/internal/timeutil/rate/rate.go @@ -0,0 +1,61 @@ +// +// Copyright (C) 2019-2023 vdaas.org vald team +// +// 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 +// +// https://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 rate + +import ( + "context" + "runtime" + + "go.uber.org/ratelimit" + "golang.org/x/time/rate" +) + +type Limiter interface { + Wait(ctx context.Context) error +} + +type limiter struct { + isStd bool + uber ratelimit.Limiter + std *rate.Limiter +} + +func NewLimiter(cnt int) Limiter { + if runtime.GOMAXPROCS(0) >= 32 { + return &limiter{ + isStd: true, + std: rate.NewLimiter(rate.Limit(cnt), 1), + } + } + return &limiter{ + isStd: false, + uber: ratelimit.New(cnt, ratelimit.WithoutSlack), + } +} + +func (l *limiter) Wait(ctx context.Context) error { + select { + case <-ctx.Done(): + return ctx.Err() + default: + if l.isStd { + return l.std.Wait(ctx) + } + l.uber.Take() + } + return nil +} diff --git a/internal/k8s/rbac/benchmark/job/clusterrole.yaml b/k8s/tools/benchmark/job/clusterrole.yaml similarity index 91% rename from internal/k8s/rbac/benchmark/job/clusterrole.yaml rename to k8s/tools/benchmark/job/clusterrole.yaml index 4cdc233ec4..3fe77f9445 100644 --- a/internal/k8s/rbac/benchmark/job/clusterrole.yaml +++ b/k8s/tools/benchmark/job/clusterrole.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2022 vdaas.org vald team +# Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,9 +16,8 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null - name: benchmark-job-role # TODO: fix - namespace: default # TODO: fix + name: benchmark-job-role + namespace: default rules: - apiGroups: - apps diff --git a/internal/k8s/rbac/benchmark/job/clusterrolebinding.yaml b/k8s/tools/benchmark/job/clusterrolebinding.yaml similarity index 80% rename from internal/k8s/rbac/benchmark/job/clusterrolebinding.yaml rename to k8s/tools/benchmark/job/clusterrolebinding.yaml index f856e1b753..46b5b27c63 100644 --- a/internal/k8s/rbac/benchmark/job/clusterrolebinding.yaml +++ b/k8s/tools/benchmark/job/clusterrolebinding.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2022 vdaas.org vald team +# Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,8 +20,8 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: benchmark-job-role # TODO: fix + name: benchmark-job-role subjects: - kind: ServiceAccount - name: benchmark-job-service # TODO: fix - namespace: default # TODO: fix + name: benchmark-job-service + namespace: default diff --git a/internal/k8s/rbac/benchmark/job/serviceaccount.yaml b/k8s/tools/benchmark/job/serviceaccount.yaml similarity index 81% rename from internal/k8s/rbac/benchmark/job/serviceaccount.yaml rename to k8s/tools/benchmark/job/serviceaccount.yaml index 3bf6b046ca..419a7f8f37 100644 --- a/internal/k8s/rbac/benchmark/job/serviceaccount.yaml +++ b/k8s/tools/benchmark/job/serviceaccount.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2022 vdaas.org vald team +# Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,5 +16,5 @@ apiVersoin: v1 kind: ServiceAccount metadata: - name: benchmark-job-service # TODO: fix - namespace: default # TODO: fix + name: benchmark-job-service + namespace: default diff --git a/internal/k8s/rbac/benchmark/job/svc.yaml b/k8s/tools/benchmark/job/svc.yaml similarity index 81% rename from internal/k8s/rbac/benchmark/job/svc.yaml rename to k8s/tools/benchmark/job/svc.yaml index ce79a170e1..fd78cebd0e 100644 --- a/internal/k8s/rbac/benchmark/job/svc.yaml +++ b/k8s/tools/benchmark/job/svc.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2022 vdaas.org vald team +# Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,11 +20,11 @@ metadata: spec: ports: - name: prometheus - port: 4000 # TODO: fix - targetPort: 4000 # TODO: fix + port: 4000 # FIX ME + targetPort: 4000 # FIX ME protocol: TCP selector: - app.kubernetes.io/name: benchmark-job-svc # TODO: fix + app.kubernetes.io/name: benchmark-job-svc app.kubernetes.io/component: benchmark-job clusterIP: None type: ClusterIP diff --git a/internal/k8s/rbac/benchmark/operator/clusterrole.yaml b/k8s/tools/benchmark/operator/clusterrole.yaml similarity index 86% rename from internal/k8s/rbac/benchmark/operator/clusterrole.yaml rename to k8s/tools/benchmark/operator/clusterrole.yaml index acd2ffe14c..0b64888989 100644 --- a/internal/k8s/rbac/benchmark/operator/clusterrole.yaml +++ b/k8s/tools/benchmark/operator/clusterrole.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2022 vdaas.org vald team +# Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,9 +16,8 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null - name: benchmark-scenario-role # TODO: fix - namespace: default # TODO: fix + name: benchmark-operator-role + namespace: default rules: - apiGroups: - apps @@ -32,6 +31,18 @@ rules: - patch - update - watch + - apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - vald.vdaas.org resources: diff --git a/internal/k8s/rbac/benchmark/operator/clusterrolebinding.yaml b/k8s/tools/benchmark/operator/clusterrolebinding.yaml similarity index 76% rename from internal/k8s/rbac/benchmark/operator/clusterrolebinding.yaml rename to k8s/tools/benchmark/operator/clusterrolebinding.yaml index e87baf6242..d450654516 100644 --- a/internal/k8s/rbac/benchmark/operator/clusterrolebinding.yaml +++ b/k8s/tools/benchmark/operator/clusterrolebinding.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2022 vdaas.org vald team +# Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: benchmark-scenario-rolebinding + name: benchmark-operator-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: benchmark-scenario-role # TODO: fix + name: benchmark-operator-role subjects: - kind: ServiceAccount - name: vald-benchmark-operator # TODO: fix - namespace: default # TODO: fix + name: vald-benchmark-operator + namespace: default diff --git a/k8s/tools/benchmark/operator/configmap.yaml b/k8s/tools/benchmark/operator/configmap.yaml new file mode 100644 index 0000000000..7ac38b6e51 --- /dev/null +++ b/k8s/tools/benchmark/operator/configmap.yaml @@ -0,0 +1,166 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: v1 +kind: ConfigMap +metadata: + name: vald-benchmark-operator-config + labels: + app.kubernetes.io/name: vald + app.kubernetes.io/component: vald-benchmark-operator +data: + config.yaml: | + --- + version: v0.0.0 + time_zone: JST + logging: + format: raw + level: debug + logger: glg + server_config: + servers: + - name: grpc + host: 0.0.0.0 + port: 8081 + grpc: + bidirectional_stream_concurrency: 20 + connection_timeout: "" + header_table_size: 0 + initial_conn_window_size: 0 + initial_window_size: 0 + interceptors: [] + keepalive: + max_conn_age: "" + max_conn_age_grace: "" + max_conn_idle: "" + time: "" + timeout: "" + max_header_list_size: 0 + max_receive_message_size: 0 + max_send_message_size: 0 + read_buffer_size: 0 + write_buffer_size: 0 + mode: GRPC + probe_wait_time: 3s + restart: true + health_check_servers: + - name: liveness + host: 0.0.0.0 + port: 3000 + http: + handler_timeout: "" + idle_timeout: "" + read_header_timeout: "" + read_timeout: "" + shutdown_duration: 5s + write_timeout: "" + mode: "" + probe_wait_time: 3s + - name: readiness + host: 0.0.0.0 + port: 3001 + http: + handler_timeout: "" + idle_timeout: "" + read_header_timeout: "" + read_timeout: "" + shutdown_duration: 0s + write_timeout: "" + mode: "" + probe_wait_time: 3s + metrics_servers: + startup_strategy: + - liveness + - grpc + - readiness + full_shutdown_duration: 600s + tls: + ca: /path/to/ca + cert: /path/to/cert + enabled: false + key: /path/to/key + observability: + enabled: false + collector: + duration: 5s + metrics: + enable_cgo: true + enable_goroutine: true + enable_memory: true + enable_version_info: true + version_info_labels: + - vald_version + - server_name + - git_commit + - build_time + - go_version + - go_os + - go_arch + - ngt_version + trace: + enabled: false + sampling_rate: 1 + prometheus: + enabled: false + endpoint: /metrics + namespace: vald + jaeger: + enabled: false + collector_endpoint: "" + agent_endpoint: "jaeger-agent.default.svc.cluster.local:6831" + username: "" + password: "" + service_name: "vald-benchmark-job" + buffer_max_count: 10 + stackdriver: + project_id: "" + client: + api_key: "" + audiences: [] + authentication_enabled: true + credentials_file: "" + credentials_json: "" + endpoint: "" + quota_project: "" + request_reason: "" + scopes: [] + telemetry_enabled: true + user_agent: "" + exporter: + bundle_count_threshold: 0 + bundle_delay_threshold: "0" + location: "" + metric_prefix: vald.vdaas.org + monitoring_enabled: false + number_of_workers: 1 + reporting_interval: 1m + skip_cmd: false + timeout: 5s + trace_spans_buffer_max_bytes: 0 + tracing_enabled: false + profiler: + enabled: false + service: "vald-benchmark-job" + service_version: "" + debug_logging: false + mutex_profiling: true + cpu_profiling: true + alloc_profiling: true + heap_profiling: true + goroutine_profiling: true + alloc_force_gc: false + api_addr: "" + instance: "" + zone: "" diff --git a/k8s/tools/benchmark/operator/deployment.yaml b/k8s/tools/benchmark/operator/deployment.yaml new file mode 100644 index 0000000000..a5a5bd241b --- /dev/null +++ b/k8s/tools/benchmark/operator/deployment.yaml @@ -0,0 +1,51 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vald-benchmark-operator + labels: + app: vald-benchmark-operator +spec: + replicas: 1 + selector: + matchLabels: + app: vald-benchmark-operator + template: + metadata: + labels: + app: vald-benchmark-operator + spec: + containers: + - name: vald-benchmark-operator + image: vdaas/vald-benchmark-operator:latest + imagePullPolicy: Always + volumeMounts: + - name: vald-benchmark-operator-config + mountPath: /etc/server/ + ports: + - name: liveness + protocol: TCP + containerPort: 3000 + - name: readiness + protocol: TCP + containerPort: 3001 + serviceAccountName: vald-benchmark-operator + volumes: + - name: vald-benchmark-operator-config + configMap: + defaultMode: 420 + name: vald-benchmark-operator-config diff --git a/internal/k8s/rbac/benchmark/operator/serviceaccount.yaml b/k8s/tools/benchmark/operator/serviceaccount.yaml similarity index 80% rename from internal/k8s/rbac/benchmark/operator/serviceaccount.yaml rename to k8s/tools/benchmark/operator/serviceaccount.yaml index 612c1bf84a..617a413a16 100644 --- a/internal/k8s/rbac/benchmark/operator/serviceaccount.yaml +++ b/k8s/tools/benchmark/operator/serviceaccount.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2019-2022 vdaas.org vald team +# Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,5 +16,5 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: vald-benchmark-operator # TODO: fix - namespace: default # TODO: fix + name: vald-benchmark-operator + namespace: default diff --git a/pkg/tools/benchmark/job/config/config.go b/pkg/tools/benchmark/job/config/config.go index 34aa8e34fe..ad1cdb839a 100644 --- a/pkg/tools/benchmark/job/config/config.go +++ b/pkg/tools/benchmark/job/config/config.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -43,11 +43,16 @@ type Config struct { // Job represents benchmark job configurations Job *config.BenchmarkJob `json:"job" yaml:"job"` + + // K8sClient represents kubernetes clients + K8sClient client.Client } var ( - NAMESPACE = os.Getenv("CRD_NAMESPACE") - NAME = os.Getenv("CRD_NAME") + NAMESPACE = os.Getenv("CRD_NAMESPACE") + NAME = os.Getenv("CRD_NAME") + JOBNAME_ANNOTATION = "before-job-name" + JOBNAMESPACE_ANNOTATION = "before-job-namespace" ) // NewConfig represents the set config from the given setting file path. @@ -85,15 +90,15 @@ func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { // Get config from applied ValdBenchmarkJob custom resource var jobResource v1.ValdBenchmarkJob - if cfg.Job.Client == nil { + if cfg.K8sClient == nil { c, err := client.New(client.WithSchemeBuilder(*v1.SchemeBuilder)) if err != nil { log.Error(err.Error()) return nil, err } - cfg.Job.Client = c + cfg.K8sClient = c } - err = cfg.Job.Client.Get(ctx, NAME, NAMESPACE, &jobResource) + err = cfg.K8sClient.Get(ctx, NAME, NAMESPACE, &jobResource) if err != nil { log.Warn(err.Error()) } else { @@ -108,10 +113,12 @@ func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { cfg.Job.UpsertConfig = jobResource.Spec.UpsertConfig cfg.Job.SearchConfig = jobResource.Spec.SearchConfig cfg.Job.RemoveConfig = jobResource.Spec.RemoveConfig + cfg.Job.ObjectConfig = jobResource.Spec.ObjectConfig cfg.Job.ClientConfig = jobResource.Spec.ClientConfig + cfg.Job.RPS = jobResource.Spec.RPS if annotations := jobResource.GetAnnotations(); annotations != nil { - cfg.Job.BeforeJobName = annotations["before-job-name"] - cfg.Job.BeforeJobNamespace = annotations["before-job-namespace"] + cfg.Job.BeforeJobName = annotations[JOBNAME_ANNOTATION] + cfg.Job.BeforeJobNamespace = annotations[JOBNAMESPACE_ANNOTATION] } } diff --git a/pkg/tools/benchmark/job/config/config_test.go b/pkg/tools/benchmark/job/config/config_test.go index dc35aae48b..57dcee5efd 100644 --- a/pkg/tools/benchmark/job/config/config_test.go +++ b/pkg/tools/benchmark/job/config/config_test.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,12 +20,10 @@ package config import ( "context" "io/fs" - "os" "testing" "github.com/vdaas/vald/internal/config" "github.com/vdaas/vald/internal/errors" - "github.com/vdaas/vald/internal/file" "github.com/vdaas/vald/internal/test/comparator" "github.com/vdaas/vald/internal/test/goleak" ) @@ -59,26 +57,12 @@ func TestNewConfig(t *testing.T) { } tests := []test{ func() test { - name := "/home/vankichi/Documents/vald-read-test.yaml" - path := name + var path string return test{ name: "return error when can't read file", args: args{ path: path, }, - beforeFunc: func(t *testing.T, a args) { - t.Helper() - f, err := file.Open(a.path, os.O_CREATE, fs.ModeIrregular) - if err != nil { - if errors.Is(err, fs.ErrPermission) { - return - } - t.Error(err) - } - if err := f.Close(); err != nil { - t.Error(err) - } - }, checkFunc: func(w want, gotCfg *Config, err error) error { if errors.Is(err, fs.ErrPermission) { return nil @@ -86,17 +70,14 @@ func TestNewConfig(t *testing.T) { if !errors.Is(err, w.err) { return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) } - return nil - }, - afterFunc: func(t *testing.T, a args) { - t.Helper() - if err := os.Remove(a.path); err != nil { - t.Fatal(err) + if gotCfg != nil { + return errors.Errorf("got cfg: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotCfg, nil) } + return nil }, want: want{ wantCfg: nil, - err: errors.ErrUnsupportedConfigFileType(".yaml"), + err: errors.ErrPathNotSpecified, }, } }(), diff --git a/pkg/tools/benchmark/job/config/doc.go b/pkg/tools/benchmark/job/config/doc.go index 57ef74c464..04fa0ebe9b 100644 --- a/pkg/tools/benchmark/job/config/doc.go +++ b/pkg/tools/benchmark/job/config/doc.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/job/handler/doc.go b/pkg/tools/benchmark/job/handler/doc.go index f1014141ea..825bcc7f61 100644 --- a/pkg/tools/benchmark/job/handler/doc.go +++ b/pkg/tools/benchmark/job/handler/doc.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/job/handler/grpc/handler.go b/pkg/tools/benchmark/job/handler/grpc/handler.go index 85be642c7d..7febc72d11 100644 --- a/pkg/tools/benchmark/job/handler/grpc/handler.go +++ b/pkg/tools/benchmark/job/handler/grpc/handler.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/job/handler/grpc/option.go b/pkg/tools/benchmark/job/handler/grpc/option.go index 4319ef3a74..d5d9c32d1e 100644 --- a/pkg/tools/benchmark/job/handler/grpc/option.go +++ b/pkg/tools/benchmark/job/handler/grpc/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/job/handler/rest/handler.go b/pkg/tools/benchmark/job/handler/rest/handler.go index f752dbbbbf..1b9494c3d5 100644 --- a/pkg/tools/benchmark/job/handler/rest/handler.go +++ b/pkg/tools/benchmark/job/handler/rest/handler.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/job/handler/rest/option.go b/pkg/tools/benchmark/job/handler/rest/option.go index ca3aee436d..054b120b94 100644 --- a/pkg/tools/benchmark/job/handler/rest/option.go +++ b/pkg/tools/benchmark/job/handler/rest/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/job/router/doc.go b/pkg/tools/benchmark/job/router/doc.go index 053c9aeb49..ffa1ce388f 100644 --- a/pkg/tools/benchmark/job/router/doc.go +++ b/pkg/tools/benchmark/job/router/doc.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/job/router/option.go b/pkg/tools/benchmark/job/router/option.go index 063f5ce6f2..7053ee57ea 100644 --- a/pkg/tools/benchmark/job/router/option.go +++ b/pkg/tools/benchmark/job/router/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/job/router/router.go b/pkg/tools/benchmark/job/router/router.go index e032578e97..6b45c72184 100644 --- a/pkg/tools/benchmark/job/router/router.go +++ b/pkg/tools/benchmark/job/router/router.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/job/service/doc.go b/pkg/tools/benchmark/job/service/doc.go index eb86a28c5b..4d4cfe1548 100644 --- a/pkg/tools/benchmark/job/service/doc.go +++ b/pkg/tools/benchmark/job/service/doc.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/job/service/insert.go b/pkg/tools/benchmark/job/service/insert.go new file mode 100644 index 0000000000..946710df4b --- /dev/null +++ b/pkg/tools/benchmark/job/service/insert.go @@ -0,0 +1,77 @@ +// +// Copyright (C) 2019-2023 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service + +import ( + "context" + "strconv" + + "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/net/grpc/status" +) + +func (j *job) insert(ctx context.Context, ech chan error) error { + log.Info("[benchmark job] Start benchmarking insert") + // create data + vecs := j.genVec(j.dataset) + cfg := &payload.Insert_Config{ + SkipStrictExistCheck: j.insertConfig.SkipStrictExistCheck, + } + if j.timestamp > int64(0) { + cfg.Timestamp = j.timestamp + } + for i := 0; i < len(vecs); i++ { + log.Infof("[benchmark job] Start insert: iter = %d", i) + err := j.limiter.Wait(ctx) + if err != nil { + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + ech <- err + } + res, err := j.client.Insert(ctx, &payload.Insert_Request{ + Vector: &payload.Object_Vector{ + Id: strconv.Itoa(i), + Vector: vecs[i], + }, + Config: cfg, + }) + if err != nil { + select { + case <-ctx.Done(): + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + select { + case <-ctx.Done(): + return errors.Join(err, context.Canceled) + case ech <- errors.Join(err, ctx.Err()): + } + default: + st, _ := status.FromError(err) + log.Warnf("[benchmark job] insert error is detected: code = %d, msg = %s", st.Code(), err.Error()) + } + } + // TODO: send metrics to the Prometeus + log.Infof("[benchmark job] Finish insert: iter= %d \n%v\n", i, res) + } + log.Info("[benchmark job] Finish benchmarking insert") + return nil +} diff --git a/pkg/tools/benchmark/job/service/job.go b/pkg/tools/benchmark/job/service/job.go index 69c0d43f0e..2736ad898c 100644 --- a/pkg/tools/benchmark/job/service/job.go +++ b/pkg/tools/benchmark/job/service/job.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,8 +19,10 @@ package service import ( "context" + "math" "os" "reflect" + "strconv" "syscall" "time" @@ -34,6 +36,7 @@ import ( "github.com/vdaas/vald/internal/log" "github.com/vdaas/vald/internal/safety" "github.com/vdaas/vald/internal/test/data/hdf5" + "github.com/vdaas/vald/internal/timeutil/rate" ) type Job interface { @@ -46,15 +49,33 @@ type jobType int const ( USERDEFINED jobType = iota + INSERT SEARCH + UPDATE + UPSERT + REMOVE + GETOBJECT + EXISTS ) func (jt jobType) String() string { switch jt { case USERDEFINED: return "userdefined" + case INSERT: + return "insert" case SEARCH: return "search" + case UPDATE: + return "update" + case UPSERT: + return "upsert" + case REMOVE: + return "remove" + case GETOBJECT: + return "getobject" + case EXISTS: + return "exists" } return "" } @@ -70,12 +91,17 @@ type job struct { upsertConfig *config.UpsertConfig searchConfig *config.SearchConfig removeConfig *config.RemoveConfig + objectConfig *config.ObjectConfig client vald.Client hdf5 hdf5.Data beforeJobName string beforeJobNamespace string k8sClient client.Client beforeJobDur time.Duration + limiter rate.Limiter + rps int + timeout time.Duration + timestamp int64 } func New(opts ...Option) (Job, error) { @@ -91,16 +117,90 @@ func New(opts ...Option) (Job, error) { opt := WithJobFunc(j.jobFunc) err := opt(j) return nil, errors.ErrOptionFailed(err, reflect.ValueOf(opt)) + case INSERT: + j.jobFunc = j.insert + if j.insertConfig == nil { + return nil, errors.NewErrInvalidOption("insert config", j.insertConfig) + } + ts, err := strconv.Atoi(j.insertConfig.Timestamp) + if err != nil { + log.Warn("[benchmark job]: ", errors.NewErrInvalidOption("insert config timestamp", j.insert, err).Error()) + } else { + j.timestamp = int64(ts) + } case SEARCH: j.jobFunc = j.search + if j.searchConfig == nil { + return nil, errors.NewErrInvalidOption("search config", j.searchConfig) + } + to, err := time.ParseDuration(j.searchConfig.Timeout) + if err != nil { + log.Warn("[benchmark job]: ", errors.NewErrInvalidOption("search config timeout", j.searchConfig.Timeout, err).Error()) + } else { + j.timeout = to + } + case UPDATE: + j.jobFunc = j.update + if j.updateConfig == nil { + return nil, errors.NewErrInvalidOption("update config", j.updateConfig) + } + ts, err := strconv.Atoi(j.updateConfig.Timestamp) + if err != nil { + log.Warn("[benchmark job]: ", errors.NewErrInvalidOption("update config timestamp", j.updateConfig.Timestamp, err).Error()) + } else { + j.timestamp = int64(ts) + } + case UPSERT: + j.jobFunc = j.upsert + if j.upsertConfig == nil { + return nil, errors.NewErrInvalidOption("upsert config", j.insertConfig) + } + ts, err := strconv.Atoi(j.upsertConfig.Timestamp) + if err != nil { + log.Warn("[benchmark job]: ", errors.NewErrInvalidOption("upsert config timestamp", j.upsertConfig.Timestamp, err).Error()) + } else { + j.timestamp = int64(ts) + } + case REMOVE: + j.jobFunc = j.remove + if j.removeConfig == nil { + return nil, errors.NewErrInvalidOption("insert config", j.insertConfig) + } + ts, err := strconv.Atoi(j.removeConfig.Timestamp) + if err != nil { + log.Warn("[benchmark job]: ", errors.NewErrInvalidOption("remove config timestamp", j.removeConfig.Timestamp, err).Error()) + } else { + j.timestamp = int64(ts) + } + case GETOBJECT: + j.jobFunc = j.getObject + if j.objectConfig == nil { + log.Warnf("[benchmark job] No get object config is set: %v", j.objectConfig) + } + case EXISTS: + j.jobFunc = j.exists } } else if j.jobType != USERDEFINED { log.Warnf("[benchmark job] userdefined jobFunc is set but jobType is set %s", j.jobType.String()) } + if j.rps > 0 { + j.limiter = rate.NewLimiter(j.rps) + } return j, nil } func (j *job) PreStart(ctx context.Context) error { + log.Infof("[benchmark job] start download dataset of %s", j.hdf5.GetName().String()) + if err := j.hdf5.Download(); err != nil { + return err + } + log.Infof("[benchmark job] success download dataset of %s", j.hdf5.GetName().String()) + log.Infof("[benchmark job] start load dataset of %s", j.hdf5.GetName().String()) + if err := j.hdf5.Read(); err != nil { + return err + } + log.Infof("[benchmark job] success load dataset of %s", j.hdf5.GetName().String()) + // Wait for beforeJob completed if exists if len(j.beforeJobName) != 0 { var jobResource v1.ValdBenchmarkJob log.Info("[benchmark job] check before benchjob is completed or not...") @@ -128,17 +228,6 @@ func (j *job) PreStart(ctx context.Context) error { return err } } - - log.Infof("[benchmark job] start download dataset of %s", j.hdf5.GetName().String()) - if err := j.hdf5.Download(); err != nil { - return err - } - log.Infof("[benchmark job] success download dataset of %s", j.hdf5.GetName().String()) - log.Infof("[benchmark job] start load dataset of %s", j.hdf5.GetName().String()) - if err := j.hdf5.Read(); err != nil { - return err - } - log.Infof("[benchmark job] success load dataset of %s", j.hdf5.GetName().String()) return nil } @@ -169,7 +258,7 @@ func (j *job) Start(ctx context.Context) (<-chan error, error) { if err != nil { select { case <-ctx.Done(): - ech <- errors.Wrap(err, ctx.Err().Error()) + ech <- errors.Join(err, ctx.Err()) case ech <- err: } } @@ -192,33 +281,42 @@ func (j *job) Stop(ctx context.Context) (err error) { return } -func calcRecall(linearRes, searchRes []*payload.Object_Distance) (recall float64) { - if len(linearRes) == 0 || len(searchRes) == 0 { +func calcRecall(linearRes, searchRes *payload.Search_Response) (recall float64) { + if linearRes == nil || searchRes == nil { + return + } + lres := linearRes.Results + sres := searchRes.Results + if len(lres) == 0 || len(sres) == 0 { return } linearIds := map[string]struct{}{} - for _, v := range linearRes { + for _, v := range lres { linearIds[v.Id] = struct{}{} } - for _, v := range searchRes { + for _, v := range sres { if _, ok := linearIds[v.Id]; ok { recall++ } } - return recall / float64(len(linearRes)) + return recall / float64(len(lres)) } -func genVec(data [][]float32, cfg *config.BenchmarkDataset) [][]float32 { +func (j *job) genVec(cfg *config.BenchmarkDataset) [][]float32 { start := cfg.Range.Start end := cfg.Range.End - if (end - start) < cfg.Indexes { - end = cfg.Indexes + // If (Range.End - Range.Start) is smaller than Indexes, Indexes are prioritized based on Range.Start. + if (end - start + 1) < cfg.Indexes { + end = cfg.Range.Start + cfg.Indexes } - num := end - start + 1 - if len(data) < num { - num = len(data) - end = start + num + 1 + data := j.hdf5.GetByGroupName(cfg.Group) + if n := math.Ceil(float64(end) / float64(len(data))); n > 1 { + var def [][]float32 + for i := 0; i < int(n-1); i++ { + def = append(def, data...) + } + data = append(data, def...) } - vectors := data[start : end+1] + vectors := data[start-1 : end] return vectors } diff --git a/pkg/tools/benchmark/job/service/object.go b/pkg/tools/benchmark/job/service/object.go new file mode 100644 index 0000000000..c36ebd9fdb --- /dev/null +++ b/pkg/tools/benchmark/job/service/object.go @@ -0,0 +1,119 @@ +// +// Copyright (C) 2019-2023 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service + +import ( + "context" + "strconv" + + "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/net/grpc/status" +) + +func (j *job) exists(ctx context.Context, ech chan error) error { + log.Info("[benchmark job] Start benchmarking exists") + for i := 0; i < j.dataset.Indexes; i++ { + err := j.limiter.Wait(ctx) + if err != nil { + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + ech <- err + } + res, err := j.client.Exists(ctx, &payload.Object_ID{ + Id: strconv.Itoa(i), + }) + if err != nil { + select { + case <-ctx.Done(): + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + select { + case <-ctx.Done(): + return errors.Join(err, context.Canceled) + case ech <- errors.Join(err, ctx.Err()): + } + default: + st, _ := status.FromError(err) + log.Warnf("[benchmark job] exists error is detected: code = %d, msg = %s", st.Code(), err.Error()) + } + } + if res != nil { + log.Infof("[benchmark exists job] iter=%d, Id=%s", i, res.GetId()) + } + } + log.Info("[benchmark job] Finish benchmarking exists") + return nil +} + +func (j *job) getObject(ctx context.Context, ech chan error) error { + log.Info("[benchmark job] Start benchmarking getObject") + // create data + vecs := j.genVec(j.dataset) + for i := 0; i < len(vecs); i++ { + log.Infof("[benchmark job] Start getObject: iter = %d", i) + err := j.limiter.Wait(ctx) + if err != nil { + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + ech <- err + } + ft := []*payload.Filter_Target{} + if j.objectConfig != nil { + for i, target := range j.objectConfig.FilterConfig.Targets { + ft[i] = &payload.Filter_Target{ + Host: target.Host, + Port: uint32(target.Port), + } + } + } + res, err := j.client.GetObject(ctx, &payload.Object_VectorRequest{ + Id: &payload.Object_ID{ + Id: strconv.Itoa(i), + }, + Filters: &payload.Filter_Config{ + Targets: ft, + }, + }) + if res != nil { + log.Infof("[benchmark get object job] iter=%d, Id=%s, Vec=%v", i, res.GetId(), res.GetVector()) + } + if err != nil { + select { + case <-ctx.Done(): + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + select { + case <-ctx.Done(): + return errors.Join(err, context.Canceled) + case ech <- errors.Join(err, ctx.Err()): + } + default: + st, _ := status.FromError(err) + log.Warnf("[benchmark job] get object error is detected: code = %d, msg = %s", st.Code(), err.Error()) + } + } + } + log.Info("[benchmark job] Finish benchmarking getObject") + return nil +} diff --git a/pkg/tools/benchmark/job/service/option.go b/pkg/tools/benchmark/job/service/option.go index 998f9f8700..772e638a6f 100644 --- a/pkg/tools/benchmark/job/service/option.go +++ b/pkg/tools/benchmark/job/service/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ var defaultOpts = []Option{ // TODO: set default config for client WithDimension(748), WithBeforeJobDuration("30s"), + WithRPS(100), } // WithDimension sets the vector's dimension for running benchmark job with dataset. @@ -97,6 +98,16 @@ func WithRemoveConfig(c *config.RemoveConfig) Option { } } +// WithObjectConfig sets the get object API config for running get object request job. +func WithObjectConfig(c *config.ObjectConfig) Option { + return func(j *job) error { + if c != nil { + j.objectConfig = c + } + return nil + } +} + // WithValdClient sets the Vald client for sending request to the target Vald cluster. func WithValdClient(c vald.Client) Option { return func(j *job) error { @@ -147,8 +158,20 @@ func WithJobTypeByString(t string) Option { switch t { case "userdefined": jt = USERDEFINED + case "insert": + jt = INSERT case "search": jt = SEARCH + case "update": + jt = UPDATE + case "upsert": + jt = UPSERT + case "remove": + jt = REMOVE + case "getobject": + jt = GETOBJECT + case "exists": + jt = EXISTS } return WithJobType(jt) } @@ -156,14 +179,10 @@ func WithJobTypeByString(t string) Option { // WithJobType sets the jobType for running benchmark job. func WithJobType(jt jobType) Option { return func(j *job) error { - switch jt { - case USERDEFINED: - j.jobType = jt - case SEARCH: - j.jobType = jt - default: - return errors.NewErrInvalidOption("jobType", jt) + if len(jt.String()) == 0 { + return errors.NewErrInvalidOption("jobType", jt.String()) } + j.jobType = jt return nil } } @@ -223,3 +242,13 @@ func WithK8sClient(cli client.Client) Option { return nil } } + +// WithRPS sets the rpc for sending request per seconds to the target Vald cluster. +func WithRPS(rps int) Option { + return func(j *job) error { + if rps > 0 { + j.rps = rps + } + return nil + } +} diff --git a/pkg/tools/benchmark/job/service/remove.go b/pkg/tools/benchmark/job/service/remove.go new file mode 100644 index 0000000000..7e6db88e12 --- /dev/null +++ b/pkg/tools/benchmark/job/service/remove.go @@ -0,0 +1,76 @@ +// +// Copyright (C) 2019-2023 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service + +import ( + "context" + "strconv" + + "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/net/grpc/status" +) + +func (j *job) remove(ctx context.Context, ech chan error) error { + log.Info("[benchmark job] Start benchmarking remove") + // create data + vecs := j.genVec(j.dataset) + cfg := &payload.Remove_Config{ + SkipStrictExistCheck: j.removeConfig.SkipStrictExistCheck, + } + if j.timestamp > int64(0) { + cfg.Timestamp = j.timestamp + } + for i := 0; i < len(vecs); i++ { + log.Infof("[benchmark job] Start remove: iter = %d", i) + err := j.limiter.Wait(ctx) + if err != nil { + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + ech <- err + } + res, err := j.client.Remove(ctx, &payload.Remove_Request{ + Id: &payload.Object_ID{ + Id: strconv.Itoa(i), + }, + Config: cfg, + }) + if err != nil { + select { + case <-ctx.Done(): + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + select { + case <-ctx.Done(): + return errors.Join(err, context.Canceled) + case ech <- errors.Join(err, ctx.Err()): + } + default: + st, _ := status.FromError(err) + log.Warnf("[benchmark job] remove error is detected: code = %d, msg = %s", st.Code(), err.Error()) + } + } + log.Infof("[benchmark job] Finish remove: iter= %d \n%v", i, res) + } + + log.Info("[benchmark job] Finish benchmarking remove") + return nil +} diff --git a/pkg/tools/benchmark/job/service/search.go b/pkg/tools/benchmark/job/service/search.go index 2d14a6d84b..dbc9bcbc35 100644 --- a/pkg/tools/benchmark/job/service/search.go +++ b/pkg/tools/benchmark/job/service/search.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,87 +19,99 @@ package service import ( "context" - "testing" - "time" "github.com/vdaas/vald/apis/grpc/v1/payload" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/net/grpc/codes" + "github.com/vdaas/vald/internal/net/grpc/status" ) func (j *job) search(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking search") - if j.searchConfig == nil { - err := errors.NewErrInvalidOption("searchConfig", j.searchConfig) - select { - case <-ctx.Done(): - if err != context.Canceled { - ech <- errors.Wrap(err, ctx.Err().Error()) - } else { - ech <- err - } - case ech <- err: - } - return err - } - // create data - vecs := genVec(j.hdf5.GetTest(), j.dataset) - timeout, _ := time.ParseDuration(j.searchConfig.Timeout) + vecs := j.genVec(j.dataset) cfg := &payload.Search_Config{ Num: uint32(j.searchConfig.Num), MinNum: uint32(j.searchConfig.MinNum), Radius: float32(j.searchConfig.Radius), Epsilon: float32(j.searchConfig.Epsilon), - Timeout: timeout.Nanoseconds(), + Timeout: j.timeout.Nanoseconds(), } + lres := make([]*payload.Search_Response, len(vecs)) for i := 0; i < len(vecs); i++ { - log.Infof("[benchmark job] Start search: iter = %d", i) - lres, err := j.client.LinearSearch(ctx, &payload.Search_Request{ + if len(vecs[i]) != j.dimension { + log.Warn("len(vecs) ", len(vecs[i]), "is not matched with ", j.dimension) + continue + } + res, err := j.client.LinearSearch(ctx, &payload.Search_Request{ Vector: vecs[i], Config: cfg, }) if err != nil { select { case <-ctx.Done(): - if !errors.Is(err, context.Canceled) { - ech <- errors.Wrap(err, ctx.Err().Error()) - } else { - ech <- err + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + select { + case <-ctx.Done(): + return errors.Join(err, context.Canceled) + case ech <- errors.Join(err, ctx.Err()): + } + default: + st, _ := status.FromError(err) + if st.Code() != codes.NotFound { + log.Warnf("[benchmark job] linear search error is detected: code = %d, msg = %s", st.Code(), err.Error()) } - case ech <- err: } - return err } - bres := testing.Benchmark(func(b *testing.B) { - b.Helper() - b.ResetTimer() - start := time.Now() - sres, err := j.client.Search(ctx, &payload.Search_Request{ - Vector: vecs[i], - Config: cfg, - }) - if err != nil { + lres[i] = res + } + sres := make([]*payload.Search_Response, len(vecs)) + log.Infof("[benchmark job] Start search") + for i := 0; i < len(vecs); i++ { + if len(vecs[i]) != j.dimension { + log.Warn("len(vecs) ", len(vecs[i]), "is not matched with ", j.dimension) + continue + } + err := j.limiter.Wait(ctx) + if err != nil { + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + ech <- err + } + res, err := j.client.Search(ctx, &payload.Search_Request{ + Vector: vecs[i], + Config: cfg, + }) + log.Infof("[benchmark job] search %d", i) + if err != nil { + select { + case <-ctx.Done(): + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } select { case <-ctx.Done(): - if errors.Is(err, context.Canceled) { - ech <- errors.Wrap(err, ctx.Err().Error()) - } else { - ech <- err - } - case ech <- err: - break + return errors.Join(err, context.Canceled) + case ech <- errors.Join(err, ctx.Err()): + } + default: + st, _ := status.FromError(err) + if st.Code() != codes.NotFound { + log.Warnf("[benchmark job] search error is detected: code = %d, msg = %s", st.Code(), err.Error()) } } - latency := time.Since(start) - recall := calcRecall(lres.Results, sres.Results) - b.ReportMetric(recall, "recall") - b.ReportMetric(float64(latency.Microseconds()), "latency") - }) - // TODO: send metrics to the Prometeus - log.Infof("[benchmark job] Finish search bench: iter= %d \n%#v\n", i, bres) + } + sres[i] = res + } + recall := make([]float64, len(vecs)) + for i := 0; i < len(vecs); i++ { + recall[i] = calcRecall(lres[i], sres[i]) + log.Info("[branch job] search recall: ", recall[i]) } - log.Info("[benchmark job] Finish benchmarking search") return nil } diff --git a/pkg/tools/benchmark/job/service/update.go b/pkg/tools/benchmark/job/service/update.go new file mode 100644 index 0000000000..ac7a59a596 --- /dev/null +++ b/pkg/tools/benchmark/job/service/update.go @@ -0,0 +1,79 @@ +// +// Copyright (C) 2019-2023 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service + +import ( + "context" + "strconv" + + "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/net/grpc/status" +) + +func (j *job) update(ctx context.Context, ech chan error) error { + log.Info("[benchmark job] Start benchmarking update") + // create data + vecs := j.genVec(j.dataset) + cfg := &payload.Update_Config{ + SkipStrictExistCheck: j.updateConfig.SkipStrictExistCheck, + DisableBalancedUpdate: j.updateConfig.DisableBalancedUpdate, + } + if j.timestamp > int64(0) { + cfg.Timestamp = j.timestamp + } + for i := 0; i < len(vecs); i++ { + log.Infof("[benchmark job] Start update: iter = %d", i) + err := j.limiter.Wait(ctx) + if err != nil { + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + ech <- err + } + res, err := j.client.Update(ctx, &payload.Update_Request{ + Vector: &payload.Object_Vector{ + Id: strconv.Itoa(i), + Vector: vecs[i], + }, + Config: cfg, + }) + if err != nil { + select { + case <-ctx.Done(): + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + select { + case <-ctx.Done(): + return errors.Join(err, context.Canceled) + case ech <- errors.Join(err, ctx.Err()): + } + default: + st, _ := status.FromError(err) + log.Warnf("[benchmark job] update error is detected: code = %d, msg = %s\n", st.Code(), err.Error()) + } + } + if res != nil { + log.Infof("[benchmark job] iter=%d, Name=%s, Uuid=%s, Ips=%v", i, res.Name, res.Uuid, res.Ips) + } + } + log.Info("[benchmark job] Finish benchmarking upsert") + return nil +} diff --git a/pkg/tools/benchmark/job/service/upsert.go b/pkg/tools/benchmark/job/service/upsert.go new file mode 100644 index 0000000000..4b3d664a83 --- /dev/null +++ b/pkg/tools/benchmark/job/service/upsert.go @@ -0,0 +1,80 @@ +// +// Copyright (C) 2019-2023 vdaas.org vald team +// +// 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 +// +// https://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 service manages the main logic of benchmark job. +package service + +import ( + "context" + "strconv" + + "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/net/grpc/status" +) + +func (j *job) upsert(ctx context.Context, ech chan error) error { + log.Info("[benchmark job] Start benchmarking upsert") + // create data + vecs := j.genVec(j.dataset) + cfg := &payload.Upsert_Config{ + SkipStrictExistCheck: j.upsertConfig.SkipStrictExistCheck, + DisableBalancedUpdate: j.upsertConfig.DisableBalancedUpdate, + } + if j.timestamp > int64(0) { + cfg.Timestamp = j.timestamp + } + for i := 0; i < len(vecs); i++ { + log.Infof("[benchmark job] Start upsert: iter = %d", i) + err := j.limiter.Wait(ctx) + if err != nil { + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + ech <- err + } + res, err := j.client.Upsert(ctx, &payload.Upsert_Request{ + Vector: &payload.Object_Vector{ + Id: strconv.Itoa(i), + Vector: vecs[i], + }, + Config: cfg, + }) + if err != nil { + select { + case <-ctx.Done(): + if errors.Is(err, context.Canceled) { + return errors.Join(err, context.Canceled) + } + select { + case <-ctx.Done(): + return errors.Join(err, context.Canceled) + case ech <- errors.Join(err, ctx.Err()): + } + default: + st, _ := status.FromError(err) + log.Warnf("[benchmark job] upsert error is detected: code = %d, msg = %s", st.Code(), err.Error()) + } + } + if res != nil { + log.Infof("[benchmark job] iter=%d, Name=%s, Uuid=%s, Ips=%v", i, res.Name, res.Uuid, res.Ips) + } + } + + log.Info("[benchmark job] Finish benchmarking upsert") + return nil +} diff --git a/pkg/tools/benchmark/job/usecase/benchmarkd.go b/pkg/tools/benchmark/job/usecase/benchmarkd.go index 4bd9917b32..f714bf9563 100644 --- a/pkg/tools/benchmark/job/usecase/benchmarkd.go +++ b/pkg/tools/benchmark/job/usecase/benchmarkd.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -99,10 +99,12 @@ func New(cfg *config.Config) (r runner.Runner, err error) { service.WithUpsertConfig(cfg.Job.UpsertConfig), service.WithSearchConfig(cfg.Job.SearchConfig), service.WithRemoveConfig(cfg.Job.RemoveConfig), + service.WithObjectConfig(cfg.Job.ObjectConfig), service.WithHdf5(d), service.WithBeforeJobName(cfg.Job.BeforeJobName), service.WithBeforeJobNamespace(cfg.Job.BeforeJobNamespace), - service.WithK8sClient(cfg.Job.Client), + service.WithK8sClient(cfg.K8sClient), + service.WithRPS(cfg.Job.RPS), ) if err != nil { return nil, err diff --git a/pkg/tools/benchmark/operator/config/config.go b/pkg/tools/benchmark/operator/config/config.go index 915ec1251a..45cec91f51 100644 --- a/pkg/tools/benchmark/operator/config/config.go +++ b/pkg/tools/benchmark/operator/config/config.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/config/doc.go b/pkg/tools/benchmark/operator/config/doc.go index 57ef74c464..04fa0ebe9b 100644 --- a/pkg/tools/benchmark/operator/config/doc.go +++ b/pkg/tools/benchmark/operator/config/doc.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/handler/doc.go b/pkg/tools/benchmark/operator/handler/doc.go index f1014141ea..825bcc7f61 100644 --- a/pkg/tools/benchmark/operator/handler/doc.go +++ b/pkg/tools/benchmark/operator/handler/doc.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/handler/grpc/handler.go b/pkg/tools/benchmark/operator/handler/grpc/handler.go index 8120e778b0..a11cf4133f 100644 --- a/pkg/tools/benchmark/operator/handler/grpc/handler.go +++ b/pkg/tools/benchmark/operator/handler/grpc/handler.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/handler/grpc/option.go b/pkg/tools/benchmark/operator/handler/grpc/option.go index 4319ef3a74..d5d9c32d1e 100644 --- a/pkg/tools/benchmark/operator/handler/grpc/option.go +++ b/pkg/tools/benchmark/operator/handler/grpc/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/handler/rest/handler.go b/pkg/tools/benchmark/operator/handler/rest/handler.go index f752dbbbbf..1b9494c3d5 100644 --- a/pkg/tools/benchmark/operator/handler/rest/handler.go +++ b/pkg/tools/benchmark/operator/handler/rest/handler.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/handler/rest/option.go b/pkg/tools/benchmark/operator/handler/rest/option.go index ca3aee436d..054b120b94 100644 --- a/pkg/tools/benchmark/operator/handler/rest/option.go +++ b/pkg/tools/benchmark/operator/handler/rest/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/router/doc.go b/pkg/tools/benchmark/operator/router/doc.go index 053c9aeb49..ffa1ce388f 100644 --- a/pkg/tools/benchmark/operator/router/doc.go +++ b/pkg/tools/benchmark/operator/router/doc.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/router/option.go b/pkg/tools/benchmark/operator/router/option.go index fec24bfa26..681ce4bef7 100644 --- a/pkg/tools/benchmark/operator/router/option.go +++ b/pkg/tools/benchmark/operator/router/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/router/router.go b/pkg/tools/benchmark/operator/router/router.go index 9ca7f60c95..1303eeec8d 100644 --- a/pkg/tools/benchmark/operator/router/router.go +++ b/pkg/tools/benchmark/operator/router/router.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/service/doc.go b/pkg/tools/benchmark/operator/service/doc.go index eb86a28c5b..4d4cfe1548 100644 --- a/pkg/tools/benchmark/operator/service/doc.go +++ b/pkg/tools/benchmark/operator/service/doc.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/service/operator.go b/pkg/tools/benchmark/operator/service/operator.go index 9dc749c386..57b6cb4d15 100644 --- a/pkg/tools/benchmark/operator/service/operator.go +++ b/pkg/tools/benchmark/operator/service/operator.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -161,6 +161,7 @@ func (o *operator) jobReconcile(ctx context.Context, jobList map[string][]job.Jo if len(jobList) == 0 { log.Info("[reconcile job] no job is founded") o.jobs.Store(&map[string]string{}) + log.Debug("[reconcile job] finish") return } cjobs := o.getAtomicJob() @@ -215,8 +216,9 @@ func (o *operator) jobReconcile(ctx context.Context, jobList map[string][]job.Jo func (o *operator) benchJobReconcile(ctx context.Context, benchJobList map[string]v1.ValdBenchmarkJob) { log.Debugf("[reconcile benchmark job resource] job list: %#v", benchJobList) if len(benchJobList) == 0 { - o.benchjobs.Store(&(map[string]*v1.ValdBenchmarkJob{})) log.Info("[reconcile benchmark job resource] job resource not found") + o.benchjobs.Store(&(map[string]*v1.ValdBenchmarkJob{})) + log.Debug("[reconcile benchmark job resource] finish") return } cbjl := o.getAtomicBenchJob() @@ -227,7 +229,11 @@ func (o *operator) benchJobReconcile(ctx context.Context, benchJobList map[strin for k := range benchJobList { // update scenario status job := benchJobList[k] - if scenarios := o.getAtomicScenario(); scenarios != nil { + hasOwner := false + if len(job.GetOwnerReferences()) > 0 { + hasOwner = true + } + if scenarios := o.getAtomicScenario(); scenarios != nil && hasOwner { on := job.GetOwnerReferences()[0].Name if _, ok := scenarios[on]; ok { if scenarios[on].BenchJobStatus == nil { @@ -282,8 +288,7 @@ func (o *operator) benchScenarioReconcile(ctx context.Context, scenarioList map[ if len(scenarioList) == 0 { log.Info("[reconcile benchmark scenario resource]: scenario not found") o.scenarios.Store(&(map[string]*scenario{})) - o.benchjobs.Store(&(map[string]*v1.ValdBenchmarkJob{})) - o.jobs.Store(&(map[string]string{})) + log.Debug("[reconcile benchmark scenario resource] finish") return } cbsl := o.getAtomicScenario() @@ -536,16 +541,44 @@ func (o *operator) checkJobsStatus(ctx context.Context, jobs map[string]string) return err } -func (o *operator) initAtomics() { - if cbsl := o.getAtomicScenario(); len(cbsl) > 0 { - o.scenarios.Store(&(map[string]*scenario{})) - } - if cbjl := o.getAtomicBenchJob(); len(cbjl) > 0 { - o.benchjobs.Store(&(map[string]*v1.ValdBenchmarkJob{})) +// checkAtomics checks each atomic keeps consistency. +func (o *operator) checkAtomics() error { + cjl := o.getAtomicJob() + cbjl := o.getAtomicBenchJob() + cbsl := o.getAtomicScenario() + if len(cjl) == 0 { + if len(cbjl) > 0 || len(cbsl) > 0 { + log.Error("mismatch atomics: job=%v, benchjob=%v, scenario=%v", cjl, cbjl, cbsl) + return errors.ErrMismatchBenchmarkAtomics(cjl, cbjl, cbsl) + } + return nil + } else if len(cbjl) == 0 { + log.Error("mismatch atomics: job=%v, benchjob=%v, scenario=%v", cjl, cbjl, cbsl) + return errors.ErrMismatchBenchmarkAtomics(cjl, cbjl, cbsl) + } + jobCounter := len(cjl) + scenarioBenchCounter := 0 + for sc := range cbsl { + scenarioBenchCounter += len(cbsl[sc].BenchJobStatus) + } + for jobName := range cjl { + if benchJob := cbjl[jobName]; benchJob != nil { + jobCounter-- + if owner := benchJob.GetOwnerReferences(); len(owner) > 0 { + scenarioName := owner[0].Name + if scenario := cbsl[scenarioName]; scenario != nil { + if _, ok := scenario.BenchJobStatus[benchJob.GetName()]; ok { + scenarioBenchCounter-- + } + } + } + } } - if cjl := o.getAtomicJob(); len(cjl) > 0 { - o.jobs.Store(&(map[string]string{})) + if jobCounter != 0 || scenarioBenchCounter != 0 { + log.Error("mismatch atomics: job=%v, benchjob=%v, scenario=%v", cjl, cbjl, cbsl) + return errors.ErrMismatchBenchmarkAtomics(cjl, cbjl, cbsl) } + return nil } func (o *operator) PreStart(ctx context.Context) error { @@ -568,13 +601,13 @@ func (o *operator) Start(ctx context.Context) (<-chan error, error) { case <-ctx.Done(): return nil case <-rcticker.C: - cbsl := o.getAtomicScenario() - if cbsl == nil { - log.Info("benchmark scenario resource is empty") - // clear atomic pointer - o.initAtomics() - continue - } else { + // check mismatch atomic + err = o.checkAtomics() + if err != nil { + ech <- err + } + // determine whether benchmark scenario status should be updated. + if cbsl := o.getAtomicScenario(); cbsl != nil { scenarioStatus := make(map[string]v1.ValdBenchmarkScenarioStatus) for name, scenario := range cbsl { if scenario.Crd.Status != v1.BenchmarkScenarioCompleted { diff --git a/pkg/tools/benchmark/operator/service/option.go b/pkg/tools/benchmark/operator/service/option.go index da106afa7f..a34242f5a5 100644 --- a/pkg/tools/benchmark/operator/service/option.go +++ b/pkg/tools/benchmark/operator/service/option.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/tools/benchmark/operator/usecase/benchmarkd.go b/pkg/tools/benchmark/operator/usecase/benchmarkd.go index 6aa069b0c0..9f77e9ae15 100644 --- a/pkg/tools/benchmark/operator/usecase/benchmarkd.go +++ b/pkg/tools/benchmark/operator/usecase/benchmarkd.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2019-2022 vdaas.org vald team +// Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ import ( "github.com/vdaas/vald/internal/log" "github.com/vdaas/vald/internal/net/grpc" "github.com/vdaas/vald/internal/net/grpc/interceptor/server/recover" - "github.com/vdaas/vald/internal/observability" infometrics "github.com/vdaas/vald/internal/observability/metrics/info" "github.com/vdaas/vald/internal/runner" From 2819060683e1c63e5e0f77b2128bb4a6d08a4ed2 Mon Sep 17 00:00:00 2001 From: Kiichiro YUKAWA Date: Wed, 24 May 2023 14:27:06 +0900 Subject: [PATCH 04/11] create helm template for benchmark operator (#2027) * :sparkles: create helm template for benchmark operator Signed-off-by: vankichi * :sparkles: refactor helm template Signed-off-by: vankichi * style: Format code with gofumpt and prettier --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --- .../workflows/dockers-benchmark-job-image.yml | 7 +- .../dockers-benchmark-operator-image.yaml | 132 ++ Makefile.d/helm.mk | 42 + Makefile.d/k8s.mk | 45 + charts/vald-benchmark-operator/Chart.yaml | 62 + .../crds}/valdbenchmarkjob.yaml | 322 ++-- .../crds/valdbenchmarkoperatorrelease.yaml | 541 ++++-- .../crds/valdbenchmarkscenario.yaml | 109 ++ .../job-values.schema.json | 469 ++++++ .../scenario-values.schema.json | 64 + .../schemas/job-values.yaml | 360 ++++ .../schemas/scenario-values.yaml | 169 ++ .../templates/NOTES.txt | 1 + .../templates/_helpers.tpl | 310 ++++ .../templates/clusterrole.yaml | 115 ++ .../templates/clusterrolebinding.yaml | 30 + .../templates/configmap.yaml | 40 + .../templates/deployment.yaml | 152 ++ .../templates/service.yaml | 51 + .../templates/serviceaccount.yaml | 22 + .../values.schema.json | 637 +++++--- charts/vald-benchmark-operator/values.yaml | 734 ++++++--- .../values/benchmark-job.yaml | 45 + .../values/benchmark-scenario.yaml | 185 +++ .../crd/benchmark/valdbenchmarkscenario.yaml | 429 ----- .../k8s/vald/benchmark/job/job_template.go | 3 +- internal/test/data/hdf5/hdf5_test.go | 1447 +++++++++++++++++ k8s/tools/benchmark/operator/clusterrole.yaml | 10 +- .../operator/clusterrolebinding.yaml | 14 +- k8s/tools/benchmark/operator/configmap.yaml | 164 +- .../operator/crds/valdbenchmarkjob.yaml | 362 +++++ .../crds/valdbenchmarkoperatorrelease.yaml | 510 ++++++ .../operator/crds/valdbenchmarkscenario.yaml | 109 ++ k8s/tools/benchmark/operator/deployment.yaml | 71 +- k8s/tools/benchmark/operator/service.yaml | 38 + .../benchmark/operator/serviceaccount.yaml | 1 + 36 files changed, 6505 insertions(+), 1297 deletions(-) create mode 100644 .github/workflows/dockers-benchmark-operator-image.yaml create mode 100644 charts/vald-benchmark-operator/Chart.yaml rename {internal/k8s/crd/benchmark => charts/vald-benchmark-operator/crds}/valdbenchmarkjob.yaml (67%) create mode 100644 charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml create mode 100644 charts/vald-benchmark-operator/job-values.schema.json create mode 100644 charts/vald-benchmark-operator/scenario-values.schema.json create mode 100644 charts/vald-benchmark-operator/schemas/job-values.yaml create mode 100644 charts/vald-benchmark-operator/schemas/scenario-values.yaml create mode 100644 charts/vald-benchmark-operator/templates/NOTES.txt create mode 100644 charts/vald-benchmark-operator/templates/_helpers.tpl create mode 100644 charts/vald-benchmark-operator/templates/clusterrole.yaml create mode 100644 charts/vald-benchmark-operator/templates/clusterrolebinding.yaml create mode 100644 charts/vald-benchmark-operator/templates/configmap.yaml create mode 100644 charts/vald-benchmark-operator/templates/deployment.yaml create mode 100644 charts/vald-benchmark-operator/templates/service.yaml create mode 100644 charts/vald-benchmark-operator/templates/serviceaccount.yaml create mode 100644 charts/vald-benchmark-operator/values/benchmark-job.yaml create mode 100644 charts/vald-benchmark-operator/values/benchmark-scenario.yaml delete mode 100644 internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml create mode 100644 internal/test/data/hdf5/hdf5_test.go create mode 100644 k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml create mode 100644 k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml create mode 100644 k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml create mode 100644 k8s/tools/benchmark/operator/service.yaml diff --git a/.github/workflows/dockers-benchmark-job-image.yml b/.github/workflows/dockers-benchmark-job-image.yml index 28a239f96f..13d8aa8a16 100644 --- a/.github/workflows/dockers-benchmark-job-image.yml +++ b/.github/workflows/dockers-benchmark-job-image.yml @@ -47,7 +47,6 @@ on: - "internal/**" - "!internal/**/*_test.go" - "!internal/db/**" - - "!internal/k8s/**" - "apis/grpc/**" - "pkg/benchmark/job/**" - "cmd/benchmark/job/**" @@ -55,6 +54,10 @@ on: - "versions/GO_VERSION" - "versions/NGT_VERSION" +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref != 'refs/heads/main' && github.ref || github.sha }}-${{ github.event_name }} + cancel-in-progress: true + jobs: build: strategy: @@ -114,7 +117,7 @@ jobs: name: Slack notification needs: build runs-on: ubuntu-latest - if: github.ref == 'refs/heads/master' || startsWith( github.ref, 'refs/tags/') + if: github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/tags/') steps: - uses: technote-space/workflow-conclusion-action@v2 with: diff --git a/.github/workflows/dockers-benchmark-operator-image.yaml b/.github/workflows/dockers-benchmark-operator-image.yaml new file mode 100644 index 0000000000..ada420e0be --- /dev/null +++ b/.github/workflows/dockers-benchmark-operator-image.yaml @@ -0,0 +1,132 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +name: "Build docker image: benchmark-operator" +on: + push: + branches: + - master + tags: + - "*.*.*" + - "v*.*.*" + - "*.*.*-*" + - "v*.*.*-*" + paths: + - ".github/actions/docker-build/actions.yaml" + - ".github/workflows/dockers-benchmak-operator-image.yml" + - "go.mod" + - "go.sum" + - "internal/**" + - "!internal/**/*_test.go" + - "!internal/db/**" + - "!internal/k8s/**" + - "apis/grpc/**" + - "pkg/benchmark/operator/**" + - "cmd/benchmark/operator/**" + - "dockers/benchmark/operator/Dockerfile" + - "versions/GO_VERSION" + - "versions/NGT_VERSION" + pull_request: + paths: + - ".github/actions/docker-build/actions.yaml" + - ".github/workflows/dockers-benchmak-operator-image.yml" + - "go.mod" + - "go.sum" + - "internal/**" + - "!internal/**/*_test.go" + - "!internal/db/**" + - "apis/grpc/**" + - "pkg/benchmark/operator/**" + - "cmd/benchmark/operator/**" + - "dockers/benchmark/operator/Dockerfile" + - "versions/GO_VERSION" + - "versions/NGT_VERSION" + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref != 'refs/heads/main' && github.ref || github.sha }}-${{ github.event_name }} + cancel-in-progress: true + +jobs: + build: + strategy: + max-parallel: 4 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup QEMU + uses: docker/setup-qemu-action@v1 + with: + platforms: all + - name: Setup Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + with: + buildkitd-flags: "--debug" + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_PASS }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ secrets.PACKAGE_USER }} + password: ${{ secrets.PACKAGE_TOKEN }} + - name: Build and Publish + id: build_and_publish + uses: ./.github/actions/docker-build + with: + target: benchmark-operator + builder: ${{ steps.buildx.outputs.name }} + - name: Initialize CodeQL + if: startsWith( github.ref, 'refs/tags/') + uses: github/codeql-action/init@v2 + - name: Run vulnerability scanner (table) + if: startsWith( github.ref, 'refs/tags/') + uses: aquasecurity/trivy-action@master + with: + image-ref: "${{ steps.build_and_publish.outputs.IMAGE_NAME }}:${{ steps.build_and_publish.outputs.PRIMARY_TAG }}" + format: "table" + - name: Run vulnerability scanner (sarif) + if: startsWith( github.ref, 'refs/tags/') + uses: aquasecurity/trivy-action@master + with: + image-ref: "${{ steps.build_and_publish.outputs.IMAGE_NAME }}:${{ steps.build_and_publish.outputs.PRIMARY_TAG }}" + format: "template" + template: "@/contrib/sarif.tpl" + output: "trivy-results.sarif" + - name: Upload Trivy scan results to Security tab + if: startsWith( github.ref, 'refs/tags/') + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: "trivy-results.sarif" + slack: + name: Slack notification + needs: build + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/tags/') + steps: + - uses: technote-space/workflow-conclusion-action@v2 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: 8398a7/action-slack@v3 + with: + author_name: benchmark-operator image build + status: ${{ env.WORKFLOW_CONCLUSION }} + only_mention_fail: channel + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_NOTIFY_WEBHOOK_URL }} diff --git a/Makefile.d/helm.mk b/Makefile.d/helm.mk index 3123fee36d..b611009bdc 100644 --- a/Makefile.d/helm.mk +++ b/Makefile.d/helm.mk @@ -90,6 +90,26 @@ charts/vald-helm-operator/values.schema.json: \ GOPRIVATE=$(GOPRIVATE) \ go run -mod=readonly hack/helm/schema/gen/main.go charts/vald-helm-operator/values.yaml > charts/vald-helm-operator/values.schema.json +.PHONY: helm/schema/vald-benchmark-job +## generate json schema for Vald Benchmark Job Chart +helm/schema/vald-benchmark-job: charts/vald-benchmark-operator/job-values.schema.json + +charts/vald-benchmark-operator/job-values.schema.json: \ + charts/vald-benchmark-operator/schemas/job-values.yaml \ + hack/helm/schema/gen/main.go + GOPRIVATE=$(GOPRIVATE) \ + go run -mod=readonly hack/helm/schema/gen/main.go charts/vald-benchmark-operator/schemas/job-values.yaml > charts/vald-benchmark-operator/job-values.schema.json + +.PHONY: helm/schema/vald-benchmark-job +## generate json schema for Vald Benchmark Job Chart +helm/schema/vald-benchmark-scenario: charts/vald-benchmark-operator/scenario-values.schema.json + +charts/vald-benchmark-operator/scenario-values.schema.json: \ + charts/vald-benchmark-operator/schemas/scenario-values.yaml \ + hack/helm/schema/gen/main.go + GOPRIVATE=$(GOPRIVATE) \ + go run -mod=readonly hack/helm/schema/gen/main.go charts/vald-benchmark-operator/schemas/scenario-values.yaml > charts/vald-benchmark-operator/scenario-values.schema.json + .PHONY: helm/schema/vald-benchmark-operator ## generate json schema for Vald Benchmark Operator Chart helm/schema/vald-benchmark-operator: charts/vald-benchmark-operator/values.schema.json @@ -133,6 +153,28 @@ helm/schema/crd/vald-helm-operator: \ $(BINDIR)/yq eval-all 'select(fileIndex==0).spec.versions[0].schema.openAPIV3Schema.properties.spec = select(fileIndex==1).spec | select(fileIndex==0)' \ $(TEMP_DIR)/valdhelmoperatorrelease.yaml $(TEMP_DIR)/valdhelmoperatorrelease-spec.yaml > charts/vald-helm-operator/crds/valdhelmoperatorrelease.yaml +.PHONY: helm/schema/crd/vald-benchmark-job +## generate OpenAPI v3 schema for ValdBenchmarkJobRelease +helm/schema/crd/vald-benchmark-job: \ + yq/install + mv charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml $(TEMP_DIR)/valdbenchmarkjob.yaml + GOPRIVATE=$(GOPRIVATE) \ + go run -mod=readonly hack/helm/schema/crd/main.go \ + charts/vald-benchmark-operator/schemas/job-values.yaml > $(TEMP_DIR)/valdbenchmarkjob-spec.yaml + $(BINDIR)/yq eval-all 'select(fileIndex==0).spec.versions[0].schema.openAPIV3Schema.properties.spec = select(fileIndex==1).spec | select(fileIndex==0)' \ + $(TEMP_DIR)/valdbenchmarkjob.yaml $(TEMP_DIR)/valdbenchmarkjob-spec.yaml > charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml + +.PHONY: helm/schema/crd/vald-benchmark-scenario +## generate OpenAPI v3 schema for ValdBenchmarkScenarioRelease +helm/schema/crd/vald-benchmark-scenario: \ + yq/install + mv charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml $(TEMP_DIR)/valdbenchmarkscenario.yaml + GOPRIVATE=$(GOPRIVATE) \ + go run -mod=readonly hack/helm/schema/crd/main.go \ + charts/vald-benchmark-operator/schemas/scenario-values.yaml > $(TEMP_DIR)/valdbenchmarkscenario-spec.yaml + $(BINDIR)/yq eval-all 'select(fileIndex==0).spec.versions[0].schema.openAPIV3Schema.properties.spec = select(fileIndex==1).spec | select(fileIndex==0)' \ + $(TEMP_DIR)/valdbenchmarkscenario.yaml $(TEMP_DIR)/valdbenchmarkscenario-spec.yaml > charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml + .PHONY: helm/schema/crd/vald-benchmark-operator ## generate OpenAPI v3 schema for ValdBenchmarkOperatorRelease helm/schema/crd/vald-benchmark-operator: \ diff --git a/Makefile.d/k8s.mk b/Makefile.d/k8s.mk index efa2590143..8973afe52f 100644 --- a/Makefile.d/k8s.mk +++ b/Makefile.d/k8s.mk @@ -63,6 +63,23 @@ k8s/manifest/helm-operator/update: \ rm -rf $(TEMP_DIR) cp -r charts/vald-helm-operator/crds k8s/operator/helm/crds +.PHONY: k8s/manifest/benchmark-operator/clean +## clean k8s manifests for benchmark-operator +k8s/manifest/benchmark-operator/clean: + rm -rf \ + k8s/tools/benchmark/operator + +.PHONY: k8s/manifest/benchmark-operator/update +## update k8s manifests for benchmark-operator using helm templates +k8s/manifest/benchmark-operator/update: \ + k8s/manifest/benchmark-operator/clean + helm template \ + --output-dir $(TEMP_DIR) \ + charts/vald-benchmark-operator + mkdir -p k8s/tools/benchmark + mv $(TEMP_DIR)/vald-benchmark-operator/templates k8s/tools/benchmark/operator + rm -rf $(TEMP_DIR) + cp -r charts/vald-benchmark-operator/crds k8s/tools/benchmark/operator/crds .PHONY: k8s/vald/deploy ## deploy vald sample cluster to k8s @@ -153,6 +170,34 @@ k8s/vr/delete: \ k8s/metrics/metrics-server/delete kubectl delete vr vald-cluster +.PHONY: k8s/vald-benchmark-operator/deploy +## deploy vald-benchmark-operator to k8s +k8s/vald-benchmark-operator/deploy: + helm template \ + --output-dir $(TEMP_DIR) \ + --set image.tag=${VERSION} \ + --include-crds \ + charts/vald-benchmark-operator + kubectl create -f $(TEMP_DIR)/vald-benchmark-operator/crds/valdbenchmarkjob.yaml + kubectl create -f $(TEMP_DIR)/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml + kubectl create -f $(TEMP_DIR)/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml + kubectl apply -f $(TEMP_DIR)/vald-benchmark-operator/templates + sleep 2 + kubectl wait --for=condition=ready pod -l name=vald-benchmark-operator --timeout=600s + +.PHONY: k8s/vald-benchmark-operator/delete +## delete vald-benchmark-operator from k8s +k8s/vald-benchmark-operator/delete: + helm template \ + --output-dir $(TEMP_DIR) \ + --set image.tag=${VERSION} \ + --include-crds \ + charts/vald-benchmark-operator + kubectl delete -f $(TEMP_DIR)/vald-benchmark-operator/templates + kubectl wait --for=delete pod -l name=vald-benchmark-operator --timeout=600s + kubectl delete -f $(TEMP_DIR)/vald-benchmark-operator/crds + rm -rf $(TEMP_DIR) + .PHONY: k8s/external/cert-manager/deploy ## deploy cert-manager k8s/external/cert-manager/deploy: diff --git a/charts/vald-benchmark-operator/Chart.yaml b/charts/vald-benchmark-operator/Chart.yaml new file mode 100644 index 0000000000..b0726effed --- /dev/null +++ b/charts/vald-benchmark-operator/Chart.yaml @@ -0,0 +1,62 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +appVersion: "1.16.0" +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# + +apiVersion: v2 +name: vald-benchmark-operator +version: v1.7.5 +description: A benchmark operator for benchmarking the Vald cluster. +type: application +keywords: + - Vald + - NGT + - vector + - search + - approximate-nearest-neighbor-search + - nearest-neighbor-search + - vector-search-engine + - similarity-search + - image-search + - Kubernetes + - k8s + - AI + - artificial-intelligence +home: https://vald.vdaas.org +icon: https://raw.githubusercontent.com/vdaas/vald/main/assets/image/svg/symbol.svg +sources: + - https://github.com/vdaas/vald +maintainers: + - name: kpango + email: kpango@vdaas.org + - name: vankichi + email: vankichi@vdaas.org + - name: kmrmt + email: ksk@vdaas.org diff --git a/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml b/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml similarity index 67% rename from internal/k8s/crd/benchmark/valdbenchmarkjob.yaml rename to charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml index dbaf7547cc..de85714816 100644 --- a/internal/k8s/crd/benchmark/valdbenchmarkjob.yaml +++ b/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml @@ -47,16 +47,10 @@ spec: type: object properties: apiVersion: - description: - "APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" type: string kind: - description: - "Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" type: string metadata: type: object @@ -69,129 +63,17 @@ spec: - Healthy type: string spec: - description: ValdBenchmarkJobSpec defines the desired state of ValdBenchmarkJob type: object properties: - target: - description: BenchmarkTarget defines the desired state of BenchmarkTarget - properties: - host: - type: string - port: - type: integer - required: - - host - - port - type: object - dataset: - description: BenchmarkDataset defines the desired state of BenchmarkDateset - properties: - group: - type: string - indexes: - type: integer - name: - type: string - range: - description: BenchmarkDatasetRange defines the desired state of BenchmarkDatesetRange - properties: - end: - type: integer - start: - type: integer - required: - - end - - start - type: object - required: - - group - - indexes - - name - type: object - dimension: - type: integer - job_type: - type: string - repetition: - type: integer - replica: - type: integer - rules: - items: - description: BenchmarkJobRule defines the desired state of BenchmarkJobRule - properties: - name: - type: string - type: - type: string - required: - - name - - type - type: object - type: array - rps: - type: integer - insert_config: - description: InsertConfig defines the desired state of insert config - properties: - skip_strict_exist_check: - type: boolean - timestamp: - type: string - type: object - remove_config: - description: RemoveConfig defines the desired state of remove config - properties: - skip_strict_exist_check: - type: boolean - timestamp: - type: string - type: object - search_config: - description: SearchConfig defines the desired state of search config - properties: - epsilon: - type: number - min_num: - format: int32 - type: integer - num: - format: int32 - type: integer - radius: - type: number - timeout: - type: string - type: object - update_config: - description: UpdateConfig defines the desired state of update config - properties: - skip_strict_exist_check: - type: boolean - timestamp: - type: string - disable_balanced_update: - type: boolean - type: object - upsert_config: - description: UpsertConfig defines the desired state of upsert config - properties: - skip_strict_exist_check: - type: boolean - timestamp: - type: string - disable_balanced_update: - type: boolean - type: object client_config: - description: ClientConfig represents the configurations for gRPC client. + type: object properties: addrs: + type: array items: type: string - type: array backoff: - description: Backoff represents the configuration for the internal backoff package. + type: object properties: backoff_factor: type: number @@ -207,21 +89,11 @@ spec: type: string retry_count: type: integer - type: object call_option: - description: CallOption represents the configurations for call option. - properties: - max_recv_msg_size: - type: integer - max_retry_rpc_buffer_size: - type: integer - max_send_msg_size: - type: integer - wait_for_ready: - type: boolean type: object + x-kubernetes-preserve-unknown-fields: true circuit_breaker: - description: CircuitBreaker represents the configuration for the internal circuitbreaker package. + type: object properties: closed_error_rate: type: number @@ -230,27 +102,24 @@ spec: half_open_error_rate: type: number min_samples: - format: int64 type: integer open_timeout: type: string - type: object connection_pool: - description: ConnectionPool represents the configurations for connection pool. + type: object properties: + enable_dns_resolver: + type: boolean enable_rebalance: type: boolean old_conn_close_duration: type: string rebalance_duration: type: string - resolve_dns: - type: boolean size: type: integer - type: object dial_option: - description: DialOption represents the configurations for dial option. + type: object properties: backoff_base_delay: type: string @@ -269,11 +138,13 @@ spec: insecure: type: boolean interceptors: + type: array items: type: string - type: array + enum: + - TraceInterceptor keepalive: - description: GRPCClientKeepalive represents the configurations for gRPC keep-alive. + type: object properties: permit_without_stream: type: boolean @@ -281,28 +152,24 @@ spec: type: string timeout: type: string - type: object max_msg_size: type: integer - minimum_connection_timeout: + min_connection_timeout: type: string net: - description: Net represents the network configuration tcp, udp, unix domain socket. + type: object properties: dialer: - description: Dialer represents the configuration for dial. + type: object properties: dual_stack_enabled: type: boolean - fallback_delay: - type: string keepalive: type: string timeout: type: string - type: object dns: - description: DNS represents the configuration for resolving DNS. + type: object properties: cache_enabled: type: boolean @@ -310,9 +177,8 @@ spec: type: string refresh_duration: type: string - type: object socket_option: - description: SocketOption represents the socket configurations. + type: object properties: ip_recover_destination_addr: type: boolean @@ -332,57 +198,165 @@ spec: type: boolean tcp_quick_ack: type: boolean - type: object tls: - description: TLS represent the TLS configuration for server. + type: object properties: ca: - description: CA represent the CA certificate environment variable key used to start server. type: string cert: - description: Cert represent the certificate environment variable key used to start server. type: string enabled: - description: Enable represent the server enable TLS or not. type: boolean insecure_skip_verify: - description: InsecureSkipVerify represent enable/disable skip SSL certificate verification type: boolean key: - description: Key represent the private key environment variable key used to start server. type: string - type: object - type: object read_buffer_size: type: integer timeout: type: string write_buffer_size: type: integer - type: object health_check_duration: type: string + max_recv_msg_size: + type: integer + max_retry_rpc_buffer_size: + type: integer + max_send_msg_size: + type: integer tls: - description: TLS represent the TLS configuration for server. + type: object properties: ca: - description: CA represent the CA certificate environment variable key used to start server. type: string cert: - description: Cert represent the certificate environment variable key used to start server. type: string enabled: - description: Enable represent the server enable TLS or not. type: boolean insecure_skip_verify: - description: InsecureSkipVerify represent enable/disable skip SSL certificate verification type: boolean key: - description: Key represent the private key environment variable key used to start server. type: string + wait_for_ready: + type: boolean + dataset: + type: object + properties: + group: + type: string + minLength: 1 + indexes: + type: integer + minimum: 0 + name: + type: string + enum: + - fashion-mnist + range: type: object + properties: + end: + type: integer + minimum: 1 + start: + type: integer + minimum: 1 + required: + - name + - indexes + - group + - range + dimension: + type: integer + minimum: 1 + insert_config: + type: object + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + job_type: + type: string + enum: + - insert + - update + - upsert + - search + - remove + - get_object + - exists + object_config: + type: object + properties: + filter_config: + type: object + properties: + host: + type: string + remove_config: type: object - required: - - job_type - - dimension - - dataset + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + repetition: + type: integer + minimum: 1 + replica: + type: integer + minimum: 1 + rps: + type: integer + maximum: 65535 + minimum: 0 + rules: + type: array + items: + type: string + search_config: + type: object + properties: + epsilon: + type: number + min_num: + type: integer + num: + type: integer + radius: + type: number + timeout: + type: string + target: + type: object + properties: + host: + type: string + minLength: 1 + port: + type: integer + maximum: 65535 + minimum: 0 + required: + - host + - port + update_config: + type: object + properties: + disable_balance_update: + type: boolean + skip_strict_exist_check: + type: boolean + timestamp: + type: string + upsert_config: + type: object + properties: + disable_balance_update: + type: boolean + skip_strict_exist_check: + type: boolean + timestamp: + type: string diff --git a/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml b/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml index ae88b72dc5..3451a48b38 100644 --- a/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml +++ b/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml @@ -16,17 +16,17 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: valdbenchmarkscenarios.vald.vdaas.org + name: valdbenchmarkoperatorreleases.vald.vdaas.org spec: group: vald.vdaas.org names: - kind: ValdBenchmarkScenario - listKind: ValdBenchmarkScenarioList - plural: valdbenchmarkscenarios - singular: valdbenchmarkscenario + kind: ValdBenchmarkOperatorRelease + listKind: ValdBenchmarkOperatorReleaseList + plural: valdbenchmarkoperatorreleases + singular: valdbenchmarkoperatorrelease shortNames: - - vbo - - vbos + - vbor + - vbors scope: Namespaced versions: - name: v1 @@ -55,171 +55,456 @@ spec: description: ValdBenchmarkScenarioStatus defines the observed state of ValdBenchmarkScenario enum: - NotReady + - Completed - Available - Healthy type: string spec: type: object properties: - client_config: + affinity: + type: object + x-kubernetes-preserve-unknown-fields: true + annotations: + type: object + x-kubernetes-preserve-unknown-fields: true + image: + type: object + properties: + pullPolicy: + type: string + enum: + - Always + - Never + - IfNotPresent + repository: + type: string + tag: + type: string + logging: + type: object + properties: + format: + type: string + enum: + - raw + - json + level: + type: string + enum: + - debug + - info + - warn + - error + - fatal + logger: + type: string + enum: + - glg + - zap + name: + type: string + nodeSelector: + type: object + x-kubernetes-preserve-unknown-fields: true + observability: type: object properties: - addrs: - type: array - items: - type: string - backoff: + enabled: + type: boolean + otlp: type: object properties: - backoff_factor: - type: number - backoff_time_limit: + attribute: + type: object + properties: + metrics: + type: object + properties: + enable_cgo: + type: boolean + enable_goroutine: + type: boolean + enable_memory: + type: boolean + enable_version_info: + type: boolean + version_info_labels: + type: array + items: + type: string + namespace: + type: string + node_name: + type: string + pod_name: + type: string + service_name: + type: string + collector_endpoint: type: string - enable_error_log: - type: boolean - initial_duration: + metrics_export_interval: type: string - jitter_limit: + metrics_export_timeout: type: string - maximum_duration: + trace_batch_timeout: type: string - retry_count: - type: integer - call_option: - type: object - x-kubernetes-preserve-unknown-fields: true - circuit_breaker: - type: object - properties: - closed_error_rate: - type: number - closed_refresh_timeout: + trace_export_timeout: type: string - half_open_error_rate: - type: number - min_samples: + trace_max_export_batch_size: type: integer - open_timeout: - type: string - connection_pool: + trace_max_queue_size: + type: integer + trace: type: object properties: - enable_dns_resolver: - type: boolean - enable_rebalance: + enabled: type: boolean - old_conn_close_duration: - type: string - rebalance_duration: - type: string - size: + sampling_rate: type: integer - dial_option: + podAnnotations: + type: object + x-kubernetes-preserve-unknown-fields: true + podSecurityContext: + type: object + x-kubernetes-preserve-unknown-fields: true + rbac: + type: object + properties: + create: + type: boolean + name: + type: string + replicas: + type: integer + resources: + type: object + properties: + limits: + type: object + x-kubernetes-preserve-unknown-fields: true + requests: + type: object + x-kubernetes-preserve-unknown-fields: true + securityContext: + type: object + x-kubernetes-preserve-unknown-fields: true + server_config: + type: object + properties: + full_shutdown_duration: + type: string + healths: type: object properties: - backoff_base_delay: - type: string - backoff_jitter: - type: number - backoff_max_delay: - type: string - backoff_multiplier: - type: number - enable_backoff: - type: boolean - initial_connection_window_size: - type: integer - initial_window_size: - type: integer - insecure: - type: boolean - interceptors: - type: array - items: - type: string - enum: - - TraceInterceptor - keepalive: + liveness: type: object properties: - permit_without_stream: + enabled: type: boolean - time: + host: type: string - timeout: + livenessProbe: + type: object + properties: + failureThreshold: + type: integer + httpGet: + type: object + properties: + path: + type: string + port: + type: string + scheme: + type: string + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + port: + type: integer + server: + type: object + properties: + http: + type: object + properties: + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + timeout: + type: string + write_timeout: + type: string + mode: + type: string + network: + type: string + probe_wait_time: + type: string + socket_path: + type: string + servicePort: + type: integer + readiness: + type: object + properties: + enabled: + type: boolean + host: type: string - max_msg_size: - type: integer - min_connection_timeout: - type: string - net: + port: + type: integer + readinessProbe: + type: object + properties: + failureThreshold: + type: integer + httpGet: + type: object + properties: + path: + type: string + port: + type: string + scheme: + type: string + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + server: + type: object + properties: + http: + type: object + properties: + handler_timeout: + type: string + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + write_timeout: + type: string + mode: + type: string + network: + type: string + probe_wait_time: + type: string + socket_path: + type: string + servicePort: + type: integer + startup: + type: object + properties: + enabled: + type: boolean + startupProbe: type: object properties: - dialer: + failureThreshold: + type: integer + httpGet: type: object properties: - dual_stack_enabled: - type: boolean - keepalive: + path: + type: string + port: type: string - timeout: + scheme: type: string - dns: + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + metrics: + type: object + properties: + pprof: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: integer + server: type: object properties: - cache_enabled: - type: boolean - cache_expiration: + http: + type: object + properties: + handler_timeout: + type: string + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + write_timeout: + type: string + mode: type: string - refresh_duration: + network: type: string - socket_option: - type: "" - tls: - type: "" - read_buffer_size: - type: integer - timeout: - type: string - write_buffer_size: - type: integer - health_check_duration: - type: string - max_recv_msg_size: - type: integer - max_retry_rpc_buffer_size: - type: integer - max_send_msg_size: - type: integer + probe_wait_time: + type: string + socket_path: + type: string + servers: + type: object + properties: + grpc: + type: object + properties: + enabled: + type: boolean + host: + type: string + name: + type: string + port: + type: integer + server: + type: object + properties: + grpc: + type: object + properties: + bidirectional_stream_concurrency: + type: integer + connection_timeout: + type: string + enable_reflection: + type: boolean + header_table_size: + type: integer + initial_conn_window_size: + type: integer + initial_window_size: + type: integer + interceptors: + type: array + items: + type: string + keepalive: + type: object + properties: + max_conn_age: + type: string + max_conn_age_grace: + type: string + max_conn_idle: + type: string + min_time: + type: string + permit_without_stream: + type: boolean + time: + type: string + timeout: + type: string + max_header_list_size: + type: integer + max_receive_message_size: + type: integer + max_send_msg_size: + type: integer + read_buffer_size: + type: integer + write_buffer_size: + type: integer + mode: + type: string + network: + type: string + probe_wait_time: + type: string + restart: + type: boolean + socket_path: + type: string + servicePort: + type: integer + rest: + type: object + properties: + enabled: + type: boolean tls: - type: "" - wait_for_ready: - type: boolean - dataset: + type: object + properties: + ca: + type: string + cert: + type: string + enabled: + type: boolean + insecure_skip_verify: + type: boolean + key: + type: string + service: type: object properties: - group: + annotations: + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + type: boolean + externalTrafficPolicy: type: string - indexes: - type: integer + labels: + type: object + x-kubernetes-preserve-unknown-fields: true + type: + type: string + enum: + - ClusterIP + - LoadBalancer + - NodePort + serviceAccount: + type: object + properties: + create: + type: boolean name: type: string - range: - type: object - properties: - end: - type: integer - start: - type: integer - jobs: + time_zone: + type: string + tolerations: type: array items: type: object x-kubernetes-preserve-unknown-fields: true - target: - type: object - properties: - host: - type: string - port: - type: integer + version: + type: string diff --git a/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml b/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml new file mode 100644 index 0000000000..f705e7df6b --- /dev/null +++ b/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml @@ -0,0 +1,109 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: valdbenchmarkscenarios.vald.vdaas.org +spec: + group: vald.vdaas.org + names: + kind: ValdBenchmarkScenario + listKind: ValdBenchmarkScenarioList + plural: valdbenchmarkscenarios + singular: valdbenchmarkscenario + shortNames: + - vbo + - vbos + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - jsonPath: .status + name: STATUS + type: string + schema: + openAPIV3Schema: + description: ValdBenchmarkScenario is the Schema for the valdbenchmarkscenarios API + type: object + properties: + apiVersion: + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + status: + description: ValdBenchmarkScenarioStatus defines the observed state of ValdBenchmarkScenario + enum: + - NotReady + - Completed + - Available + - Healthy + type: string + spec: + type: object + properties: + dataset: + type: object + properties: + group: + type: string + minLength: 1 + indexes: + type: integer + minimum: 0 + name: + type: string + enum: + - fashion-mnist + range: + type: object + properties: + end: + type: integer + minimum: 1 + start: + type: integer + minimum: 1 + required: + - name + - indexes + - group + - range + jobs: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + target: + type: object + properties: + host: + type: string + minLength: 1 + port: + type: integer + maximum: 65535 + minimum: 0 + required: + - host + - port diff --git a/charts/vald-benchmark-operator/job-values.schema.json b/charts/vald-benchmark-operator/job-values.schema.json new file mode 100644 index 0000000000..24f2f5b91f --- /dev/null +++ b/charts/vald-benchmark-operator/job-values.schema.json @@ -0,0 +1,469 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Values", + "type": "object", + "properties": { + "client_config": { + "type": "object", + "description": "gRPC client config for request to the Vald cluster", + "properties": { + "addrs": { + "type": "array", + "description": "gRPC client addresses", + "items": { "type": "string" } + }, + "backoff": { + "type": "object", + "properties": { + "backoff_factor": { + "type": "number", + "description": "gRPC client backoff factor" + }, + "backoff_time_limit": { + "type": "string", + "description": "gRPC client backoff time limit" + }, + "enable_error_log": { + "type": "boolean", + "description": "gRPC client backoff log enabled" + }, + "initial_duration": { + "type": "string", + "description": "gRPC client backoff initial duration" + }, + "jitter_limit": { + "type": "string", + "description": "gRPC client backoff jitter limit" + }, + "maximum_duration": { + "type": "string", + "description": "gRPC client backoff maximum duration" + }, + "retry_count": { + "type": "integer", + "description": "gRPC client backoff retry count" + } + } + }, + "call_option": { "type": "object" }, + "circuit_breaker": { + "type": "object", + "properties": { + "closed_error_rate": { + "type": "number", + "description": "gRPC client circuitbreaker closed error rate" + }, + "closed_refresh_timeout": { + "type": "string", + "description": "gRPC client circuitbreaker closed refresh timeout" + }, + "half_open_error_rate": { + "type": "number", + "description": "gRPC client circuitbreaker half-open error rate" + }, + "min_samples": { + "type": "integer", + "description": "gRPC client circuitbreaker minimum sampling count" + }, + "open_timeout": { + "type": "string", + "description": "gRPC client circuitbreaker open timeout" + } + } + }, + "connection_pool": { + "type": "object", + "properties": { + "enable_dns_resolver": { + "type": "boolean", + "description": "enables gRPC client connection pool dns resolver, when enabled vald uses ip handshake exclude dns discovery which improves network performance" + }, + "enable_rebalance": { + "type": "boolean", + "description": "enables gRPC client connection pool rebalance" + }, + "old_conn_close_duration": { + "type": "string", + "description": "makes delay before gRPC client connection closing during connection pool rebalance" + }, + "rebalance_duration": { + "type": "string", + "description": "gRPC client connection pool rebalance duration" + }, + "size": { + "type": "integer", + "description": "gRPC client connection pool size" + } + } + }, + "dial_option": { + "type": "object", + "properties": { + "backoff_base_delay": { + "type": "string", + "description": "gRPC client dial option base backoff delay" + }, + "backoff_jitter": { + "type": "number", + "description": "gRPC client dial option base backoff delay" + }, + "backoff_max_delay": { + "type": "string", + "description": "gRPC client dial option max backoff delay" + }, + "backoff_multiplier": { + "type": "number", + "description": "gRPC client dial option base backoff delay" + }, + "enable_backoff": { + "type": "boolean", + "description": "gRPC client dial option backoff enabled" + }, + "initial_connection_window_size": { + "type": "integer", + "description": "gRPC client dial option initial connection window size" + }, + "initial_window_size": { + "type": "integer", + "description": "gRPC client dial option initial window size" + }, + "insecure": { + "type": "boolean", + "description": "gRPC client dial option insecure enabled" + }, + "interceptors": { + "type": "array", + "description": "gRPC client interceptors", + "items": { "type": "string", "enum": ["TraceInterceptor"] } + }, + "keepalive": { + "type": "object", + "properties": { + "permit_without_stream": { + "type": "boolean", + "description": "gRPC client keep alive permit without stream" + }, + "time": { + "type": "string", + "description": "gRPC client keep alive time" + }, + "timeout": { + "type": "string", + "description": "gRPC client keep alive timeout" + } + } + }, + "max_msg_size": { + "type": "integer", + "description": "gRPC client dial option max message size" + }, + "min_connection_timeout": { + "type": "string", + "description": "gRPC client dial option minimum connection timeout" + }, + "net": { + "type": "object", + "properties": { + "dialer": { + "type": "object", + "properties": { + "dual_stack_enabled": { + "type": "boolean", + "description": "gRPC client TCP dialer dual stack enabled" + }, + "keepalive": { + "type": "string", + "description": "gRPC client TCP dialer keep alive" + }, + "timeout": { + "type": "string", + "description": "gRPC client TCP dialer timeout" + } + } + }, + "dns": { + "type": "object", + "properties": { + "cache_enabled": { + "type": "boolean", + "description": "gRPC client TCP DNS cache enabled" + }, + "cache_expiration": { + "type": "string", + "description": "gRPC client TCP DNS cache expiration" + }, + "refresh_duration": { + "type": "string", + "description": "gRPC client TCP DNS cache refresh duration" + } + } + }, + "socket_option": { + "type": "object", + "properties": { + "ip_recover_destination_addr": { + "type": "boolean", + "description": "server listen socket option for ip_recover_destination_addr functionality" + }, + "ip_transparent": { + "type": "boolean", + "description": "server listen socket option for ip_transparent functionality" + }, + "reuse_addr": { + "type": "boolean", + "description": "server listen socket option for reuse_addr functionality" + }, + "reuse_port": { + "type": "boolean", + "description": "server listen socket option for reuse_port functionality" + }, + "tcp_cork": { + "type": "boolean", + "description": "server listen socket option for tcp_cork functionality" + }, + "tcp_defer_accept": { + "type": "boolean", + "description": "server listen socket option for tcp_defer_accept functionality" + }, + "tcp_fast_open": { + "type": "boolean", + "description": "server listen socket option for tcp_fast_open functionality" + }, + "tcp_no_delay": { + "type": "boolean", + "description": "server listen socket option for tcp_no_delay functionality" + }, + "tcp_quick_ack": { + "type": "boolean", + "description": "server listen socket option for tcp_quick_ack functionality" + } + } + }, + "tls": { + "type": "object", + "properties": { + "ca": { "type": "string" }, + "cert": { "type": "string" }, + "enabled": { "type": "boolean" }, + "insecure_skip_verify": { "type": "boolean" }, + "key": { "type": "string" } + } + } + } + }, + "read_buffer_size": { + "type": "integer", + "description": "gRPC client dial option read buffer size" + }, + "timeout": { + "type": "string", + "description": "gRPC client dial option timeout" + }, + "write_buffer_size": { + "type": "integer", + "description": "gRPC client dial option write buffer size" + } + } + }, + "health_check_duration": { + "type": "string", + "description": "gRPC client health check duration" + }, + "max_recv_msg_size": { "type": "integer" }, + "max_retry_rpc_buffer_size": { "type": "integer" }, + "max_send_msg_size": { "type": "integer" }, + "tls": { + "type": "object", + "properties": { + "ca": { "type": "string", "description": "TLS ca path" }, + "cert": { "type": "string", "description": "TLS cert path" }, + "enabled": { "type": "boolean", "description": "TLS enabled" }, + "insecure_skip_verify": { + "type": "boolean", + "description": "enable/disable skip SSL certificate verification" + }, + "key": { "type": "string", "description": "TLS key path" } + } + }, + "wait_for_ready": { "type": "boolean" } + } + }, + "dataset": { + "type": "object", + "description": "dataset information", + "properties": { + "group": { + "type": "string", + "description": "the hdf5 group name of dataset", + "minLength": 1 + }, + "indexes": { + "type": "integer", + "description": "the amount of indexes", + "minimum": 0 + }, + "name": { + "type": "string", + "description": "the name of dataset", + "enum": ["fashion-mnist"] + }, + "range": { + "type": "object", + "description": "the data range of indexes", + "properties": { + "end": { + "type": "integer", + "description": "end index number", + "minimum": 1 + }, + "start": { + "type": "integer", + "description": "start index number", + "minimum": 1 + } + } + } + }, + "required": ["name", "indexes", "group", "range"] + }, + "dimension": { + "type": "integer", + "description": "vector dimension", + "minimum": 1 + }, + "insert_config": { + "type": "object", + "description": "insert config", + "properties": { + "skip_strict_exist_check": { + "type": "boolean", + "description": "skip strict exists check flag config" + }, + "timestamp": { "type": "string", "description": "index timestamp" } + } + }, + "job_type": { + "type": "string", + "description": "job type name", + "enum": [ + "insert", + "update", + "upsert", + "search", + "remove", + "get_object", + "exists" + ] + }, + "object_config": { + "type": "object", + "description": "object config", + "properties": { + "filter_config": { + "type": "object", + "description": "filter target config", + "properties": { + "host": { "type": "string", "description": "filter target host" } + } + } + } + }, + "remove_config": { + "type": "object", + "description": "remove config", + "properties": { + "skip_strict_exist_check": { + "type": "boolean", + "description": "skip strict exists check flag config" + }, + "timestamp": { "type": "string", "description": "index timestamp" } + } + }, + "repetition": { + "type": "integer", + "description": "the number of repeat job", + "minimum": 1 + }, + "replica": { + "type": "integer", + "description": "the number of running concurrency job", + "minimum": 1 + }, + "rps": { + "type": "integer", + "description": "desired request per sec", + "maximum": 65535, + "minimum": 0 + }, + "rules": { + "type": "array", + "description": "executing rule", + "items": { "type": "string" } + }, + "search_config": { + "type": "object", + "description": "upsert config", + "properties": { + "epsilon": { "type": "number", "description": "epsilon" }, + "min_num": { + "type": "integer", + "description": "minimum number of top-k" + }, + "num": { "type": "integer", "description": "number of top-k" }, + "radius": { "type": "number", "description": "radius" }, + "timeout": { + "type": "string", + "description": "search operation timeout" + } + } + }, + "target": { + "type": "object", + "description": "target cluster location", + "properties": { + "host": { + "type": "string", + "description": "target cluster host", + "minLength": 1 + }, + "port": { + "type": "integer", + "description": "target cluster port", + "maximum": 65535, + "minimum": 0 + } + }, + "required": ["host", "port"] + }, + "update_config": { + "type": "object", + "description": "update config", + "properties": { + "disable_balance_update": { + "type": "boolean", + "description": "disabled balance update flag" + }, + "skip_strict_exist_check": { + "type": "boolean", + "description": "skip strict exists check flag config" + }, + "timestamp": { "type": "string", "description": "index timestamp" } + } + }, + "upsert_config": { + "type": "object", + "description": "upsert config", + "properties": { + "disable_balance_update": { + "type": "boolean", + "description": "disabled balance update flag" + }, + "skip_strict_exist_check": { + "type": "boolean", + "description": "skip strict exists check flag config" + }, + "timestamp": { "type": "string", "description": "index timestamp" } + } + } + } +} diff --git a/charts/vald-benchmark-operator/scenario-values.schema.json b/charts/vald-benchmark-operator/scenario-values.schema.json new file mode 100644 index 0000000000..e3e549e69d --- /dev/null +++ b/charts/vald-benchmark-operator/scenario-values.schema.json @@ -0,0 +1,64 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Values", + "type": "object", + "properties": { + "dataset": { + "type": "object", + "description": "dataset information", + "properties": { + "group": { + "type": "string", + "description": "the hdf5 group name of dataset", + "minLength": 1 + }, + "indexes": { + "type": "integer", + "description": "the amount of indexes", + "minimum": 0 + }, + "name": { + "type": "string", + "description": "the name of dataset", + "enum": ["fashion-mnist"] + }, + "range": { + "type": "object", + "description": "the data range of indexes", + "properties": { + "end": { + "type": "integer", + "description": "end index number", + "minimum": 1 + }, + "start": { + "type": "integer", + "description": "start index number", + "minimum": 1 + } + } + } + }, + "required": ["name", "indexes", "group", "range"] + }, + "jobs": { "type": "array", "items": { "type": "object" } }, + "target": { + "type": "object", + "description": "target cluster location", + "properties": { + "host": { + "type": "string", + "description": "target cluster host", + "minLength": 1 + }, + "port": { + "type": "integer", + "description": "target cluster port", + "maximum": 65535, + "minimum": 0 + } + }, + "required": ["host", "port"] + } + } +} diff --git a/charts/vald-benchmark-operator/schemas/job-values.yaml b/charts/vald-benchmark-operator/schemas/job-values.yaml new file mode 100644 index 0000000000..47fafd98b0 --- /dev/null +++ b/charts/vald-benchmark-operator/schemas/job-values.yaml @@ -0,0 +1,360 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# + +# @schema {"name": "target", "type": "object", "required": ["host", "port"]} +# target -- target cluster location +target: + # @schema {"name": "target.host", "type": "string", "minLength": 1} + # target.host -- target cluster host + host: + "vald-lb-gateway.default.svc.cluster.local" + # @schema {"name": "target.port", "type": "integer", "minimum": 0, "maximum": 65535} + # target.port -- target cluster port + port: 8081 +# @schema {"name": "dataset", "type": "object", "required": ["name", "indexes", "group", "range"]} +# dataset -- dataset information +dataset: + # @schema {"name": "dataset.name", "type": "string", "enum": ["fashion-mnist"] } + # dataset.name -- the name of dataset + name: "fashion-mnist" + # @schema {"name": "dataset.indexes", "type": "integer", "minimum": 0} + # dataset.indexes -- the amount of indexes + indexes: 1000 + # @schema {"name": "dataset.group", "type": "string", "minLength": 1} + # dataset.group -- the hdf5 group name of dataset + group: "test" + # @schema {"name": "dataset.range", "type": "object", "range": ["start", "port"]} + # dataset.range -- the data range of indexes + range: + # @schema {"name": "dataset.range.start", "type": "integer", "minimum": 1} + # dataset.range.start -- start index number + start: 1 + # @schema {"name": "dataset.range.end", "type": "integer", "minimum": 1} + # dataset.range.end -- end index number + end: 1000 +# @schema {"name": "dimension", "type": "integer", "minimum": 1} +# dimension -- vector dimension +dimension: 784 +# @schema {"name": "replica", "type": "integer", "minimum": 1} +# replica -- the number of running concurrency job +replica: 1 +# @schema {"name": "repetition", "type": "integer", "minimum": 1} +# repetition -- the number of repeat job +repetition: 1 +# @schema {"name": "job_type", "type": "string", "enum": ["insert", "update", "upsert", "search", "remove", "get_object", "exists"]} +# job_type -- job type name +job_type: "search" +# @schema {"name": "insert_config", "type": "object"} +# insert_config -- insert config +insert_config: + # @schema {"name": "insert_config.skip_strict_exist_check", "type": "boolean"} + # insert_config.skip_strict_exist_check -- skip strict exists check flag config + skip_strict_exist_check: false + # @schema {"name": "insert_config.timestamp", "type": "string"} + # insert_config.timestamp -- index timestamp + timestamp: "" +# @schema {"name": "update_config", "type": "object"} +# update_config -- update config +update_config: + # @schema {"name": "update_config.skip_strict_exist_check", "type": "boolean"} + # update_config.skip_strict_exist_check -- skip strict exists check flag config + skip_strict_exist_check: false + # @schema {"name": "update_config.timestamp", "type": "string"} + # update_config.timestamp -- index timestamp + timestamp: "" + # @schema {"name": "update_config.disable_balance_update", "type": "boolean"} + # update_config.disable_balance_update -- disabled balance update flag + disable_balance_update: false +# @schema {"name": "upsert_config", "type": "object"} +# upsert_config -- upsert config +upsert_config: + # @schema {"name": "upsert_config.skip_strict_exist_check", "type": "boolean"} + # upsert_config.skip_strict_exist_check -- skip strict exists check flag config + skip_strict_exist_check: false + # @schema {"name": "upsert_config.timestamp", "type": "string"} + # upsert_config.timestamp -- index timestamp + timestamp: "" + # @schema {"name": "upsert_config.disable_balance_update", "type": "boolean"} + # upsert_config.disable_balance_update -- disabled balance update flag + disable_balance_update: false +# @schema {"name": "search_config", "type": "object"} +# search_config -- upsert config +search_config: + # @schema {"name": "search_config.epsilon", "type": "number"} + # search_config.epsilon -- epsilon + epsilon: 0.1 + # @schema {"name": "search_config.radius", "type": "number"} + # search_config.radius -- radius + radius: -1 + # @schema {"name": "search_config.num", "type": "integer"} + # search_config.num -- number of top-k + num: 10 + # @schema {"name": "search_config.min_num", "type": "integer"} + # search_config.min_num -- minimum number of top-k + min_num: 10 + # @schema {"name": "search_config.timeout", "type": "string"} + # search_config.timeout -- search operation timeout + timeout: "10s" + +# @schema {"name": "remove_config", "type": "object"} +# remove_config -- remove config +remove_config: + # @schema {"name": "remove_config.skip_strict_exist_check", "type": "boolean"} + # remove_config.skip_strict_exist_check -- skip strict exists check flag config + skip_strict_exist_check: false + # @schema {"name": "remove_config.timestamp", "type": "string"} + # remove_config.timestamp -- index timestamp + timestamp: "" +# @schema {"name": "object_config", "type": "object"} +# object_config -- object config +object_config: + # @schema {"name": "object_config.filter_config", "type": "object"} + # object_config.filter_config -- filter target config + filter_config: + # @schema {"name": "object_config.filter_config.host", "type": "string"} + # object_config.filter_config.host -- filter target host + host: 0.0.0.0 + # @schema {"name": "object_config.filter_config.host", "type": "integer"} + # object_config.filter_config.port -- filter target host + port: 8081 +# @schema {"name": "client_config", "type": "object"} +# client_config -- gRPC client config for request to the Vald cluster +client_config: + # @schema {"name": "client_config.addrs", "type": "array", "items": {"type": "string"}} + # client_config.addrs -- gRPC client addresses + addrs: [] + # @schema {"name": "client_config.health_check_duration", "type": "string"} + # client_config.health_check_duration -- gRPC client health check duration + health_check_duration: "1s" + # @schema {"name": "client_config.connection_pool", "type": "object"} + connection_pool: + # @schema {"name": "client_config.connection_pool.enable_dns_resolver", "type": "boolean"} + # client_config.connection_pool.enable_dns_resolver -- enables gRPC client connection pool dns resolver, when enabled vald uses ip handshake exclude dns discovery which improves network performance + enable_dns_resolver: true + # @schema {"name": "client_config.connection_pool.enable_rebalance", "type": "boolean"} + # client_config.connection_pool.enable_rebalance -- enables gRPC client connection pool rebalance + enable_rebalance: true + # @schema {"name": "client_config.connection_pool.rebalance_duration", "type": "string"} + # client_config.connection_pool.rebalance_duration -- gRPC client connection pool rebalance duration + rebalance_duration: 30m + # @schema {"name": "client_config.connection_pool.size", "type": "integer"} + # client_config.connection_pool.size -- gRPC client connection pool size + size: 3 + # @schema {"name": "client_config.connection_pool.old_conn_close_duration", "type": "string"} + # client_config.connection_pool.old_conn_close_duration -- makes delay before gRPC client connection closing during connection pool rebalance + old_conn_close_duration: "2m" + # @schema {"name": "client_config.backoff", "type": "object", "anchor": "backoff"} + backoff: + # @schema {"name": "client_config.backoff.initial_duration", "type": "string"} + # client_config.backoff.initial_duration -- gRPC client backoff initial duration + initial_duration: 5ms + # @schema {"name": "client_config.backoff.backoff_time_limit", "type": "string"} + # client_config.backoff.backoff_time_limit -- gRPC client backoff time limit + backoff_time_limit: 5s + # @schema {"name": "client_config.backoff.maximum_duration", "type": "string"} + # client_config.backoff.maximum_duration -- gRPC client backoff maximum duration + maximum_duration: 5s + # @schema {"name": "client_config.backoff.jitter_limit", "type": "string"} + # client_config.backoff.jitter_limit -- gRPC client backoff jitter limit + jitter_limit: 100ms + # @schema {"name": "client_config.backoff.backoff_factor", "type": "number"} + # client_config.backoff.backoff_factor -- gRPC client backoff factor + backoff_factor: 1.1 + # @schema {"name": "client_config.backoff.retry_count", "type": "integer"} + # client_config.backoff.retry_count -- gRPC client backoff retry count + retry_count: 100 + # @schema {"name": "client_config.backoff.enable_error_log", "type": "boolean"} + # client_config.backoff.enable_error_log -- gRPC client backoff log enabled + enable_error_log: true + # @schema {"name": "client_config.circuit_breaker", "type": "object"} + circuit_breaker: + # @schema {"name": "client_config.circuit_breaker.closed_error_rate", "type": "number"} + # client_config.circuit_breaker.closed_error_rate -- gRPC client circuitbreaker closed error rate + closed_error_rate: 0.7 + # @schema {"name": "client_config.circuit_breaker.half_open_error_rate", "type": "number"} + # client_config.circuit_breaker.half_open_error_rate -- gRPC client circuitbreaker half-open error rate + half_open_error_rate: 0.5 + # @schema {"name": "client_config.circuit_breaker.min_samples", "type": "integer"} + # client_config.circuit_breaker.min_samples -- gRPC client circuitbreaker minimum sampling count + min_samples: 1000 + # @schema {"name": "client_config.circuit_breaker.open_timeout", "type": "string"} + # client_config.circuit_breaker.open_timeout -- gRPC client circuitbreaker open timeout + open_timeout: "1s" + # @schema {"name": "client_config.circuit_breaker.closed_refresh_timeout", "type": "string"} + # client_config.circuit_breaker.closed_refresh_timeout -- gRPC client circuitbreaker closed refresh timeout + closed_refresh_timeout: "10s" + # @schema {"name": "client_config.call_option", "type": "object"} + call_option: + # @schema {"name": "client_config.wait_for_ready", "type": "boolean"} + # client_config.call_option.wait_for_ready -- gRPC client call option wait for ready + wait_for_ready: true + # @schema {"name": "client_config.max_retry_rpc_buffer_size", "type": "integer"} + # client_config.call_option.max_retry_rpc_buffer_size -- gRPC client call option max retry rpc buffer size + max_retry_rpc_buffer_size: 0 + # @schema {"name": "client_config.max_recv_msg_size", "type": "integer"} + # client_config.call_option.max_recv_msg_size -- gRPC client call option max receive message size + max_recv_msg_size: 0 + # @schema {"name": "client_config.max_send_msg_size", "type": "integer"} + # client_config.call_option.max_send_msg_size -- gRPC client call option max send message size + max_send_msg_size: 0 + # @schema {"name": "client_config.dial_option", "type": "object"} + dial_option: + # @schema {"name": "client_config.dial_option.write_buffer_size", "type": "integer"} + # client_config.dial_option.write_buffer_size -- gRPC client dial option write buffer size + write_buffer_size: 0 + # @schema {"name": "client_config.dial_option.read_buffer_size", "type": "integer"} + # client_config.dial_option.read_buffer_size -- gRPC client dial option read buffer size + read_buffer_size: 0 + # @schema {"name": "client_config.dial_option.initial_window_size", "type": "integer"} + # client_config.dial_option.initial_window_size -- gRPC client dial option initial window size + initial_window_size: 0 + # @schema {"name": "client_config.dial_option.initial_connection_window_size", "type": "integer"} + # client_config.dial_option.initial_connection_window_size -- gRPC client dial option initial connection window size + initial_connection_window_size: 0 + # @schema {"name": "client_config.dial_option.max_msg_size", "type": "integer"} + # client_config.dial_option.max_msg_size -- gRPC client dial option max message size + max_msg_size: 0 + # @schema {"name": "client_config.dial_option.backoff_max_delay", "type": "string"} + # client_config.dial_option.backoff_max_delay -- gRPC client dial option max backoff delay + backoff_max_delay: "120s" + # @schema {"name": "client_config.dial_option.backoff_base_delay", "type": "string"} + # client_config.dial_option.backoff_base_delay -- gRPC client dial option base backoff delay + backoff_base_delay: "1s" + # @schema {"name": "client_config.dial_option.backoff_multiplier", "type": "number"} + # client_config.dial_option.backoff_multiplier -- gRPC client dial option base backoff delay + backoff_multiplier: 1.6 + # @schema {"name": "client_config.dial_option.backoff_jitter", "type": "number"} + # client_config.dial_option.backoff_jitter -- gRPC client dial option base backoff delay + backoff_jitter: 0.2 + # @schema {"name": "client_config.dial_option.min_connection_timeout", "type": "string"} + # client_config.dial_option.min_connection_timeout -- gRPC client dial option minimum connection timeout + min_connection_timeout: "20s" + # @schema {"name": "client_config.dial_option.enable_backoff", "type": "boolean"} + # client_config.dial_option.enable_backoff -- gRPC client dial option backoff enabled + enable_backoff: false + # @schema {"name": "client_config.dial_option.insecure", "type": "boolean"} + # client_config.dial_option.insecure -- gRPC client dial option insecure enabled + insecure: true + # @schema {"name": "client_config.dial_option.timeout", "type": "string"} + # client_config.dial_option.timeout -- gRPC client dial option timeout + timeout: "" + # @schema {"name": "client_config.dial_option.interceptors", "type": "array", "items": {"type": "string", "enum": ["TraceInterceptor"]}} + # client_config.dial_option.interceptors -- gRPC client interceptors + interceptors: [] + # @schema {"name": "client_config.dial_option.net", "type": "object", "anchor": "net"} + net: + # @schema {"name": "client_config.dial_option.net.dns", "type": "object"} + dns: + # @schema {"name": "client_config.dial_option.net.dns.cache_enabled", "type": "boolean"} + # client_config.dial_option.net.dns.cache_enabled -- gRPC client TCP DNS cache enabled + cache_enabled: true + # @schema {"name": "client_config.dial_option.net.dns.refresh_duration", "type": "string"} + # client_config.dial_option.net.dns.refresh_duration -- gRPC client TCP DNS cache refresh duration + refresh_duration: 30m + # @schema {"name": "client_config.dial_option.net.dns.cache_expiration", "type": "string"} + # client_config.dial_option.net.dns.cache_expiration -- gRPC client TCP DNS cache expiration + cache_expiration: 1h + # @schema {"name": "client_config.dial_option.net.dialer", "type": "object"} + dialer: + # @schema {"name": "client_config.dial_option.net.dialer.timeout", "type": "string"} + # client_config.dial_option.net.dialer.timeout -- gRPC client TCP dialer timeout + timeout: "" + # @schema {"name": "client_config.dial_option.net.dialer.keepalive", "type": "string"} + # client_config.dial_option.net.dialer.keepalive -- gRPC client TCP dialer keep alive + keepalive: "" + # @schema {"name": "client_config.dial_option.net.dialer.dual_stack_enabled", "type": "boolean"} + # client_config.dial_option.net.dialer.dual_stack_enabled -- gRPC client TCP dialer dual stack enabled + dual_stack_enabled: true + # @schema {"name": "client_config.dial_option.net.tls", "type": "object"} + tls: + # @schema {"name": "client_config.dial_option.net.tls.enabled", "type": "boolean"} + # client_config.tls.enabled -- TLS enabled + enabled: false + # @schema {"name": "client_config.dial_option.net.tls.cert", "type": "string"} + # client_config.tls.cert -- TLS cert path + cert: /path/to/cert + # @schema {"name": "client_config.dial_option.net.tls.key", "type": "string"} + # client_config.tls.key -- TLS key path + key: /path/to/key + # @schema {"name": "client_config.dial_option.net.tls.ca", "type": "string"} + # client_config.tls.ca -- TLS ca path + ca: /path/to/ca + # @schema {"name": "client_config.dial_option.net.tls.insecure_skip_verify", "type": "boolean"} + # client_config.tls.insecure_skip_verify -- enable/disable skip SSL certificate verification + insecure_skip_verify: false + # @schema {"name": "client_config.dial_option.net.socket_option", "type": "object"} + socket_option: + # @schema {"name": "client_config.dial_option.net.socket_option.reuse_port", "type": "boolean"} + # client_config.dial_option.net.socket_option.reuse_port -- server listen socket option for reuse_port functionality + reuse_port: true + # @schema {"name": "client_config.dial_option.net.socket_option.reuse_addr", "type": "boolean"} + # client_config.dial_option.net.socket_option.reuse_addr -- server listen socket option for reuse_addr functionality + reuse_addr: true + # @schema {"name": "client_config.dial_option.net.socket_option.tcp_fast_open", "type": "boolean"} + # client_config.dial_option.net.socket_option.tcp_fast_open -- server listen socket option for tcp_fast_open functionality + tcp_fast_open: true + # @schema {"name": "client_config.dial_option.net.socket_option.tcp_no_delay", "type": "boolean"} + # client_config.dial_option.net.socket_option.tcp_no_delay -- server listen socket option for tcp_no_delay functionality + tcp_no_delay: true + # @schema {"name": "client_config.dial_option.net.socket_option.tcp_cork", "type": "boolean"} + # client_config.dial_option.net.socket_option.tcp_cork -- server listen socket option for tcp_cork functionality + tcp_cork: false + # @schema {"name": "client_config.dial_option.net.socket_option.tcp_quick_ack", "type": "boolean"} + # client_config.dial_option.net.socket_option.tcp_quick_ack -- server listen socket option for tcp_quick_ack functionality + tcp_quick_ack: true + # @schema {"name": "client_config.dial_option.net.socket_option.tcp_defer_accept", "type": "boolean"} + # client_config.dial_option.net.socket_option.tcp_defer_accept -- server listen socket option for tcp_defer_accept functionality + tcp_defer_accept: true + # @schema {"name": "client_config.dial_option.net.socket_option.ip_transparent", "type": "boolean"} + # client_config.dial_option.net.socket_option.ip_transparent -- server listen socket option for ip_transparent functionality + ip_transparent: false + # @schema {"name": "client_config.dial_option.net.socket_option.ip_recover_destination_addr", "type": "boolean"} + # client_config.dial_option.net.socket_option.ip_recover_destination_addr -- server listen socket option for ip_recover_destination_addr functionality + ip_recover_destination_addr: false + # @schema {"name": "client_config.dial_option.keepalive", "type": "object"} + keepalive: + # @schema {"name": "client_config.dial_option.keepalive.time", "type": "string"} + # client_config.dial_option.keepalive.time -- gRPC client keep alive time + time: "120s" + # @schema {"name": "client_config.dial_option.keepalive.timeout", "type": "string"} + # client_config.dial_option.keepalive.timeout -- gRPC client keep alive timeout + timeout: "30s" + # @schema {"name": "client_config.dial_option.keepalive.permit_without_stream", "type": "boolean"} + # client_config.dial_option.keepalive.permit_without_stream -- gRPC client keep alive permit without stream + permit_without_stream: true + # @schema {"name": "client_config.tls", "type": "object"} + tls: + # @schema {"name": "client_config.tls.enabled", "type": "boolean"} + # client_config.tls.enabled -- TLS enabled + enabled: false + # @schema {"name": "client_config.tls.cert", "type": "string"} + # client_config.tls.cert -- TLS cert path + cert: /path/to/cert + # @schema {"name": "client_config.tls.key", "type": "string"} + # client_config.tls.key -- TLS key path + key: /path/to/key + # @schema {"name": "client_config.tls.ca", "type": "string"} + # client_config.tls.ca -- TLS ca path + ca: /path/to/ca + # @schema {"name": "client_config.tls.insecure_skip_verify", "type": "boolean"} + # client_config.tls.insecure_skip_verify -- enable/disable skip SSL certificate verification + insecure_skip_verify: false +# @schema {"name": "rules", "type": "array", "items": {"type": "string"}} +# rules -- executing rule +rules: [] +# @schema {"name": "rps", "type": "integer", "minimum": 0, "maximum": 65535} +# rps -- desired request per sec +rps: 1000 diff --git a/charts/vald-benchmark-operator/schemas/scenario-values.yaml b/charts/vald-benchmark-operator/schemas/scenario-values.yaml new file mode 100644 index 0000000000..dedc6b51b5 --- /dev/null +++ b/charts/vald-benchmark-operator/schemas/scenario-values.yaml @@ -0,0 +1,169 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# + +# @schema {"name": "target", "type": "object", "required": ["host", "port"]} +# target -- target cluster location +target: + # @schema {"name": "target.host", "type": "string", "minLength": 1} + # target.host -- target cluster host + host: + "vald-lb-gateway.default.svc.cluster.local" + # @schema {"name": "target.port", "type": "integer", "minimum": 0, "maximum": 65535} + # target.port -- target cluster port + port: 8081 + +# @schema {"name": "dataset", "type": "object", "required": ["name", "indexes", "group", "range"]} +# dataset -- dataset information +dataset: + # @schema {"name": "dataset.name", "type": "string", "enum": ["fashion-mnist"] } + # dataset.name -- the name of dataset + name: "fashion-mnist" + # @schema {"name": "dataset.indexes", "type": "integer", "minimum": 0} + # dataset.indexes -- the amount of indexes + indexes: 1000 + # @schema {"name": "dataset.group", "type": "string", "minLength": 1} + # dataset.group -- the hdf5 group name of dataset + group: "test" + # @schema {"name": "dataset.range", "type": "object", "range": ["start", "port"]} + # dataset.range -- the data range of indexes + range: + # @schema {"name": "dataset.range.start", "type": "integer", "minimum": 1} + # dataset.range.start -- start index number + start: 1 + # @schema {"name": "dataset.range.end", "type": "integer", "minimum": 1} + # dataset.range.end -- end index number + end: 1000 + +# @schema {"name": "jobs", "type": "array", "items": {"type": "object"}} +jobs: + - target: + host: "vald-lb-gateway.default.svc.cluster.local" + port: 8081 + dataset: + name: "fashion-mnist" + indexes: 1000 + group: "test" + range: + start: 1 + end: 1000 + dimension: 784 + replica: 1 + repetition: 1 + job_type: "search" + insert_config: + skip_strict_exist_check: false + timestamp: "" + update_config: + skip_strict_exist_check: false + timestamp: "" + disable_balance_update: false + upsert_config: + skip_strict_exist_check: false + timestamp: "" + disable_balance_update: false + search_config: + epsilon: 0.1 + radius: -1 + num: 10 + min_num: 10 + timeout: "10s" + remove_config: + skip_strict_exist_check: false + timestamp: "" + object_config: + filter_config: + host: 0.0.0.0 + port: 8081 + client_config: + addrs: [] + health_check_duration: "1s" + connection_pool: + enable_dns_resolver: true + enable_rebalance: true + rebalance_duration: 30m + size: 3 + old_conn_close_duration: "2m" + backoff: + initial_duration: 5ms + backoff_time_limit: 5s + maximum_duration: 5s + jitter_limit: 100ms + backoff_factor: 1.1 + retry_count: 100 + enable_error_log: true + circuit_breaker: + closed_error_rate: 0.7 + half_open_error_rate: 0.5 + min_samples: 1000 + open_timeout: "1s" + closed_refresh_timeout: "10s" + call_option: + wait_for_ready: true + max_retry_rpc_buffer_size: 0 + max_recv_msg_size: 0 + max_send_msg_size: 0 + dial_option: + write_buffer_size: 0 + read_buffer_size: 0 + initial_window_size: 0 + initial_connection_window_size: 0 + max_msg_size: 0 + backoff_max_delay: "120s" + backoff_base_delay: "1s" + backoff_multiplier: 1.6 + backoff_jitter: 0.2 + min_connection_timeout: "20s" + enable_backoff: false + insecure: true + timeout: "" + interceptors: [] + net: + dns: + cache_enabled: true + refresh_duration: 30m + cache_expiration: 1h + dialer: + timeout: "" + keepalive: "" + dual_stack_enabled: true + tls: + enabled: false + cert: /path/to/cert + key: /path/to/key + ca: /path/to/ca + insecure_skip_verify: false + socket_option: + reuse_port: true + reuse_addr: true + tcp_fast_open: true + tcp_no_delay: true + tcp_cork: false + tcp_quick_ack: true + tcp_defer_accept: true + ip_transparent: false + ip_recover_destination_addr: false + keepalive: + time: "120s" + timeout: "30s" + permit_without_stream: true + tls: + enabled: false + cert: /path/to/cert + key: /path/to/key + ca: /path/to/ca + insecure_skip_verify: false + rules: [] + rps: 1000 diff --git a/charts/vald-benchmark-operator/templates/NOTES.txt b/charts/vald-benchmark-operator/templates/NOTES.txt new file mode 100644 index 0000000000..8dff4bbfb1 --- /dev/null +++ b/charts/vald-benchmark-operator/templates/NOTES.txt @@ -0,0 +1 @@ +Release {{ .Release.Name }} is created. diff --git a/charts/vald-benchmark-operator/templates/_helpers.tpl b/charts/vald-benchmark-operator/templates/_helpers.tpl new file mode 100644 index 0000000000..42ae6d7c72 --- /dev/null +++ b/charts/vald-benchmark-operator/templates/_helpers.tpl @@ -0,0 +1,310 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "vald.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "vald.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "vald.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "vald.labels" -}} +app.kubernetes.io/name: {{ include "vald.name" . }} +helm.sh/chart: {{ include "vald.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +joinListWithSpace +*/}} +{{- define "vald.utils.joinListWithSpace" -}} +{{- $local := dict "first" true -}} +{{- range $k, $v := . -}}{{- if not $local.first -}}{{- " " -}}{{- end -}}{{- $v -}}{{- $_ := set $local "first" false -}}{{- end -}} +{{- end -}} + +{{/* +joinListWithComma +*/}} +{{- define "vald.utils.joinListWithComma" -}} +{{- $local := dict "first" true -}} +{{- range $k, $v := . -}}{{- if not $local.first -}},{{- end -}}{{- $v -}}{{- $_ := set $local "first" false -}}{{- end -}} +{{- end -}} + +{{/* +logging settings +*/}} +{{- define "vald.logging"}} +{{- if .Values -}} +logger: {{ .Values.logger | quote }} +level: {{ .Values.level | quote }} +format: {{ .Values.format | quote }} +{{- end }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "vald.selectorLabels" -}} +app.kubernetes.io/name: {{ include "vald.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "vald.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "vald.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end -}} + +{{/* +Server configures that inserted into server_config +*/}} +{{- define "vald.servers" -}} +servers: + {{- $restEnabled := false }} + {{- if hasKey .Values.servers.rest "enabled" }} + {{- $restEnabled = .Values.servers.rest.enabled }} + {{- end }} + {{- if $restEnabled }} + - name: rest + host: {{ .Values.servers.rest.host }} + port: {{ .Values.servers.rest.port }} + {{- if .Values.servers.rest.server }} + mode: {{ .Values.servers.rest.server.mode }} + probe_wait_time: {{ .Values.servers.rest.server.probe_wait_time }} + network: {{ .Values.servers.rest.server.network | quote }} + socket_path: {{ .Values.servers.rest.server.socket_path | quote }} + http: + {{- if .Values.servers.rest.server.http }} + shutdown_duration: {{ .Values.servers.rest.server.http.shutdown_duration }} + handler_timeout: {{.Values.servers.rest.server.http.handler_timeout }} + idle_timeout: {{ .Values.servers.rest.server.http.idle_timeout }} + read_header_timeout: {{ .Values.servers.rest.server.http.read_header_timeout }} + read_timeout: {{ .Values.servers.rest.server.http.read_timeout }} + write_timeout: {{ .Values.servers.rest.server.http.write_timeout }} + {{- end }} + {{- end }} + {{- end }} + {{- $grpcEnabled := false }} + {{- if hasKey .Values.servers.grpc "enabled" }} + {{- $grpcEnabled = .Values.servers.grpc.enabled }} + {{- end }} + {{- if $grpcEnabled }} + - name: grpc + host: {{ .Values.servers.grpc.host }} + port: {{ .Values.servers.grpc.port }} + {{- if .Values.servers.grpc.server }} + mode: {{ .Values.servers.grpc.server.mode }} + probe_wait_time: {{ .Values.servers.grpc.server.probe_wait_time | quote }} + network: {{ .Values.servers.grpc.server.network | quote }} + socket_path: {{ .Values.servers.grpc.server.socket_path | quote }} + grpc: + {{- if .Values.servers.grpc.server.grpc }} + bidirectional_stream_concurrency: {{ .Values.servers.grpc.server.grpc.bidirectional_stream_concurrency }} + connection_timeout: {{ .Values.servers.grpc.server.grpc.connection_timeout | quote }} + max_receive_message_size: {{ .Values.servers.grpc.server.grpc.max_receive_message_size }} + max_send_message_size: {{ .Values.servers.grpc.server.grpc.max_send_message_size }} + initial_window_size: {{ .Values.servers.grpc.server.grpc.initial_window_size }} + initial_conn_window_size: {{ .Values.servers.grpc.server.grpc.initial_conn_window_size }} + keepalive: + {{- if .Values.servers.grpc.server.grpc.keepalive }} + max_conn_idle: {{ .Values.servers.grpc.server.grpc.keepalive.max_conn_idle | quote }} + max_conn_age: {{ .Values.servers.grpc.server.grpc.keepalive.max_conn_age | quote }} + max_conn_age_grace: {{ .Values.servers.grpc.server.grpc.keepalive.max_conn_age_grace | quote }} + time: {{ .Values.servers.grpc.server.grpc.keepalive.time | quote }} + timeout: {{ .Values.servers.grpc.server.grpc.keepalive.timeout | quote }} + min_time: {{ .Values.servers.grpc.server.grpc.keepalive.min_time | quote }} + permit_without_stream: {{ .Values.servers.grpc.server.grpc.keepalive.permit_without_stream }} + {{- end }} + write_buffer_size: {{ .Values.servers.grpc.server.grpc.write_buffer_size }} + read_buffer_size: {{ .Values.servers.grpc.server.grpc.read_buffer_size }} + max_header_list_size: {{ .Values.servers.grpc.server.grpc.max_header_list_size }} + header_table_size: {{ .Values.servers.grpc.server.grpc.header_table_size }} + {{- if .Values.servers.grpc.server.grpc.interceptors }} + interceptors: + {{- toYaml .Values.servers.grpc.server.grpc.interceptors | nindent 8 }} + {{- else }} + interceptors: [] + {{- end }} + enable_reflection: {{ .Values.servers.grpc.server.grpc.enable_reflection }} + {{- end }} + restart: {{ .Values.servers.grpc.server.restart }} + {{- end }} + {{- end }} +health_check_servers: + {{- $livenessEnabled := false }} + {{- if hasKey .Values.healths.liveness "enabled" }} + {{- $livenessEnabled = .Values.healths.liveness.enabled }} + {{- end }} + {{- if $livenessEnabled }} + - name: liveness + host: {{ .Values.healths.liveness.host }} + port: {{ .Values.healths.liveness.port }} + {{- if .Values.healths.liveness.server }} + mode: {{ .Values.healths.liveness.server.mode | quote }} + probe_wait_time: {{ .Values.healths.liveness.server.probe_wait_time | quote }} + network: {{ .Values.healths.liveness.server.network | quote }} + socket_path: {{ .Values.healths.liveness.server.socket_path | quote }} + http: + {{- if .Values.healths.liveness.server.http }} + shutdown_duration: {{ .Values.healths.liveness.server.http.shutdown_duration | quote }} + handler_timeout: {{ .Values.healths.liveness.server.http.handler_timeout | quote }} + idle_timeout: {{ .Values.healths.liveness.server.http.idle_timeout | quote }} + read_header_timeout: {{ .Values.healths.liveness.server.http.read_header_timeout | quote }} + read_timeout: {{ .Values.healths.liveness.server.http.read_timeout | quote }} + write_timeout: {{ .Values.healths.liveness.server.http.write_timeout | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- $readinessEnabled := false }} + {{- if hasKey .Values.healths.readiness "enabled" }} + {{- $readinessEnabled = .Values.healths.readiness.enabled }} + {{- end }} + {{- if $readinessEnabled }} + - name: readiness + host: {{ .Values.healths.readiness.host }} + port: {{ .Values.healths.readiness.port }} + {{- if .Values.healths.readiness.server }} + mode: {{ .Values.healths.readiness.server.mode | quote }} + probe_wait_time: {{ .Values.healths.readiness.server.probe_wait_time | quote }} + network: {{ .Values.healths.readiness.server.network | quote }} + socket_path: {{ .Values.healths.readiness.server.socket_path | quote }} + http: + {{- if .Values.healths.readiness.server.http }} + shutdown_duration: {{ .Values.healths.readiness.server.http.shutdown_duration | quote }} + handler_timeout: {{ .Values.healths.readiness.server.http.handler_timeout | quote }} + idle_timeout: {{ .Values.healths.readiness.server.http.idle_timeout | quote }} + read_header_timeout: {{ .Values.healths.readiness.server.http.read_header_timeout | quote }} + read_timeout: {{ .Values.healths.readiness.server.http.read_timeout | quote }} + write_timeout: {{ .Values.healths.readiness.server.http.write_timeout | quote }} + {{- end }} + {{- end }} + {{- end }} +metrics_servers: + {{- $pprofEnabled := false }} + {{- if hasKey .Values.metrics.pprof "enabled" }} + {{- $pprofEnabled = .Values.metrics.pprof.enabled }} + {{- end }} + {{- if $pprofEnabled }} + - name: pprof + host: {{ .Values.metrics.pprof.host }} + port: {{ .Values.metrics.pprof.port }} + {{- if .Values.metrics.pprof.server }} + mode: {{ .Values.metrics.pprof.server.mode }} + probe_wait_time: {{ .Values.metrics.pprof.server.probe_wait_time }} + network: {{ .Values.metrics.pprof.server.network | quote }} + socket_path: {{ .Values.metrics.pprof.server.socket_path | quote }} + http: + {{- if .Values.metrics.pprof.server.http }} + shutdown_duration: {{ .Values.metrics.pprof.server.http.shutdown_duration }} + handler_timeout: {{ .Values.metrics.pprof.server.http.handler_timeout }} + idle_timeout: {{ .Values.metrics.pprof.server.http.idle_timeout }} + read_header_timeout: {{ .Values.metrics.pprof.server.http.read_header_timeout }} + read_timeout: {{ .Values.metrics.pprof.server.http.read_timeout }} + write_timeout: {{ .Values.metrics.pprof.server.http.write_timeout }} + {{- end }} + {{- end }} + {{- end }} +startup_strategy: + {{- if $livenessEnabled }} + - liveness + {{- end }} + {{- if $pprofEnabled }} + - pprof + {{- end }} + {{- if $grpcEnabled }} + - grpc + {{- end }} + {{- if $restEnabled }} + - rest + {{- end }} + {{- if $readinessEnabled }} + - readiness + {{- end }} +full_shutdown_duration: {{ .Values.full_shutdown_duration }} +tls: + {{- if .Values.tls }} + enabled: {{ .Values.tls.enabled }} + cert: {{ .Values.tls.cert | quote }} + key: {{ .Values.tls.key | quote }} + ca: {{ .Values.tls.ca | quote }} + insecure_skip_verify: {{ .Values.tls.insecure_skip_verify }} + {{- end }} +{{- end -}} + +{{/* +observability +*/}} +{{- define "vald.observability" -}} +enabled: {{ .Values.enabled }} +otlp: + {{- if .Values.otlp }} + collector_endpoint: {{ .Values.otlp.collector_endpoint | quote }} + trace_batch_timeout: {{ .Values.otlp.trace_batch_timeout | quote }} + trace_export_timeout: {{ .Values.otlp.trace_export_timeout | quote }} + trace_max_export_batch_size: {{ .Values.otlp.trace_max_export_batch_size }} + trace_max_queue_size: {{ .Values.otlp.trace_max_queue_size }} + metrics_export_interval: {{ .Values.otlp.metrics_export_interval | quote }} + metrics_export_timeout: {{ .Values.otlp.metrics_export_timeout | quote }} + attribute: + {{- if .Values.otlp.attribute }} + namespace: {{ .Values.otlp.attribute.namespace | quote }} + pod_name: {{ .Values.otlp.attribute.pod_name | quote }} + node_name: {{ .Values.otlp.attribute.node_name | quote }} + service_name: {{ .Values.otlp.attribute.service_name | quote }} + {{- end }} + {{- end }} +metrics: + {{- if .Values.metrics }} + enable_version_info: {{ .Values.metrics.enable_version_info }} + {{- if .Values.metrics.version_info_labels }} + version_info_labels: + {{- toYaml .Values.metrics.version_info_labels | nindent 4 }} + {{- else }} + version_info_labels: [] + {{- end }} + enable_memory: {{ .Values.metrics.enable_memory }} + enable_goroutine: {{ .Values.metrics.enable_goroutine }} + enable_cgo: {{ .Values.metrics.enable_cgo }} + {{- end }} +trace: + {{- if .Values.trace }} + enabled: {{ .Values.trace.enabled }} + sampling_rate: {{ .Values.trace.sampling_rate }} + {{- end }} +{{- end -}} + diff --git a/charts/vald-benchmark-operator/templates/clusterrole.yaml b/charts/vald-benchmark-operator/templates/clusterrole.yaml new file mode 100644 index 0000000000..63fc0c6812 --- /dev/null +++ b/charts/vald-benchmark-operator/templates/clusterrole.yaml @@ -0,0 +1,115 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: {{ .Values.rbac.name }} + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - vald.vdaas.org + resources: + - valdbenchmarkscenarios + - valdbenchmarkscenario + - valdbenchmarkjob + - valdbenchmarkjobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - vald.vdaas.org + resources: + - valdbenchmarkscenarios/finalizers + - valdbenchmarkscenario/finalizers + - valdbenchmarkjob/finalizers + - valdbenchmarkjobs/finalizers + verbs: + - update + - apiGroups: + - vald.vdaas.org + resources: + - valdbenchmarkscenario/status + - valdbenchmarkscenarios/status + - valdbenchmarkjob/status + - valdbenchmarkjobs/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - update + - apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +{{- end }} diff --git a/charts/vald-benchmark-operator/templates/clusterrolebinding.yaml b/charts/vald-benchmark-operator/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..5130f14292 --- /dev/null +++ b/charts/vald-benchmark-operator/templates/clusterrolebinding.yaml @@ -0,0 +1,30 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +{{- if .Values.rbac.create }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Values.serviceAccount.name }} + namespace: {{ .Release.Namespace }} +subjects: + - kind: ServiceAccount + name: {{ .Values.serviceAccount.name }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ .Values.rbac.name }} + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/charts/vald-benchmark-operator/templates/configmap.yaml b/charts/vald-benchmark-operator/templates/configmap.yaml new file mode 100644 index 0000000000..ceb92f2b55 --- /dev/null +++ b/charts/vald-benchmark-operator/templates/configmap.yaml @@ -0,0 +1,40 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.name }}-config + labels: + app.kubernetes.io/name: {{ include "vald.name" . }} + helm.sh/chart: {{ include "vald.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/version: {{ .Chart.Version }} + app.kubernetes.io/component: benchmark-operator +data: + config.yaml: | + --- + version: {{ .Values.version }} + time_zone: {{ .Values.time_zone | quote }} + logging: + {{- $logging := dict "Values" .Values.logging }} + {{- include "vald.logging" $logging | nindent 6 }} + server_config: + {{- $servers := dict "Values" .Values.server_config }} + {{- include "vald.servers" $servers | nindent 6 }} + observability: + {{- $observability := dict "Values" .Values.observability}} + {{- include "vald.observability" $observability | nindent 6 }} diff --git a/charts/vald-benchmark-operator/templates/deployment.yaml b/charts/vald-benchmark-operator/templates/deployment.yaml new file mode 100644 index 0000000000..bff799a214 --- /dev/null +++ b/charts/vald-benchmark-operator/templates/deployment.yaml @@ -0,0 +1,152 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.name }} + namespace: {{ .Release.Namespace }} + labels: + app: {{ .Values.name }} + app.kubernetes.io/name: {{ include "vald.name" . }} + helm.sh/chart: {{ include "vald.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/version: {{ .Chart.Version }} + app.kubernetes.io/component: benchmark-operator + {{- if .Values.annotations }} + annotations: + {{- toYaml .Values.annotations | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + name: {{ .Values.name }} + template: + metadata: + labels: + name: {{ .Values.name }} + app.kubernetes.io/name: {{ include "vald.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: benchmark-operator + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ .Values.serviceAccount.name }} + containers: + - name: {{ .Values.name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- $liveness := .Values.server_config.healths.liveness }} + {{- if $liveness.enabled }} + livenessProbe: + failureThreshold: {{ $liveness.livenessProbe.failureThreshold }} + httpGet: + path: {{ $liveness.livenessProbe.httpGet.path }} + port: {{ $liveness.livenessProbe.httpGet.port }} + scheme: {{ $liveness.livenessProbe.httpGet.scheme }} + initialDelaySeconds: {{ $liveness.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ $liveness.livenessProbe.periodSeconds }} + successThreshold: {{ $liveness.livenessProbe.successThreshold }} + timeoutSeconds: {{ $liveness.livenessProbe.timeoutSeconds }} + {{- end}} + {{- $readiness := .Values.server_config.healths.readiness }} + {{- if $readiness.enabled }} + readinessProbe: + failureThreshold: {{ $readiness.readinessProbe.failureThreshold }} + httpGet: + path: {{ $readiness.readinessProbe.httpGet.path }} + port: {{ $readiness.readinessProbe.httpGet.port }} + scheme: {{ $readiness.readinessProbe.httpGet.scheme }} + initialDelaySeconds: {{ $readiness.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ $readiness.readinessProbe.periodSeconds }} + successThreshold: {{ $readiness.readinessProbe.successThreshold }} + timeoutSeconds: {{ $readiness.readinessProbe.timeoutSeconds }} + {{- end}} + {{- $startup := .Values.server_config.healths.startup }} + {{- if $startup.enabled }} + startupProbe: + failureThreshold: {{ $startup.startupProbe.failureThreshold }} + httpGet: + path: {{ $startup.startupProbe.httpGet.path }} + port: {{ $startup.startupProbe.httpGet.port }} + scheme: {{ $startup.startupProbe.httpGet.scheme }} + initialDelaySeconds: {{ $startup.startupProbe.initialDelaySeconds }} + periodSeconds: {{ $startup.startupProbe.periodSeconds }} + successThreshold: {{ $startup.startupProbe.successThreshold }} + timeoutSeconds: {{ $startup.startupProbe.timeoutSeconds }} + {{- end}} + ports: + {{- if $liveness.enabled }} + - name: liveness + protocol: TCP + containerPort: {{ $liveness.port }} + {{- end}} + {{- if $readiness.enabled }} + - name: readiness + protocol: TCP + containerPort: {{ $readiness.port }} + {{- end}} + - name: grpc + protocol: TCP + containerPort: {{ default 8081 .Values.grpc }} + - name: pprof + protocol: TCP + containerPort: {{ default 6060 .Values.pprof }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: {{ .Values.name }}-config + mountPath: /etc/server + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + restartPolicy: Always + volumes: + - name: {{ .Values.name }}-config + configMap: + defaultMode: 420 + name: {{ .Values.name }}-config + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + + {{- if .Values.nodeName }} + nodeName: {{ .Values.nodeName }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: + {{- toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: + {{- toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.podPriority }} + {{- if .Values.podPriority.enabled }} + priorityClassName: {{ .Release.Namespace }}-{{ .Values.name }}-priority + {{- end }} + {{- end }} diff --git a/charts/vald-benchmark-operator/templates/service.yaml b/charts/vald-benchmark-operator/templates/service.yaml new file mode 100644 index 0000000000..0be6a1a882 --- /dev/null +++ b/charts/vald-benchmark-operator/templates/service.yaml @@ -0,0 +1,51 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +{{- if .Values.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.name }} + {{- if .Values.service.annotations }} + annotations: + {{- toYaml .Values.service.annotations | nindent 4 }} + {{- end }} + labels: + app.kubernetes.io/name: {{ include "vald.name" . }} + helm.sh/chart: {{ include "vald.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/version: {{ .Chart.Version }} + app.kubernetes.io/component: helm-operator + {{- if .Values.service.labels }} + {{- toYaml .Values.service.labels | nindent 4 }} + {{- end }} +spec: + ports: + - name: prometheus + port: {{ .Values.server_config.metrics.pprof.port }} + targetPort: {{ .Values.server_config.metrics.pprof.port }} + protocol: TCP + selector: + app.kubernetes.io/name: {{ include "vald.name" . }} + app.kubernetes.io/component: helm-operator + {{- if eq .Values.service.type "ClusterIP" }} + clusterIP: None + {{- end }} + type: {{ .Values.service.type }} + {{- if .Values.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} + {{- end }} +{{- end }} diff --git a/charts/vald-benchmark-operator/templates/serviceaccount.yaml b/charts/vald-benchmark-operator/templates/serviceaccount.yaml new file mode 100644 index 0000000000..f07cdf57b9 --- /dev/null +++ b/charts/vald-benchmark-operator/templates/serviceaccount.yaml @@ -0,0 +1,22 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.serviceAccount.name }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/vald-benchmark-operator/values.schema.json b/charts/vald-benchmark-operator/values.schema.json index 0764934803..6ed6f1764f 100644 --- a/charts/vald-benchmark-operator/values.schema.json +++ b/charts/vald-benchmark-operator/values.schema.json @@ -3,261 +3,502 @@ "title": "Values", "type": "object", "properties": { - "client_config": { + "affinity": { "type": "object", "description": "affinity" }, + "annotations": { + "type": "object", + "description": "deployment annotations" + }, + "image": { "type": "object", "properties": { - "addrs": { - "type": "array", - "description": "gRPC client addresses", - "items": { "type": "string" } + "pullPolicy": { + "type": "string", + "description": "image pull policy", + "enum": ["Always", "Never", "IfNotPresent"] }, - "backoff": { - "type": "object", - "properties": { - "backoff_factor": { - "type": "number", - "description": "gRPC client backoff factor" - }, - "backoff_time_limit": { - "type": "string", - "description": "gRPC client backoff time limit" - }, - "enable_error_log": { - "type": "boolean", - "description": "gRPC client backoff log enabled" - }, - "initial_duration": { - "type": "string", - "description": "gRPC client backoff initial duration" - }, - "jitter_limit": { - "type": "string", - "description": "gRPC client backoff jitter limit" - }, - "maximum_duration": { - "type": "string", - "description": "gRPC client backoff maximum duration" - }, - "retry_count": { - "type": "integer", - "description": "gRPC client backoff retry count" - } - } + "repository": { "type": "string", "description": "image repository" }, + "tag": { "type": "string", "description": "image tag" } + } + }, + "logging": { + "type": "object", + "properties": { + "format": { + "type": "string", + "description": "logging format. logging format must be `raw` or `json`", + "enum": ["raw", "json"] }, - "call_option": { "type": "object" }, - "circuit_breaker": { + "level": { + "type": "string", + "description": "logging level. logging level must be `debug`, `info`, `warn`, `error` or `fatal`.", + "enum": ["debug", "info", "warn", "error", "fatal"] + }, + "logger": { + "type": "string", + "description": "logger name. currently logger must be `glg` or `zap`.", + "enum": ["glg", "zap"] + } + } + }, + "name": { "type": "string", "description": "name of the deployment" }, + "nodeSelector": { + "type": "object", + "description": "node labels for pod assignment" + }, + "observability": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "otlp": { "type": "object", "properties": { - "closed_error_rate": { - "type": "number", - "description": "gRPC client circuitbreaker closed error rate" - }, - "closed_refresh_timeout": { - "type": "string", - "description": "gRPC client circuitbreaker closed refresh timeout" - }, - "half_open_error_rate": { - "type": "number", - "description": "gRPC client circuitbreaker half-open error rate" - }, - "min_samples": { - "type": "integer", - "description": "gRPC client circuitbreaker minimum sampling count" + "attribute": { + "type": "object", + "properties": { + "metrics": { + "type": "object", + "properties": { + "enable_cgo": { "type": "boolean" }, + "enable_goroutine": { "type": "boolean" }, + "enable_memory": { "type": "boolean" }, + "enable_version_info": { "type": "boolean" }, + "version_info_labels": { + "type": "array", + "items": { "type": "string" } + } + } + }, + "namespace": { "type": "string" }, + "node_name": { "type": "string" }, + "pod_name": { "type": "string" }, + "service_name": { "type": "string" } + } }, - "open_timeout": { - "type": "string", - "description": "gRPC client circuitbreaker open timeout" - } + "collector_endpoint": { "type": "string" }, + "metrics_export_interval": { "type": "string" }, + "metrics_export_timeout": { "type": "string" }, + "trace_batch_timeout": { "type": "string" }, + "trace_export_timeout": { "type": "string" }, + "trace_max_export_batch_size": { "type": "integer" }, + "trace_max_queue_size": { "type": "integer" } } }, - "connection_pool": { + "trace": { "type": "object", "properties": { - "enable_dns_resolver": { - "type": "boolean", - "description": "enables gRPC client connection pool dns resolver, when enabled vald uses ip handshake exclude dns discovery which improves network performance" - }, - "enable_rebalance": { - "type": "boolean", - "description": "enables gRPC client connection pool rebalance" - }, - "old_conn_close_duration": { - "type": "string", - "description": "makes delay before gRPC client connection closing during connection pool rebalance" - }, - "rebalance_duration": { - "type": "string", - "description": "gRPC client connection pool rebalance duration" - }, - "size": { - "type": "integer", - "description": "gRPC client connection pool size" - } + "enabled": { "type": "boolean" }, + "sampling_rate": { "type": "integer" } } + } + } + }, + "podAnnotations": { "type": "object", "description": "pod annotations" }, + "podSecurityContext": { + "type": "object", + "description": "security context for pod" + }, + "rbac": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "required roles and rolebindings will be created" }, - "dial_option": { + "name": { + "type": "string", + "description": "name of roles and rolebindings" + } + } + }, + "replicas": { + "type": "integer", + "description": "the number of replica for deployment" + }, + "resources": { + "type": "object", + "description": "kubernetes resources of pod", + "properties": { + "limits": { "type": "object" }, + "requests": { "type": "object" } + } + }, + "securityContext": { + "type": "object", + "description": "security context for container" + }, + "server_config": { + "type": "object", + "properties": { + "full_shutdown_duration": { "type": "string" }, + "healths": { "type": "object", "properties": { - "backoff_base_delay": { - "type": "string", - "description": "gRPC client dial option base backoff delay" - }, - "backoff_jitter": { - "type": "number", - "description": "gRPC client dial option base backoff delay" - }, - "backoff_max_delay": { - "type": "string", - "description": "gRPC client dial option max backoff delay" - }, - "backoff_multiplier": { - "type": "number", - "description": "gRPC client dial option base backoff delay" - }, - "enable_backoff": { - "type": "boolean", - "description": "gRPC client dial option backoff enabled" - }, - "initial_connection_window_size": { - "type": "integer", - "description": "gRPC client dial option initial connection window size" - }, - "initial_window_size": { - "type": "integer", - "description": "gRPC client dial option initial window size" - }, - "insecure": { - "type": "boolean", - "description": "gRPC client dial option insecure enabled" - }, - "interceptors": { - "type": "array", - "description": "gRPC client interceptors", - "items": { "type": "string", "enum": ["TraceInterceptor"] } - }, - "keepalive": { + "liveness": { "type": "object", "properties": { - "permit_without_stream": { - "type": "boolean", - "description": "gRPC client keep alive permit without stream" + "enabled": { "type": "boolean" }, + "host": { "type": "string" }, + "livenessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer", + "description": "liveness probe failure threshold" + }, + "httpGet": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "readiness probe path" + }, + "port": { + "type": "string", + "description": "readiness probe port" + }, + "scheme": { + "type": "string", + "description": "readiness probe scheme" + } + } + }, + "initialDelaySeconds": { + "type": "integer", + "description": "liveness probe initial delay seconds" + }, + "periodSeconds": { + "type": "integer", + "description": "liveness probe period seconds" + }, + "successThreshold": { + "type": "integer", + "description": "liveness probe success threshold" + }, + "timeoutSeconds": { + "type": "integer", + "description": "liveness probe timeout seconds" + } + } }, - "time": { - "type": "string", - "description": "gRPC client keep alive time" + "port": { "type": "integer" }, + "server": { + "type": "object", + "properties": { + "http": { + "type": "object", + "properties": { + "idle_timeout": { "type": "string" }, + "read_header_timeout": { "type": "string" }, + "read_timeout": { "type": "string" }, + "shutdown_duration": { "type": "string" }, + "timeout": { "type": "string" }, + "write_timeout": { "type": "string" } + } + }, + "mode": { "type": "string" }, + "network": { "type": "string" }, + "probe_wait_time": { "type": "string" }, + "socket_path": { "type": "string" } + } }, - "timeout": { - "type": "string", - "description": "gRPC client keep alive timeout" - } + "servicePort": { "type": "integer" } } }, - "max_msg_size": { - "type": "integer", - "description": "gRPC client dial option max message size" + "readiness": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "host": { "type": "string" }, + "port": { "type": "integer" }, + "readinessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer", + "description": "readiness probe failure threshold" + }, + "httpGet": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "readiness probe path" + }, + "port": { + "type": "string", + "description": "readiness probe port" + }, + "scheme": { + "type": "string", + "description": "readiness probe scheme" + } + } + }, + "initialDelaySeconds": { + "type": "integer", + "description": "readiness probe initial delay seconds" + }, + "periodSeconds": { + "type": "integer", + "description": "readiness probe period seconds" + }, + "successThreshold": { + "type": "integer", + "description": "readiness probe success threshold" + }, + "timeoutSeconds": { + "type": "integer", + "description": "readiness probe timeout seconds" + } + } + }, + "server": { + "type": "object", + "properties": { + "http": { + "type": "object", + "properties": { + "handler_timeout": { "type": "string" }, + "idle_timeout": { "type": "string" }, + "read_header_timeout": { "type": "string" }, + "read_timeout": { "type": "string" }, + "shutdown_duration": { "type": "string" }, + "write_timeout": { "type": "string" } + } + }, + "mode": { "type": "string" }, + "network": { "type": "string" }, + "probe_wait_time": { "type": "string" }, + "socket_path": { "type": "string" } + } + }, + "servicePort": { "type": "integer" } + } }, - "min_connection_timeout": { - "type": "string", - "description": "gRPC client dial option minimum connection timeout" + "startup": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "enable startup probe." + } + } }, - "net": { + "startupProbe": { "type": "object", "properties": { - "dialer": { + "failureThreshold": { + "type": "integer", + "description": "startupProbe probe failure threshold" + }, + "httpGet": { "type": "object", "properties": { - "dual_stack_enabled": { - "type": "boolean", - "description": "gRPC client TCP dialer dual stack enabled" + "path": { + "type": "string", + "description": "startup probe path" }, - "keepalive": { + "port": { "type": "string", - "description": "gRPC client TCP dialer keep alive" + "description": "startup probe port" }, - "timeout": { + "scheme": { "type": "string", - "description": "gRPC client TCP dialer timeout" + "description": "startup probe scheme" } } }, - "dns": { + "initialDelaySeconds": { + "type": "integer", + "description": "startup probe initial delay seconds" + }, + "periodSeconds": { + "type": "integer", + "description": "startup probe period seconds" + }, + "successThreshold": { + "type": "integer", + "description": "startup probe success threshold" + }, + "timeoutSeconds": { + "type": "integer", + "description": "startup probe timeout seconds" + } + } + } + } + }, + "metrics": { + "type": "object", + "properties": { + "pprof": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "host": { "type": "string" }, + "port": { "type": "integer" }, + "server": { "type": "object", "properties": { - "cache_enabled": { - "type": "boolean", - "description": "gRPC client TCP DNS cache enabled" + "http": { + "type": "object", + "properties": { + "handler_timeout": { "type": "string" }, + "idle_timeout": { "type": "string" }, + "read_header_timeout": { "type": "string" }, + "read_timeout": { "type": "string" }, + "shutdown_duration": { "type": "string" }, + "write_timeout": { "type": "string" } + } }, - "cache_expiration": { - "type": "string", - "description": "gRPC client TCP DNS cache expiration" + "mode": { "type": "string" }, + "network": { "type": "string" }, + "probe_wait_time": { "type": "string" }, + "socket_path": { "type": "string" } + } + } + } + } + } + }, + "servers": { + "type": "object", + "properties": { + "grpc": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "host": { "type": "string" }, + "name": { "type": "string" }, + "port": { "type": "integer" }, + "server": { + "type": "object", + "properties": { + "grpc": { + "type": "object", + "properties": { + "bidirectional_stream_concurrency": { + "type": "integer" + }, + "connection_timeout": { "type": "string" }, + "enable_reflection": { "type": "boolean" }, + "header_table_size": { "type": "integer" }, + "initial_conn_window_size": { "type": "integer" }, + "initial_window_size": { "type": "integer" }, + "interceptors": { + "type": "array", + "items": { "type": "string" } + }, + "keepalive": { + "type": "object", + "properties": { + "max_conn_age": { + "type": "string", + "description": "gRPC server keep alive max connection age" + }, + "max_conn_age_grace": { + "type": "string", + "description": "gRPC server keep alive max connection age grace" + }, + "max_conn_idle": { + "type": "string", + "description": "gRPC server keep alive max connection idle" + }, + "min_time": { + "type": "string", + "description": "gRPC server keep alive min_time" + }, + "permit_without_stream": { + "type": "boolean", + "description": "gRPC server keep alive permit_without_stream" + }, + "time": { + "type": "string", + "description": "gRPC server keep alive time" + }, + "timeout": { + "type": "string", + "description": "gRPC server keep alive timeout" + } + } + }, + "max_header_list_size": { "type": "integer" }, + "max_receive_message_size": { "type": "integer" }, + "max_send_msg_size": { "type": "integer" }, + "read_buffer_size": { "type": "integer" }, + "write_buffer_size": { "type": "integer" } + } }, - "refresh_duration": { - "type": "string", - "description": "gRPC client TCP DNS cache refresh duration" - } + "mode": { "type": "string" }, + "network": { "type": "string" }, + "probe_wait_time": { "type": "string" }, + "restart": { "type": "boolean" }, + "socket_path": { "type": "string" } } }, - "socket_option": { "type": "" }, - "tls": { "type": "" } + "servicePort": { "type": "integer" } } }, - "read_buffer_size": { - "type": "integer", - "description": "gRPC client dial option read buffer size" - }, - "timeout": { - "type": "string", - "description": "gRPC client dial option timeout" - }, - "write_buffer_size": { - "type": "integer", - "description": "gRPC client dial option write buffer size" + "rest": { + "type": "object", + "properties": { "enabled": { "type": "boolean" } } } } }, - "health_check_duration": { - "type": "string", - "description": "gRPC client health check duration" - }, - "max_recv_msg_size": { "type": "integer" }, - "max_retry_rpc_buffer_size": { "type": "integer" }, - "max_send_msg_size": { "type": "integer" }, - "tls": { "type": "" }, - "wait_for_ready": { "type": "boolean" } + "tls": { + "type": "object", + "properties": { + "ca": { "type": "string" }, + "cert": { "type": "string" }, + "enabled": { "type": "boolean" }, + "insecure_skip_verify": { + "type": "boolean", + "description": "enable/disable skip SSL certificate verification" + }, + "key": { "type": "string" } + } + } } }, - "dataset": { + "service": { "type": "object", - "description": "dataset information", "properties": { - "group": { - "type": "string", - "description": "the hdf5 group name of dataset" + "annotations": { + "type": "object", + "description": "service annotations" }, - "indexes": { - "type": "integer", - "description": "the amount of indexes" + "enabled": { "type": "boolean", "description": "service enabled" }, + "externalTrafficPolicy": { + "type": "string", + "description": "external traffic policy (can be specified when service type is LoadBalancer or NodePort) : Cluster or Local" }, - "name": { "type": "string", "description": "the name of dataset" }, - "range": { - "type": "object", - "description": "the data range of indexes", - "properties": { - "end": { "type": "integer", "description": "end index number" }, - "start": { "type": "integer", "description": "start index number" } - } + "labels": { "type": "object", "description": "service labels" }, + "type": { + "type": "string", + "description": "service type: ClusterIP, LoadBalancer or NodePort", + "enum": ["ClusterIP", "LoadBalancer", "NodePort"] } } }, - "jobs": { - "type": "array", - "description": "benchmark jobs", - "items": { "type": "object" } + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "service account will be created" + }, + "name": { "type": "string", "description": "name of service account" } + } }, - "target": { + "time_zone": { "type": "string", "description": "time_zone" }, + "tolerations": { "type": "array", - "description": "target cluster host\u0026port", + "description": "tolerations", "items": { "type": "object" } + }, + "version": { + "type": "string", + "description": "version of benchmark-operator config" } } } diff --git a/charts/vald-benchmark-operator/values.yaml b/charts/vald-benchmark-operator/values.yaml index 064136e226..587bcd9121 100644 --- a/charts/vald-benchmark-operator/values.yaml +++ b/charts/vald-benchmark-operator/values.yaml @@ -14,261 +14,483 @@ # limitations under the License. # -# @schema {"name": "dataset", "type": "object"} -# dataset -- dataset information -dataset: - # @schema {"name": "dataset.name", "type": "string" } - # dataset.name -- the name of dataset - name: "fashion-mnist" - # @schema {"name": "dataset.indexes", "type": "integer"} - # dataset.indexes -- the amount of indexes - indexes: 10000 - # @schema {"name": "dataset.group", "type": "string"} - # dataset.group -- the hdf5 group name of dataset - group: "train" - # @schema {"name": "dataset.range", "type": "object"} - # dataset.range -- the data range of indexes - range: - # @schema {"name": "dataset.range.start", "type": "integer"} - # dataset.range.start -- start index number - start: 0 - # @schema {"name": "dataset.range.end", "type": "integer"} - # dataset.range.end -- end index number - end: 10000 +# @schema {"name": "name", "type": "string"} +# name -- name of the deployment +name: vald-benchmark-operator -# @schema {"name": "target", "type": "object"} -# target -- target cluster host&port -target: - # @schema {"name": "target.host", "type": "string"} - # target.host -- target cluster host - host: "vald-lb-gateway.vald.svc.cluster.local" - # @schema {"name": "target.port", "type": "integer"} - # target.port -- target cluster port - port: 8081 +# @schema {"name": "replicas", "type": "integer"} +# replicas -- the number of replica for deployment +replicas: 1 -# @schema {"name": "jobs", "type": "array", "items": {"type": "object"}} -# jobs -- benchmark jobs -jobs: - # @schema {"name": "jobs.items.dataset", "type": "object"} - - dataset: - name: "fashion-mnist" - indexes: 10000 - group: "train" - range: - start: 0 - end: 10000 - repetition: 1 - replica: 1 - dimension: 784 - target: - host: "localhost" - port: "8081" - rules: [] - search_config: - epsilon: 0.1 - radius: -1 - num: 10 - min_num: 5 - timeout: "1m" - # @schema {"name": "client_config", "type": "object", "anchor": "client_config"} - client_config: - # @schema {"name": "client_config.addrs", "type": "array", "items": {"type": "string"}} - # client_config.addrs -- gRPC client addresses - addrs: [] - # @schema {"name": "client_config.health_check_duration", "type": "string"} - # client_config.health_check_duration -- gRPC client health check duration - health_check_duration: "1s" - # @schema {"name": "client_config.connection_pool", "type": "object"} - connection_pool: - # @schema {"name": "client_config.connection_pool.enable_dns_resolver", "type": "boolean"} - # client_config.connection_pool.enable_dns_resolver -- enables gRPC client connection pool dns resolver, when enabled vald uses ip handshake exclude dns discovery which improves network performance - enable_dns_resolver: true - # @schema {"name": "client_config.connection_pool.enable_rebalance", "type": "boolean"} - # client_config.connection_pool.enable_rebalance -- enables gRPC client connection pool rebalance - enable_rebalance: true - # @schema {"name": "client_config.connection_pool.rebalance_duration", "type": "string"} - # client_config.connection_pool.rebalance_duration -- gRPC client connection pool rebalance duration - rebalance_duration: 30m - # @schema {"name": "client_config.connection_pool.size", "type": "integer"} - # client_config.connection_pool.size -- gRPC client connection pool size - size: 3 - # @schema {"name": "client_config.connection_pool.old_conn_close_duration", "type": "string"} - # client_config.connection_pool.old_conn_close_duration -- makes delay before gRPC client connection closing during connection pool rebalance - old_conn_close_duration: "2m" - # @schema {"name": "client_config.backoff", "type": "object", "anchor": "backoff"} - backoff: - # @schema {"name": "client_config.backoff.initial_duration", "type": "string"} - # client_config.backoff.initial_duration -- gRPC client backoff initial duration - initial_duration: 5ms - # @schema {"name": "client_config.backoff.backoff_time_limit", "type": "string"} - # client_config.backoff.backoff_time_limit -- gRPC client backoff time limit - backoff_time_limit: 5s - # @schema {"name": "client_config.backoff.maximum_duration", "type": "string"} - # client_config.backoff.maximum_duration -- gRPC client backoff maximum duration - maximum_duration: 5s - # @schema {"name": "client_config.backoff.jitter_limit", "type": "string"} - # client_config.backoff.jitter_limit -- gRPC client backoff jitter limit - jitter_limit: 100ms - # @schema {"name": "client_config.backoff.backoff_factor", "type": "number"} - # client_config.backoff.backoff_factor -- gRPC client backoff factor - backoff_factor: 1.1 - # @schema {"name": "client_config.backoff.retry_count", "type": "integer"} - # client_config.backoff.retry_count -- gRPC client backoff retry count - retry_count: 100 - # @schema {"name": "client_config.backoff.enable_error_log", "type": "boolean"} - # client_config.backoff.enable_error_log -- gRPC client backoff log enabled - enable_error_log: true - # @schema {"name": "client_config.circuit_breaker", "type": "object"} - circuit_breaker: - # @schema {"name": "client_config.circuit_breaker.closed_error_rate", "type": "number"} - # client_config.circuit_breaker.closed_error_rate -- gRPC client circuitbreaker closed error rate - closed_error_rate: 0.7 - # @schema {"name": "client_config.circuit_breaker.half_open_error_rate", "type": "number"} - # client_config.circuit_breaker.half_open_error_rate -- gRPC client circuitbreaker half-open error rate - half_open_error_rate: 0.5 - # @schema {"name": "client_config.circuit_breaker.min_samples", "type": "integer"} - # client_config.circuit_breaker.min_samples -- gRPC client circuitbreaker minimum sampling count - min_samples: 1000 - # @schema {"name": "client_config.circuit_breaker.open_timeout", "type": "string"} - # client_config.circuit_breaker.open_timeout -- gRPC client circuitbreaker open timeout - open_timeout: "1s" - # @schema {"name": "client_config.circuit_breaker.closed_refresh_timeout", "type": "string"} - # client_config.circuit_breaker.closed_refresh_timeout -- gRPC client circuitbreaker closed refresh timeout - closed_refresh_timeout: "10s" - # @schema {"name": "client_config.call_option", "type": "object"} - call_option: - # @schema {"name": "client_config.wait_for_ready", "type": "boolean"} - # client_config.call_option.wait_for_ready -- gRPC client call option wait for ready - wait_for_ready: true - # @schema {"name": "client_config.max_retry_rpc_buffer_size", "type": "integer"} - # client_config.call_option.max_retry_rpc_buffer_size -- gRPC client call option max retry rpc buffer size - max_retry_rpc_buffer_size: 0 - # @schema {"name": "client_config.max_recv_msg_size", "type": "integer"} - # client_config.call_option.max_recv_msg_size -- gRPC client call option max receive message size - max_recv_msg_size: 0 - # @schema {"name": "client_config.max_send_msg_size", "type": "integer"} - # client_config.call_option.max_send_msg_size -- gRPC client call option max send message size - max_send_msg_size: 0 - # @schema {"name": "client_config.dial_option", "type": "object"} - dial_option: - # @schema {"name": "client_config.dial_option.write_buffer_size", "type": "integer"} - # client_config.dial_option.write_buffer_size -- gRPC client dial option write buffer size - write_buffer_size: 0 - # @schema {"name": "client_config.dial_option.read_buffer_size", "type": "integer"} - # client_config.dial_option.read_buffer_size -- gRPC client dial option read buffer size - read_buffer_size: 0 - # @schema {"name": "client_config.dial_option.initial_window_size", "type": "integer"} - # client_config.dial_option.initial_window_size -- gRPC client dial option initial window size - initial_window_size: 0 - # @schema {"name": "client_config.dial_option.initial_connection_window_size", "type": "integer"} - # client_config.dial_option.initial_connection_window_size -- gRPC client dial option initial connection window size - initial_connection_window_size: 0 - # @schema {"name": "client_config.dial_option.max_msg_size", "type": "integer"} - # client_config.dial_option.max_msg_size -- gRPC client dial option max message size - max_msg_size: 0 - # @schema {"name": "client_config.dial_option.backoff_max_delay", "type": "string"} - # client_config.dial_option.backoff_max_delay -- gRPC client dial option max backoff delay - backoff_max_delay: "120s" - # @schema {"name": "client_config.dial_option.backoff_base_delay", "type": "string"} - # client_config.dial_option.backoff_base_delay -- gRPC client dial option base backoff delay - backoff_base_delay: "1s" - # @schema {"name": "client_config.dial_option.backoff_multiplier", "type": "number"} - # client_config.dial_option.backoff_multiplier -- gRPC client dial option base backoff delay - backoff_multiplier: 1.6 - # @schema {"name": "client_config.dial_option.backoff_jitter", "type": "number"} - # client_config.dial_option.backoff_jitter -- gRPC client dial option base backoff delay - backoff_jitter: 0.2 - # @schema {"name": "client_config.dial_option.min_connection_timeout", "type": "string"} - # client_config.dial_option.min_connection_timeout -- gRPC client dial option minimum connection timeout - min_connection_timeout: "20s" - # @schema {"name": "client_config.dial_option.enable_backoff", "type": "boolean"} - # client_config.dial_option.enable_backoff -- gRPC client dial option backoff enabled - enable_backoff: false - # @schema {"name": "client_config.dial_option.insecure", "type": "boolean"} - # client_config.dial_option.insecure -- gRPC client dial option insecure enabled - insecure: true - # @schema {"name": "client_config.dial_option.timeout", "type": "string"} - # client_config.dial_option.timeout -- gRPC client dial option timeout - timeout: "" - # @schema {"name": "client_config.dial_option.interceptors", "type": "array", "items": {"type": "string", "enum": ["TraceInterceptor"]}} - # client_config.dial_option.interceptors -- gRPC client interceptors - interceptors: [] - # @schema {"name": "client_config.dial_option.net", "type": "object", "anchor": "net"} - net: - # @schema {"name": "client_config.dial_option.net.dns", "type": "object"} - dns: - # @schema {"name": "client_config.dial_option.net.dns.cache_enabled", "type": "boolean"} - # client_config.dial_option.net.dns.cache_enabled -- gRPC client TCP DNS cache enabled - cache_enabled: true - # @schema {"name": "client_config.dial_option.net.dns.refresh_duration", "type": "string"} - # client_config.dial_option.net.dns.refresh_duration -- gRPC client TCP DNS cache refresh duration - refresh_duration: 30m - # @schema {"name": "client_config.dial_option.net.dns.cache_expiration", "type": "string"} - # client_config.dial_option.net.dns.cache_expiration -- gRPC client TCP DNS cache expiration - cache_expiration: 1h - # @schema {"name": "client_config.dial_option.net.dialer", "type": "object"} - dialer: - # @schema {"name": "client_config.dial_option.net.dialer.timeout", "type": "string"} - # client_config.dial_option.net.dialer.timeout -- gRPC client TCP dialer timeout - timeout: "" - # @schema {"name": "client_config.dial_option.net.dialer.keepalive", "type": "string"} - # client_config.dial_option.net.dialer.keepalive -- gRPC client TCP dialer keep alive - keepalive: "" - # @schema {"name": "client_config.dial_option.net.dialer.dual_stack_enabled", "type": "boolean"} - # client_config.dial_option.net.dialer.dual_stack_enabled -- gRPC client TCP dialer dual stack enabled - dual_stack_enabled: true - # @schema {"name": "client_config.dial_option.net.tls"} - tls: - # client_config.dial_option.net.tls.enabled -- TLS enabled - enabled: false - # client_config.dial_option.net.tls.cert -- TLS cert path - cert: /path/to/cert - # client_config.dial_option.net.tls.key -- TLS key path - key: /path/to/key - # client_config.dial_option.net.tls.ca -- TLS ca path - ca: /path/to/ca - # client_config.dial_option.net.tls.insecure_skip_verify -- enable/disable skip SSL certificate verification - insecure_skip_verify: false - # @schema {"name": "client_config.dial_option.net.socket_option"} - socket_option: - # client_config.dial_option.net.socket_option.reuse_port -- server listen socket option for reuse_port functionality - reuse_port: true - # client_config.dial_option.net.socket_option.reuse_addr -- server listen socket option for reuse_addr functionality - reuse_addr: true - # client_config.dial_option.net.socket_option.tcp_fast_open -- server listen socket option for tcp_fast_open functionality - tcp_fast_open: true - # client_config.dial_option.net.socket_option.tcp_no_delay -- server listen socket option for tcp_no_delay functionality - tcp_no_delay: true - # client_config.dial_option.net.socket_option.tcp_cork -- server listen socket option for tcp_cork functionality - tcp_cork: false - # client_config.dial_option.net.socket_option.tcp_quick_ack -- server listen socket option for tcp_quick_ack functionality - tcp_quick_ack: true - # client_config.dial_option.net.socket_option.tcp_defer_accept -- server listen socket option for tcp_defer_accept functionality - tcp_defer_accept: true - # client_config.dial_option.net.socket_option.ip_transparent -- server listen socket option for ip_transparent functionality - ip_transparent: false - # client_config.dial_option.net.socket_option.ip_recover_destination_addr -- server listen socket option for ip_recover_destination_addr functionality - ip_recover_destination_addr: false - # @schema {"name": "client_config.dial_option.keepalive", "type": "object"} - keepalive: - # @schema {"name": "client_config.dial_option.keepalive.time", "type": "string"} - # client_config.dial_option.keepalive.time -- gRPC client keep alive time - time: "120s" - # @schema {"name": "client_config.dial_option.keepalive.timeout", "type": "string"} - # client_config.dial_option.keepalive.timeout -- gRPC client keep alive timeout - timeout: "30s" - # @schema {"name": "client_config.dial_option.keepalive.permit_without_stream", "type": "boolean"} - # client_config.dial_option.keepalive.permit_without_stream -- gRPC client keep alive permit without stream - permit_without_stream: true - # @schema {"name": "client_config.tls"} - tls: - # client_config.tls.enabled -- TLS enabled - enabled: false - # client_config.tls.cert -- TLS cert path - cert: /path/to/cert - # client_config.tls.key -- TLS key path - key: /path/to/key - # client_config.tls.ca -- TLS ca path - ca: /path/to/ca - # client_config.tls.insecure_skip_verify -- enable/disable skip SSL certificate verification - insecure_skip_verify: false +# @schema {"name": "version", "type": "string"} +# version -- version of benchmark-operator config +version: v0.0.0 + +# @schema {"name": "time_zone", "type": "string"} +# time_zone -- time_zone +time_zone: "" + +# @schema {"name": "image", "type": "object"} +image: + # @schema {"name": "image.repository", "type": "string"} + # image.repository -- image repository + repository: vdaas/vald-benchmark-operator + # @schema {"name": "image.tag", "type": "string"} + # image.tag -- image tag + tag: v1.7.5 + # @schema {"name": "image.pullPolicy", "type": "string", "enum": ["Always", "Never", "IfNotPresent"]} + # image.pullPolicy -- image pull policy + pullPolicy: Always + +# @schema {"name": "rbac", "type": "object"} +rbac: + # @schema {"name": "rbac.create", "type": "boolean"} + # rbac.create -- required roles and rolebindings will be created + create: true + # @schema {"name": "rbac.name", "type": "string"} + # rbac.name -- name of roles and rolebindings + name: vald-benchmark-operator + +# @schema {"name": "serviceAccount", "type": "object"} +serviceAccount: + # @schema {"name": "serviceAccount.create", "type": "boolean"} + # serviceAccount.create -- service account will be created + create: true + # @schema {"name": "serviceAccount.name", "type": "string"} + # serviceAccount.name -- name of service account + name: vald-benchmark-operator + +# @schema {"name": "service", "type": "object"} +service: + # @schema {"name": "service.enabled", "type": "boolean"} + # service.enabled -- service enabled + enabled: true + # @schema {"name": "service.annotations", "type": "object"} + # service.annotations -- service annotations + annotations: {} + # @schema {"name": "service.labels", "type": "object"} + # service.labels -- service labels + labels: {} + # @schema {"name": "service.type", "type": "string", "enum": ["ClusterIP", "LoadBalancer", "NodePort"]} + # service.type -- service type: ClusterIP, LoadBalancer or NodePort + type: ClusterIP + # @schema {"name": "service.externalTrafficPolicy", "type": "string"} + # service.externalTrafficPolicy -- external traffic policy (can be specified when service type is LoadBalancer or NodePort) : Cluster or Local + externalTrafficPolicy: "" + +# @schema {"name": "annotations", "type": "object"} +# annotations -- deployment annotations +annotations: {} + +# @schema {"name": "podAnnotations", "type": "object"} +# podAnnotations -- pod annotations +podAnnotations: {} + +# @schema {"name": "securityContext", "type": "object"} +# securityContext -- security context for container +securityContext: + runAsUser: 65532 + runAsNonRoot: true + runAsGroup: 65532 + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + +# @schema {"name": "podSecurityContext", "type": "object"} +# podSecurityContext -- security context for pod +podSecurityContext: + runAsUser: 65532 + runAsNonRoot: true + runAsGroup: 65532 + fsGroup: 65532 + fsGroupChangePolicy: "OnRootMismatch" + +# @schema {"name": "resources", "type": "object"} +# resources -- kubernetes resources of pod +resources: + # @schema {"name": "resources.limits", "type": "object"} + limits: + cpu: 300m + memory: 300Mi + # @schema {"name": "resources.requests", "type": "object"} + requests: + cpu: 200m + memory: 200Mi + +# @schema {"name": "nodeSelector", "type": "object"} +# nodeSelector -- node labels for pod assignment +nodeSelector: {} + +# @schema {"name": "tolerations", "type": "array", "items": {"type": "object"}} +# tolerations -- tolerations +tolerations: [] + +# @schema {"name": "affinity", "type": "object"} +# affinity -- affinity +affinity: {} + +# @schema {"name": "logging", "type": "object"} +logging: + # @schema {"name": "logging.logger", "type": "string", "enum": ["glg", "zap"]} + # logging.logger -- logger name. + # currently logger must be `glg` or `zap`. + logger: glg + # @schema {"name": "logging.level", "type": "string", "enum": ["debug", "info", "warn", "error", "fatal"]} + # logging.level -- logging level. + # logging level must be `debug`, `info`, `warn`, `error` or `fatal`. + level: debug + # @schema {"name": "logging.format", "type": "string", "enum": ["raw", "json"]} + # logging.format -- logging format. + # logging format must be `raw` or `json` + format: raw + +# @schema {"name": "server_config", "type": "object"} +server_config: + # @schema {"name": "server_config.servers", "type": "object"} + servers: + # @schema {"name": "server_config.servers.rest", "type": "object"} + rest: + # @schema {"name": "server_config.servers.rest.enabled", "type": "boolean"} + enabled: false + # @schema {"name": "server_config.servers.grpc", "type": "object"} + grpc: + # @schema {"name": "server_config.servers.grpc.enabled", "type": "boolean"} + enabled: true + # @schema {"name": "server_config.servers.grpc.name", "type": "string"} + name: grpc + # @schema {"name": "server_config.servers.grpc.host", "type": "string"} + host: 0.0.0.0 + # @schema {"name": "server_config.servers.grpc.port", "type": "integer"} + port: 8081 + # @schema {"name": "server_config.servers.grpc.servicePort", "type": "integer"} + serviecPort: 8081 + # @schema {"name": "server_config.servers.grpc.server", "type": "object"} + server: + # @schema {"name": "server_config.servers.grpc.server.mode", "type": "string"} + mode: GRPC + # @schema {"name": "server_config.servers.grpc.server.probe_wait_time", "type": "string"} + probe_wait_time: 3s + # @schema {"name": "server_config.servers.grpc.server.network", "type": "string"} + network: tcp + # @schema {"name": "server_config.servers.grpc.server.socket_path", "type": "string"} + socket_path: "" + # @schema {"name": "server_config.servers.grpc.server.grpc", "type": "object"} + grpc: + # @schema {"name": "server_config.servers.grpc.server.grpc.bidirectional_stream_concurrency", "type": "integer"} + bidirectional_stream_concurrency: 20 + # @schema {"name": "server_config.servers.grpc.server.grpc.connection_timeout", "type": "string"} + connection_timeout: "" + # @schema {"name": "server_config.servers.grpc.server.grpc.header_table_size", "type": "integer"} + header_table_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.initial_conn_window_size", "type": "integer"} + initial_conn_window_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.initial_window_size", "type": "integer"} + initial_window_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.interceptors", "type": "array", "items": {"type": "string"}} + interceptors: [] + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive", "type": "object"} + keepalive: + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.max_conn_idle", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.max_conn_idle -- gRPC server keep alive max connection idle + max_conn_idle: "" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.max_conn_age", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.max_conn_age -- gRPC server keep alive max connection age + max_conn_age: "" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.max_conn_age_grace", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.max_conn_age_grace -- gRPC server keep alive max connection age grace + max_conn_age_grace: "" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.time", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.time -- gRPC server keep alive time + time: "120s" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.timeout", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.timeout -- gRPC server keep alive timeout + timeout: "30s" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.min_time", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.min_time -- gRPC server keep alive min_time + min_time: "60s" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.permit_without_stream", "type": "boolean"} + # server_config.servers.grpc.server.grpc.keepalive.permit_without_stream -- gRPC server keep alive permit_without_stream + permit_without_stream: true + # @schema {"name": "server_config.servers.grpc.server.grpc.max_header_list_size", "type": "integer"} + max_header_list_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.max_receive_message_size", "type": "integer"} + max_receive_message_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.max_send_msg_size", "type": "integer"} + max_send_message_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.read_buffer_size", "type": "integer"} + read_buffer_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.write_buffer_size", "type": "integer"} + write_buffer_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.enable_reflection", "type": "boolean"} + enable_reflection: true + # @schema {"name": "server_config.servers.grpc.server.restart", "type": "boolean"} + restart: true + # @schema {"name": "server_config.healths", "type": "object"} + # health_check_servers: + healths: + # @schema {"name": "server_config.healths.liveness", "type": "object"} + liveness: + # @schema {"name": "server_config.healths.liveness.enabled", "type": "boolean"} + enabled: true + # @schema {"name": "server_config.healths.liveness.host", "type": "string"} + host: 0.0.0.0 + # @schema {"name": "server_config.healths.liveness.port", "type": "integer"} + port: 3000 + # @schema {"name": "server_config.healths.liveness.servicePort", "type": "integer"} + servicePort: 3000 + # @schema {"name": "server_config.healths.liveness.livenessProbe", "type": "object"} + livenessProbe: + # @schema {"name": "server_config.healths.liveness.livenessProbe.httpGet", "type": "object"} + httpGet: + # @schema {"name": "server_config.healths.liveness.livenessProbe.httpGet.path", "type": "string"} + # server_config.healths.liveness.livenessProbe.httpGet.path -- readiness probe path + path: /liveness + # @schema {"name": "server_config.healths.liveness.livenessProbe.httpGet.port", "type": "string"} + # server_config.healths.liveness.livenessProbe.httpGet.port -- readiness probe port + port: liveness + # @schema {"name": "server_config.healths.liveness.livenessProbe.httpGet.scheme", "type": "string"} + # server_config.healths.liveness.livenessProbe.httpGet.scheme -- readiness probe scheme + scheme: HTTP + # @schema {"name": "server_config.healths.liveness.livenessProbe.initialDelaySeconds", "type": "integer"} + # server_config.healths.liveness.livenessProbe.initialDelaySeconds -- liveness probe initial delay seconds + initialDelaySeconds: 15 + # @schema {"name": "server_config.healths.liveness.livenessProbe.periodSeconds", "type": "integer"} + # server_config.healths.liveness.livenessProbe.periodSeconds -- liveness probe period seconds + periodSeconds: 20 + # @schema {"name": "server_config.healths.liveness.livenessProbe.successThreshold", "type": "integer"} + # server_config.healths.liveness.livenessProbe.successThreshold -- liveness probe success threshold + successThreshold: 1 + # @schema {"name": "server_config.healths.liveness.livenessProbe.failureThreshold", "type": "integer"} + # server_config.healths.liveness.livenessProbe.failureThreshold -- liveness probe failure threshold + failureThreshold: 2 + # @schema {"name": "server_config.healths.liveness.livenessProbe.timeoutSeconds", "type": "integer"} + # server_config.healths.liveness.livenessProbe.timeoutSeconds -- liveness probe timeout seconds + timeoutSeconds: 5 + # @schema {"name": "server_config.healths.liveness.server", "type": "object"} + server: + # @schema {"name": "server_config.healths.liveness.server.mode", "type": "string"} + mode: "" + # @schema {"name": "server_config.healths.liveness.server.probe_wait_time", "type": "string"} + probe_wait_time: 3s + # @schema {"name": "server_config.healths.liveness.server.network", "type": "string"} + network: tcp + # @schema {"name": "server_config.healths.liveness.server.socket_path", "type": "string"} + socket_path: "" + # @schema {"name": "server_config.healths.liveness.server.http", "type": "object"} + http: + # @schema {"name": "server_config.healths.liveness.server.http.timeout", "type": "string"} + handler_timeout: "" + # @schema {"name": "server_config.healths.liveness.server.http.idle_timeout", "type": "string"} + idle_timeout: "" + # @schema {"name": "server_config.healths.liveness.server.http.read_header_timeout", "type": "string"} + read_header_timeout: "" + # @schema {"name": "server_config.healths.liveness.server.http.read_timeout", "type": "string"} + read_timeout: "" + # @schema {"name": "server_config.healths.liveness.server.http.shutdown_duration", "type": "string"} + shutdown_duration: 5s + # @schema {"name": "server_config.healths.liveness.server.http.write_timeout", "type": "string"} + write_timeout: "" + # @schema {"name": "server_config.healths.readiness", "type": "object"} + readiness: + # @schema {"name": "server_config.healths.readiness.enabled", "type": "boolean"} + enabled: true + # @schema {"name": "server_config.healths.readiness.host", "type": "string"} + host: 0.0.0.0 + # @schema {"name": "server_config.healths.readiness.port", "type": "integer"} + port: 3001 + # @schema {"name": "server_config.healths.readiness.servicePort", "type": "integer"} + servicePort: 3001 + # @schema {"name": "server_config.healths.readiness.readinessProbe", "type": "object"} + readinessProbe: + # @schema {"name": "server_config.healths.readiness.readinessProbe.httpGet", "type": "object"} + httpGet: + # @schema {"name": "server_config.healths.readiness.readinessProbe.httpGet.path", "type": "string"} + # server_config.healths.readiness.readinessProbe.httpGet.path -- readiness probe path + path: /readiness + # @schema {"name": "server_config.healths.readiness.readinessProbe.httpGet.port", "type": "string"} + # server_config.healths.readiness.readinessProbe.httpGet.port -- readiness probe port + port: readiness + # @schema {"name": "server_config.healths.readiness.readinessProbe.httpGet.scheme", "type": "string"} + # server_config.healths.readiness.readinessProbe.httpGet.scheme -- readiness probe scheme + scheme: HTTP + # @schema {"name": "server_config.healths.readiness.readinessProbe.initialDelaySeconds", "type": "integer"} + # server_config.healths.readiness.readinessProbe.initialDelaySeconds -- readiness probe initial delay seconds + initialDelaySeconds: 10 + # @schema {"name": "server_config.healths.readiness.readinessProbe.periodSeconds", "type": "integer"} + # server_config.healths.readiness.readinessProbe.periodSeconds -- readiness probe period seconds + periodSeconds: 3 + # @schema {"name": "server_config.healths.readiness.readinessProbe.successThreshold", "type": "integer"} + # server_config.healths.readiness.readinessProbe.successThreshold -- readiness probe success threshold + successThreshold: 1 + # @schema {"name": "server_config.healths.readiness.readinessProbe.failureThreshold", "type": "integer"} + # server_config.healths.readiness.readinessProbe.failureThreshold --readiness probe failure threshold + failureThreshold: 2 + # @schema {"name": "server_config.healths.readiness.readinessProbe.timeoutSeconds", "type": "integer"} + # server_config.healths.readiness.readinessProbe.timeoutSeconds -- readiness probe timeout seconds + timeoutSeconds: 2 + # @schema {"name": "server_config.healths.readiness.server", "type": "object"} + server: + # @schema {"name": "server_config.healths.readiness.server.mode", "type": "string"} + mode: "" + # @schema {"name": "server_config.healths.readiness.server.probe_wait_time", "type": "string"} + probe_wait_time: 3s + # @schema {"name": "server_config.healths.readiness.server.network", "type": "string"} + network: tcp + # @schema {"name": "server_config.healths.readiness.server.socket_path", "type": "string"} + socket_path: "" + # @schema {"name": "server_config.healths.readiness.server.http", "type": "object"} + http: + # @schema {"name": "server_config.healths.readiness.server.http.handler_timeout", "type": "string"} + handler_timeout: "" + # @schema {"name": "server_config.healths.readiness.server.http.idle_timeout", "type": "string"} + idle_timeout: "" + # @schema {"name": "server_config.healths.readiness.server.http.read_header_timeout", "type": "string"} + read_header_timeout: "" + # @schema {"name": "server_config.healths.readiness.server.http.read_timeout", "type": "string"} + read_timeout: "" + # @schema {"name": "server_config.healths.readiness.server.http.shutdown_duration", "type": "string"} + shutdown_duration: 0s + # @schema {"name": "server_config.healths.readiness.server.http.write_timeout", "type": "string"} + write_timeout: "" + # @schema {"name": "server_config.healths.startup", "type": "object"} + startup: + # @schema {"name": "server_config.healths.startup.enabled", "type": "boolean"} + # server_config.healths.startup.enabled -- enable startup probe. + enabled: true + # @schema {"name": "server_config.healths.startupProbe", "type": "object"} + startupProbe: + # @schema {"name": "server_config.healths.startupProbe.httpGet", "type": "object"} + httpGet: + # @schema {"name": "server_config.healths.startupProbe.httpGet.path", "type": "string"} + # server_config.healths.startupProbe.httpGet.path -- startup probe path + path: /liveness + # @schema {"name": "server_config.healths.startupProbe.httpGet.port", "type": "string"} + # server_config.healths.startupProbe.httpGet.port -- startup probe port + port: liveness + # @schema {"name": "server_config.healths.startupProbe.httpGet.scheme", "type": "string"} + # server_config.healths.startupProbe.httpGet.scheme -- startup probe scheme + scheme: HTTP + # @schema {"name": "server_config.healths.startupProbe.initialDelaySeconds", "type": "integer"} + # server_config.healths.startupProbe.initialDelaySeconds -- startup probe initial delay seconds + initialDelaySeconds: 5 + # @schema {"name": "server_config.healths.startupProbe.periodSeconds", "type": "integer"} + # server_config.healths.startupProbe.periodSeconds -- startup probe period seconds + periodSeconds: 5 + # @schema {"name": "server_config.healths.startupProbe.successThreshold", "type": "integer"} + # server_config.healths.startupProbe.successThreshold -- startup probe success threshold + successThreshold: 1 + # @schema {"name": "server_config.healths.startupProbe.failureThreshold", "type": "integer"} + # server_config.healths.startupProbe.failureThreshold -- startupProbe probe failure threshold + failureThreshold: 30 + # @schema {"name": "server_config.healths.startupProbe.timeoutSeconds", "type": "integer"} + # server_config.healths.startupProbe.timeoutSeconds -- startup probe timeout seconds + timeoutSeconds: 2 + # @schema {"name": "server_config.metrics", "type": "object"} + metrics: + # @schema {"name": "server_config.metrics.pprof", "type": "object"} + pprof: + # @schema {"name": "server_config.metrics.pprof.enabled", "type": "boolean"} + enabled: false + # @schema {"name": "server_config.metrics.pprof.host", "type": "string"} + host: 0.0.0.0 + # @schema {"name": "server_config.metrics.pprof.port", "type": "integer"} + port: 6060 + # @schema {"name": "server_config.metrics.pprof.server", "type": "object"} + server: + # @schema {"name": "server_config.metrics.pprof.server.mode", "type": "string"} + mode: REST + # @schema {"name": "server_config.metrics.pprof.server.probe_wait_time", "type": "string"} + probe_wait_time: 3s + # @schema {"name": "server_config.metrics.pprof.server.network", "type": "string"} + network: tcp + # @schema {"name": "server_config.metrics.pprof.server.socket_path", "type": "string"} + socket_path: "" + # @schema {"name": "server_config.metrics.pprof.server.http", "type": "object"} + http: + # @schema {"name": "server_config.metrics.pprof.server.http.handler_timeout", "type": "string"} + handler_timeout: 5s + # @schema {"name": "server_config.metrics.pprof.server.http.idle_timeout", "type": "string"} + idle_timeout: 2s + # @schema {"name": "server_config.metrics.pprof.server.http.read_header_timeout", "type": "string"} + read_header_timeout: 1s + # @schema {"name": "server_config.metrics.pprof.server.http.read_timeout", "type": "string"} + read_timeout: 1s + # @schema {"name": "server_config.metrics.pprof.server.http.shutdown_duration", "type": "string"} + shutdown_duration: 5s + # @schema {"name": "server_config.metrics.pprof.server.http.write_timeout", "type": "string"} + write_timeout: 1m + # @schema {"name": "server_config.full_shutdown_duration", "type": "string"} + full_shutdown_duration: 600s + # @schema {"name": "server_config.tls", "type": "object"} + tls: + # @schema {"name": "server_config.tls.enabled", "type": "boolean"} + enabled: false + # @schema {"name": "server_config.tls.ca", "type": "string"} + ca: /path/to/ca + # @schema {"name": "server_config.tls.cert", "type": "string"} + cert: /path/to/cert + # @schema {"name": "server_config.tls.key", "type": "string"} + key: /path/to/key + # @schema {"name": "server_config.tls.insecure_skip_verify", "type": "boolean"} + # server_config.tls.insecure_skip_verify -- enable/disable skip SSL certificate verification + insecure_skip_verify: false + +# @schema {"name": "observability", "type": "object"} +observability: + # @schema {"name": "observability.enabled", "type": "boolean"} + enabled: false + # @schema {"name": "observability.otlp", "type": "object"} + otlp: + # @schema {"name": "observability.otlp.collector_endpoint", "type": "string"} + collector_endpoint: "" + # @schema {"name": "observability.otlp.trace_batch_timeout", "type": "string"} + trace_batch_timeout: "1s" + # @schema {"name": "observability.otlp.trace_export_timeout", "type": "string"} + trace_export_timeout: "1m" + # @schema {"name": "observability.otlp.trace_max_export_batch_size", "type": "integer"} + trace_max_export_batch_size: 1024 + # @schema {"name": "observability.otlp.trace_max_queue_size", "type": "integer"} + trace_max_queue_size: 256 + # @schema {"name": "observability.otlp.metrics_export_interval", "type": "string"} + metrics_export_interval: "1s" + # @schema {"name": "observability.otlp.metrics_export_timeout", "type": "string"} + metrics_export_timeout: "1m" + # @schema {"name": "observability.otlp.attribute", "type": "object"} + attribute: + # @schema {"name": "observability.otlp.attribute.namespace", "type": "string"} + namespace: "_MY_POD_NAMESPACE_" + # @schema {"name": "observability.otlp.attribute.pod_name", "type": "string"} + pod_name: "_MY_POD_NAME_" + # @schema {"name": "observability.otlp.attribute.node_name", "type": "string"} + node_name: "_MY_NODE_NAME_" + # @schema {"name": "observability.otlp.attribute.service_name", "type": "string"} + service_name: "vald-benchmark-operator" + # @schema {"name": "observability.otlp.attribute.metrics", "type": "object"} + metrics: + # @schema {"name": "observability.otlp.attribute.metrics.enable_cgo", "type": "boolean"} + enable_cgo: true + # @schema {"name": "observability.otlp.attribute.metrics.enable_goroutine", "type": "boolean"} + enable_goroutine: true + # @schema {"name": "observability.otlp.attribute.metrics.enable_memory", "type": "boolean"} + enable_memory: true + # @schema {"name": "observability.otlp.attribute.metrics.enable_version_info", "type": "boolean"} + enable_version_info: true + # @schema {"name": "observability.otlp.attribute.metrics.version_info_labels", "type": "array", "items": {"type": "string"}} + version_info_labels: + - vald_version + - server_name + - git_commit + - build_time + - go_version + - go_os + - go_arch + - ngt_version + # @schema {"name": "observability.trace", "type": "object"} + trace: + # @schema {"name": "observability.trace.enabled", "type": "boolean"} + enabled: false + # @schema {"name": "observability.trace.sampling_rate", "type": "integer"} + sampling_rate: 1 diff --git a/charts/vald-benchmark-operator/values/benchmark-job.yaml b/charts/vald-benchmark-operator/values/benchmark-job.yaml new file mode 100644 index 0000000000..c8c13e1f3a --- /dev/null +++ b/charts/vald-benchmark-operator/values/benchmark-job.yaml @@ -0,0 +1,45 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: vald.vdaas.org/v1 +kind: ValdBenchmarkJob +metadata: + name: sample-search-job + namespace: default +spec: + dataset: + name: "fashion-mnist" + indexes: 1000 + group: "test" + range: + start: 1 + end: 1000 + job_type: "search" + dimension: 784 + repetition: 1 + replica: 1 + rules: [] + client_config: + health_check_duration: "10s" + rps: 1000 + search_config: + epsilon: 0.1 + radius: -1 + num: 10 + min_num: 10 + timeout: "1m" + target: + host: "vald-lb-gateway.default.svc.cluster.local" + port: 8081 diff --git a/charts/vald-benchmark-operator/values/benchmark-scenario.yaml b/charts/vald-benchmark-operator/values/benchmark-scenario.yaml new file mode 100644 index 0000000000..4a0068eb20 --- /dev/null +++ b/charts/vald-benchmark-operator/values/benchmark-scenario.yaml @@ -0,0 +1,185 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: vald.vdaas.org/v1 +kind: ValdBenchmarkScenario +metadata: + name: sample-scenario + namespace: default +spec: + # @schema {"name": "dataset", "type": "object"} + # dataset -- dataset information + dataset: + # @schema {"name": "dataset.name", "type": "string" } + # dataset.name -- the name of dataset + name: "fashion-mnist" + # @schema {"name": "dataset.indexes", "type": "integer"} + # dataset.indexes -- the amount of indexes + indexes: 1000 + # @schema {"name": "dataset.group", "type": "string"} + # dataset.group -- the hdf5 group name of dataset + group: "test" + # @schema {"name": "dataset.range", "type": "object"} + # dataset.range -- the data range of indexes + range: + # @schema {"name": "dataset.range.start", "type": "integer"} + # dataset.range.start -- start index number + start: 1 + # @schema {"name": "dataset.range.end", "type": "integer"} + # dataset.range.end -- end index number + end: 1000 + # @schema {"name": "jobs", "type": "array", "items": {"type": "object"}} + # jobs -- benchmark jobs + jobs: + # @schema {"name": "jobs.items.dataset", "type": "object"} + - job_type: "insert" + dimension: 784 + repetition: 1 + replica: 1 + rules: [] + dataset: + name: "fashion-mnist" + indexes: 10000 + group: "train" + range: + start: 1 + end: 10000 + insert_config: + skip_strict_exist_check: true + client_config: + health_check_duration: "10s" + rps: 500 + - job_type: "update" + dimension: 784 + repetition: 1 + replica: 1 + rules: [] + dataset: + name: "fashion-mnist" + indexes: 10000 + group: "train" + range: + start: 10001 + end: 20000 + update_config: + skip_strict_exist_check: true + client_config: + health_check_duration: "10s" + rps: 500 + - job_type: "search" + dimension: 784 + repetition: 1 + replica: 1 + rules: [] + search_config: + epsilon: 0.1 + radius: -1 + num: 10 + min_num: 10 + timeout: "1m" + client_config: + health_check_duration: "10s" + rps: 2000 + - job_type: "upsert" + dimension: 784 + repetition: 1 + replica: 1 + rules: [] + dataset: + name: "fashion-mnist" + indexes: 30000 + group: "train" + range: + start: 10001 + end: 40000 + upsert_config: + skip_strict_exist_check: true + client_config: + health_check_duration: "10s" + rps: 1000 + - job_type: "search" + dimension: 784 + repetition: 1 + replica: 1 + rules: [] + dataset: + name: "fashion-mnist" + indexes: 20000 + group: "test" + range: + start: 1 + end: 20000 + search_config: + epsilon: 0.1 + radius: -1 + num: 10 + min_num: 10 + timeout: "1m" + client_config: + health_check_duration: "10s" + rps: 4000 + - job_type: "exists" + dimension: 784 + repetition: 1 + replica: 1 + rules: [] + dataset: + name: "fashion-mnist" + indexes: 20000 + group: "train" + range: + start: 1 + end: 20000 + client_config: + health_check_duration: "10s" + rps: 1000 + - job_type: "getobject" + dimension: 784 + repetition: 1 + replica: 1 + rules: [] + dataset: + name: "fashion-mnist" + indexes: 20000 + group: "train" + range: + start: 1 + end: 20000 + client_config: + health_check_duration: "10s" + rps: 1000 + - job_type: "remove" + dimension: 784 + repetition: 1 + replica: 1 + rules: [] + dataset: + name: "fashion-mnist" + indexes: 30000 + group: "train" + range: + start: 1 + end: 30000 + remove_config: + skip_strict_exist_check: true + client_config: + health_check_duration: "10s" + rps: 1000 + + # @schema {"name": "target", "type": "array", "items": {"type": "object"}} + # target -- target cluster host&port + target: + host: "vald-lb-gateway.default.svc.cluster.local" + port: 8081 diff --git a/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml b/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml deleted file mode 100644 index 32010539ef..0000000000 --- a/internal/k8s/crd/benchmark/valdbenchmarkscenario.yaml +++ /dev/null @@ -1,429 +0,0 @@ -# -# Copyright (C) 2019-2023 vdaas.org vald team -# -# 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 -# -# https://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. -# -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: valdbenchmarkscenarios.vald.vdaas.org -spec: - group: vald.vdaas.org - names: - kind: ValdBenchmarkScenario - listKind: ValdBenchmarkScenarioList - plural: valdbenchmarkscenarios - singular: valdbenchmarkscenario - shortNames: - - vbo - - vbos - scope: Namespaced - versions: - - name: v1 - served: true - storage: true - subresources: - status: {} - additionalPrinterColumns: - - jsonPath: .status - name: STATUS - type: string - schema: - openAPIV3Schema: - description: ValdBenchmarkScenario is the Schema for the valdbenchmarkscenarios API - type: object - properties: - apiVersion: - description: - "APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" - type: string - kind: - description: - "Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" - type: string - metadata: - type: object - status: - description: ValdBenchmarkScenarioStatus defines the observed state of ValdBenchmarkScenario - enum: - - NotReady - - Completed - - Available - - Healthy - type: string - spec: - description: ValdBenchmarkScenarioSpec defines the desired state of ValdBenchmarkScenario - type: object - properties: - dataset: - description: BenchmarkDataset defines the desired state of BenchmarkDateset - properties: - group: - type: string - indexes: - type: integer - name: - type: string - range: - description: BenchmarkDatasetRange defines the desired state of BenchmarkDatesetRange - properties: - end: - type: integer - start: - type: integer - required: - - end - - start - type: object - required: - - group - - indexes - - name - type: object - target: - properties: - host: - type: string - port: - type: integer - required: - - host - - port - type: object - jobs: - type: array - items: - description: BenchmarkJobSpec defines the desired state of ValdBenchmarkJob - type: object - properties: - target: - description: BenchmarkTarget defines the desired state of BenchmarkTarget - properties: - host: - type: string - port: - type: integer - required: - - host - - port - type: object - dataset: - description: BenchmarkDataset defines the desired state of BenchmarkDateset - properties: - group: - type: string - indexes: - type: integer - name: - type: string - range: - description: BenchmarkDatasetRange defines the desired state of BenchmarkDatesetRange - properties: - end: - type: integer - start: - type: integer - required: - - end - - start - type: object - required: - - group - - indexes - - name - type: object - dimension: - type: integer - job_type: - type: string - repetition: - type: integer - replica: - type: integer - rules: - items: - description: BenchmarkJobRule defines the desired state of BenchmarkJobRule - properties: - name: - type: string - type: - type: string - required: - - name - - type - type: object - type: array - rps: - type: integer - insert_config: - description: InsertConfig defines the desired state of insert config - properties: - skip_strict_exist_check: - type: boolean - timestamp: - type: string - type: object - remove_config: - description: RemoveConfig defines the desired state of remove config - properties: - skip_strict_exist_check: - type: boolean - timestamp: - type: string - type: object - search_config: - description: SearchConfig defines the desired state of search config - properties: - epsilon: - type: number - min_num: - format: int32 - type: integer - num: - format: int32 - type: integer - radius: - type: number - timeout: - type: string - type: object - update_config: - description: UpdateConfig defines the desired state of update config - properties: - skip_strict_exist_check: - type: boolean - timestamp: - type: string - disable_balanced_update: - type: boolean - type: object - upsert_config: - description: UpsertConfig defines the desired state of upsert config - properties: - skip_strict_exist_check: - type: boolean - timestamp: - type: string - disable_balanced_update: - type: boolean - type: object - client_config: - description: ClientConfig represents the configurations for gRPC client. - properties: - addrs: - items: - type: string - type: array - backoff: - description: Backoff represents the configuration for the internal backoff package. - properties: - backoff_factor: - type: number - backoff_time_limit: - type: string - enable_error_log: - type: boolean - initial_duration: - type: string - jitter_limit: - type: string - maximum_duration: - type: string - retry_count: - type: integer - type: object - call_option: - description: CallOption represents the configurations for call option. - properties: - max_recv_msg_size: - type: integer - max_retry_rpc_buffer_size: - type: integer - max_send_msg_size: - type: integer - wait_for_ready: - type: boolean - type: object - circuit_breaker: - description: CircuitBreaker represents the configuration for the internal circuitbreaker package. - properties: - closed_error_rate: - type: number - closed_refresh_timeout: - type: string - half_open_error_rate: - type: number - min_samples: - format: int64 - type: integer - open_timeout: - type: string - type: object - connection_pool: - description: ConnectionPool represents the configurations for connection pool. - properties: - enable_rebalance: - type: boolean - old_conn_close_duration: - type: string - rebalance_duration: - type: string - resolve_dns: - type: boolean - size: - type: integer - type: object - dial_option: - description: DialOption represents the configurations for dial option. - properties: - backoff_base_delay: - type: string - backoff_jitter: - type: number - backoff_max_delay: - type: string - backoff_multiplier: - type: number - enable_backoff: - type: boolean - initial_connection_window_size: - type: integer - initial_window_size: - type: integer - insecure: - type: boolean - interceptors: - items: - type: string - type: array - keepalive: - description: GRPCClientKeepalive represents the configurations for gRPC keep-alive. - properties: - permit_without_stream: - type: boolean - time: - type: string - timeout: - type: string - type: object - max_msg_size: - type: integer - minimum_connection_timeout: - type: string - net: - description: Net represents the network configuration tcp, udp, unix domain socket. - properties: - dialer: - description: Dialer represents the configuration for dial. - properties: - dual_stack_enabled: - type: boolean - fallback_delay: - type: string - keepalive: - type: string - timeout: - type: string - type: object - dns: - description: DNS represents the configuration for resolving DNS. - properties: - cache_enabled: - type: boolean - cache_expiration: - type: string - refresh_duration: - type: string - type: object - socket_option: - description: SocketOption represents the socket configurations. - properties: - ip_recover_destination_addr: - type: boolean - ip_transparent: - type: boolean - reuse_addr: - type: boolean - reuse_port: - type: boolean - tcp_cork: - type: boolean - tcp_defer_accept: - type: boolean - tcp_fast_open: - type: boolean - tcp_no_delay: - type: boolean - tcp_quick_ack: - type: boolean - type: object - tls: - description: TLS represent the TLS configuration for server. - properties: - ca: - description: CA represent the CA certificate environment variable key used to start server. - type: string - cert: - description: Cert represent the certificate environment variable key used to start server. - type: string - enabled: - description: Enable represent the server enable TLS or not. - type: boolean - insecure_skip_verify: - description: InsecureSkipVerify represent enable/disable skip SSL certificate verification - type: boolean - key: - description: Key represent the private key environment variable key used to start server. - type: string - type: object - type: object - read_buffer_size: - type: integer - timeout: - type: string - write_buffer_size: - type: integer - type: object - health_check_duration: - type: string - tls: - description: TLS represent the TLS configuration for server. - properties: - ca: - description: CA represent the CA certificate environment variable key used to start server. - type: string - cert: - description: Cert represent the certificate environment variable key used to start server. - type: string - enabled: - description: Enable represent the server enable TLS or not. - type: boolean - insecure_skip_verify: - description: InsecureSkipVerify represent enable/disable skip SSL certificate verification - type: boolean - key: - description: Key represent the private key environment variable key used to start server. - type: string - type: object - type: object - required: - - job_type - - dimension - required: - - jobs - - dataset - - target diff --git a/internal/k8s/vald/benchmark/job/job_template.go b/internal/k8s/vald/benchmark/job/job_template.go index 884addcde5..0368e6a5b5 100644 --- a/internal/k8s/vald/benchmark/job/job_template.go +++ b/internal/k8s/vald/benchmark/job/job_template.go @@ -27,7 +27,8 @@ type benchmarkJobTemplate = batchv1.Job const ( SvcAccountName = "vald-benchmark-operator" ContainerName = "vald-benchmark-job" - ContainerImage = "local-registry:5000/vdaas/vald-benchmark-job:latest" + // TODO: Fix + ContainerImage = "vdaas/vald-benchmark-job:pr-2027" RestartPolicyAlways corev1.RestartPolicy = "Always" RestartPolicyOnFailure corev1.RestartPolicy = "OnFailure" diff --git a/internal/test/data/hdf5/hdf5_test.go b/internal/test/data/hdf5/hdf5_test.go new file mode 100644 index 0000000000..d308048781 --- /dev/null +++ b/internal/test/data/hdf5/hdf5_test.go @@ -0,0 +1,1447 @@ +// +// Copyright (C) 2019-2023 vdaas.org vald team +// +// 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 +// +// https://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 hdf5 is load hdf5 file +package hdf5 + +import ( + "log" + "os" + "reflect" + "testing" + + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/test/goleak" + "gonum.org/v1/hdf5" +) + +func TestDatasetName_String(t *testing.T) { + type want struct { + want string + } + type test struct { + name string + d DatasetName + want want + checkFunc func(want, string) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got string) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + got := test.d.String() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func TestDatasetUrl_String(t *testing.T) { + type want struct { + want string + } + type test struct { + name string + d DatasetUrl + want want + checkFunc func(want, string) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got string) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + got := test.d.String() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_hdf5Key_String(t *testing.T) { + type want struct { + want string + } + type test struct { + name string + h hdf5Key + want want + checkFunc func(want, string) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got string) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + got := test.h.String() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func TestNew(t *testing.T) { + type args struct { + opts []Option + } + type want struct { + want Data + err error + } + type test struct { + name string + args args + want want + checkFunc func(want, Data, error) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, got Data, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + opts:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + opts:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + got, err := New(test.args.opts...) + if err := checkFunc(test.want, got, err); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_data_Download(t *testing.T) { + type fields struct { + name DatasetName + path string + train [][]float32 + test [][]float32 + neighbors [][]int + } + type want struct { + err error + } + type test struct { + name string + fields fields + want want + checkFunc func(want, error) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + return nil + } + tests := []test{ + /* + { + name: "test_case_1", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + d := &data{ + name: test.fields.name, + path: test.fields.path, + train: test.fields.train, + test: test.fields.test, + neighbors: test.fields.neighbors, + } + + err := d.Download() + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_data_Read(t *testing.T) { + type fields struct { + name DatasetName + path string + train [][]float32 + test [][]float32 + neighbors [][]int + } + type want struct { + err error + } + type test struct { + name string + fields fields + want want + checkFunc func(want, error) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + d := &data{ + name: test.fields.name, + path: test.fields.path, + train: test.fields.train, + test: test.fields.test, + neighbors: test.fields.neighbors, + } + + err := d.Read() + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_data_GetName(t *testing.T) { + type fields struct { + name DatasetName + path string + train [][]float32 + test [][]float32 + neighbors [][]int + } + type want struct { + want DatasetName + } + type test struct { + name string + fields fields + want want + checkFunc func(want, DatasetName) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got DatasetName) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + d := &data{ + name: test.fields.name, + path: test.fields.path, + train: test.fields.train, + test: test.fields.test, + neighbors: test.fields.neighbors, + } + + got := d.GetName() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_data_GetPath(t *testing.T) { + type fields struct { + name DatasetName + path string + train [][]float32 + test [][]float32 + neighbors [][]int + } + type want struct { + want string + } + type test struct { + name string + fields fields + want want + checkFunc func(want, string) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got string) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + d := &data{ + name: test.fields.name, + path: test.fields.path, + train: test.fields.train, + test: test.fields.test, + neighbors: test.fields.neighbors, + } + + got := d.GetPath() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_data_GetByGroupName(t *testing.T) { + type args struct { + name string + } + type fields struct { + name DatasetName + path string + train [][]float32 + test [][]float32 + neighbors [][]int + } + type want struct { + want [][]float32 + } + type test struct { + name string + args args + fields fields + want want + checkFunc func(want, [][]float32) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, got [][]float32) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + name:"", + }, + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + name:"", + }, + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + d := &data{ + name: test.fields.name, + path: test.fields.path, + train: test.fields.train, + test: test.fields.test, + neighbors: test.fields.neighbors, + } + + got := d.GetByGroupName(test.args.name) + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_data_GetTrain(t *testing.T) { + type fields struct { + name DatasetName + path string + train [][]float32 + test [][]float32 + neighbors [][]int + } + type want struct { + want [][]float32 + } + type test struct { + name string + fields fields + want want + checkFunc func(want, [][]float32) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got [][]float32) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + d := &data{ + name: test.fields.name, + path: test.fields.path, + train: test.fields.train, + test: test.fields.test, + neighbors: test.fields.neighbors, + } + + got := d.GetTrain() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_data_GetTest(t *testing.T) { + type fields struct { + name DatasetName + path string + train [][]float32 + test [][]float32 + neighbors [][]int + } + type want struct { + want [][]float32 + } + type test struct { + name string + fields fields + want want + checkFunc func(want, [][]float32) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got [][]float32) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + d := &data{ + name: test.fields.name, + path: test.fields.path, + train: test.fields.train, + test: test.fields.test, + neighbors: test.fields.neighbors, + } + + got := d.GetTest() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_data_GetNeighbors(t *testing.T) { + type fields struct { + name DatasetName + path string + train [][]float32 + test [][]float32 + neighbors [][]int + } + type want struct { + want [][]int + } + type test struct { + name string + fields fields + want want + checkFunc func(want, [][]int) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got [][]int) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + fields: fields { + name:nil, + path:"", + train:nil, + test:nil, + neighbors:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + d := &data{ + name: test.fields.name, + path: test.fields.path, + train: test.fields.train, + test: test.fields.test, + neighbors: test.fields.neighbors, + } + + got := d.GetNeighbors() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_downloadFile(t *testing.T) { + type args struct { + url string + path string + } + type want struct { + err error + } + type test struct { + name string + args args + want want + checkFunc func(want, error) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultBeforeFunc := func(t *testing.T, _ args) { + t.Helper() + err := os.Mkdir("tmp", os.ModePerm) + if err != nil { + log.Fatal(err) + } + } + defaultAfterFunc := func(t *testing.T, _ args) { + t.Helper() + err := os.RemoveAll("tmp") + if err != nil { + log.Fatal(err) + } + } + defaultCheckFunc := func(w want, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + return nil + } + tests := []test{ + { + name: "success download hdf5 file", + args: args{ + url: "https://ann-benchmarks.com/fashion-mnist-784-euclidean.hdf5", + path: "./tmp/data", + }, + want: want{ + err: nil, + }, + beforeFunc: defaultBeforeFunc, + afterFunc: defaultAfterFunc, + checkFunc: defaultCheckFunc, + }, + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + err := downloadFile(test.args.url, test.args.path) + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func TestReadDatasetF32(t *testing.T) { + type args struct { + file *hdf5.File + key hdf5Key + } + type want struct { + want [][]float32 + err error + } + type test struct { + name string + args args + want want + checkFunc func(want, [][]float32, error) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, got [][]float32, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + file:nil, + key:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + file:nil, + key:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + got, err := ReadDatasetF32(test.args.file, test.args.key) + if err := checkFunc(test.want, got, err); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func TestReadDatasetI32(t *testing.T) { + type args struct { + file *hdf5.File + key hdf5Key + } + type want struct { + want [][]int32 + err error + } + type test struct { + name string + args args + want want + checkFunc func(want, [][]int32, error) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, got [][]int32, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + file:nil, + key:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + file:nil, + key:nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + got, err := ReadDatasetI32(test.args.file, test.args.key) + if err := checkFunc(test.want, got, err); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} diff --git a/k8s/tools/benchmark/operator/clusterrole.yaml b/k8s/tools/benchmark/operator/clusterrole.yaml index 0b64888989..70ac264c95 100644 --- a/k8s/tools/benchmark/operator/clusterrole.yaml +++ b/k8s/tools/benchmark/operator/clusterrole.yaml @@ -1,3 +1,4 @@ +--- # # Copyright (C) 2019-2023 vdaas.org vald team # @@ -16,7 +17,8 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: benchmark-operator-role + creationTimestamp: null + name: vald-benchmark-operator namespace: default rules: - apiGroups: @@ -47,6 +49,8 @@ rules: - vald.vdaas.org resources: - valdbenchmarkscenarios + - valdbenchmarkscenario + - valdbenchmarkjob - valdbenchmarkjobs verbs: - create @@ -60,13 +64,17 @@ rules: - vald.vdaas.org resources: - valdbenchmarkscenarios/finalizers + - valdbenchmarkscenario/finalizers + - valdbenchmarkjob/finalizers - valdbenchmarkjobs/finalizers verbs: - update - apiGroups: - vald.vdaas.org resources: + - valdbenchmarkscenario/status - valdbenchmarkscenarios/status + - valdbenchmarkjob/status - valdbenchmarkjobs/status verbs: - get diff --git a/k8s/tools/benchmark/operator/clusterrolebinding.yaml b/k8s/tools/benchmark/operator/clusterrolebinding.yaml index d450654516..ded68d5204 100644 --- a/k8s/tools/benchmark/operator/clusterrolebinding.yaml +++ b/k8s/tools/benchmark/operator/clusterrolebinding.yaml @@ -1,3 +1,4 @@ +--- # # Copyright (C) 2019-2023 vdaas.org vald team # @@ -13,15 +14,16 @@ # See the License for the specific language governing permissions and # limitations under the License. # -apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 metadata: - name: benchmark-operator-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: benchmark-operator-role + name: vald-benchmark-operator + namespace: default subjects: - kind: ServiceAccount name: vald-benchmark-operator namespace: default +roleRef: + kind: ClusterRole + name: vald-benchmark-operator + apiGroup: rbac.authorization.k8s.io diff --git a/k8s/tools/benchmark/operator/configmap.yaml b/k8s/tools/benchmark/operator/configmap.yaml index 7ac38b6e51..05f7b34594 100644 --- a/k8s/tools/benchmark/operator/configmap.yaml +++ b/k8s/tools/benchmark/operator/configmap.yaml @@ -1,3 +1,4 @@ +--- # # Copyright (C) 2019-2023 vdaas.org vald team # @@ -18,68 +19,81 @@ kind: ConfigMap metadata: name: vald-benchmark-operator-config labels: - app.kubernetes.io/name: vald - app.kubernetes.io/component: vald-benchmark-operator + app.kubernetes.io/name: vald-benchmark-operator + helm.sh/chart: vald-benchmark-operator-v1.7.5 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/instance: release-name + app.kubernetes.io/version: v1.7.5 + app.kubernetes.io/component: benchmark-operator data: config.yaml: | --- version: v0.0.0 - time_zone: JST + time_zone: "" logging: - format: raw - level: debug - logger: glg + logger: "glg" + level: "debug" + format: "raw" server_config: servers: - name: grpc host: 0.0.0.0 port: 8081 + mode: GRPC + probe_wait_time: "3s" + network: "tcp" + socket_path: "" grpc: bidirectional_stream_concurrency: 20 connection_timeout: "" - header_table_size: 0 - initial_conn_window_size: 0 + max_receive_message_size: 0 + max_send_message_size: 0 initial_window_size: 0 - interceptors: [] + initial_conn_window_size: 0 keepalive: + max_conn_idle: "" max_conn_age: "" max_conn_age_grace: "" - max_conn_idle: "" - time: "" - timeout: "" - max_header_list_size: 0 - max_receive_message_size: 0 - max_send_message_size: 0 - read_buffer_size: 0 + time: "120s" + timeout: "30s" + min_time: "60s" + permit_without_stream: true write_buffer_size: 0 - mode: GRPC - probe_wait_time: 3s + read_buffer_size: 0 + max_header_list_size: 0 + header_table_size: 0 + interceptors: [] + enable_reflection: true restart: true health_check_servers: - name: liveness host: 0.0.0.0 port: 3000 + mode: "" + probe_wait_time: "3s" + network: "tcp" + socket_path: "" http: + shutdown_duration: "5s" handler_timeout: "" idle_timeout: "" read_header_timeout: "" read_timeout: "" - shutdown_duration: 5s write_timeout: "" - mode: "" - probe_wait_time: 3s - name: readiness host: 0.0.0.0 port: 3001 + mode: "" + probe_wait_time: "3s" + network: "tcp" + socket_path: "" http: + shutdown_duration: "0s" handler_timeout: "" idle_timeout: "" read_header_timeout: "" read_timeout: "" - shutdown_duration: 0s write_timeout: "" - mode: "" - probe_wait_time: 3s metrics_servers: startup_strategy: - liveness @@ -87,80 +101,40 @@ data: - readiness full_shutdown_duration: 600s tls: - ca: /path/to/ca - cert: /path/to/cert enabled: false - key: /path/to/key + cert: "/path/to/cert" + key: "/path/to/key" + ca: "/path/to/ca" + insecure_skip_verify: false observability: enabled: false - collector: - duration: 5s - metrics: - enable_cgo: true - enable_goroutine: true - enable_memory: true - enable_version_info: true - version_info_labels: - - vald_version - - server_name - - git_commit - - build_time - - go_version - - go_os - - go_arch - - ngt_version + otlp: + collector_endpoint: "" + trace_batch_timeout: "1s" + trace_export_timeout: "1m" + trace_max_export_batch_size: 1024 + trace_max_queue_size: 256 + metrics_export_interval: "1s" + metrics_export_timeout: "1m" + attribute: + namespace: "_MY_POD_NAMESPACE_" + pod_name: "_MY_POD_NAME_" + node_name: "_MY_NODE_NAME_" + service_name: "vald-benchmark-operator" + metrics: + enable_version_info: true + version_info_labels: + - vald_version + - server_name + - git_commit + - build_time + - go_version + - go_os + - go_arch + - ngt_version + enable_memory: true + enable_goroutine: true + enable_cgo: true trace: enabled: false sampling_rate: 1 - prometheus: - enabled: false - endpoint: /metrics - namespace: vald - jaeger: - enabled: false - collector_endpoint: "" - agent_endpoint: "jaeger-agent.default.svc.cluster.local:6831" - username: "" - password: "" - service_name: "vald-benchmark-job" - buffer_max_count: 10 - stackdriver: - project_id: "" - client: - api_key: "" - audiences: [] - authentication_enabled: true - credentials_file: "" - credentials_json: "" - endpoint: "" - quota_project: "" - request_reason: "" - scopes: [] - telemetry_enabled: true - user_agent: "" - exporter: - bundle_count_threshold: 0 - bundle_delay_threshold: "0" - location: "" - metric_prefix: vald.vdaas.org - monitoring_enabled: false - number_of_workers: 1 - reporting_interval: 1m - skip_cmd: false - timeout: 5s - trace_spans_buffer_max_bytes: 0 - tracing_enabled: false - profiler: - enabled: false - service: "vald-benchmark-job" - service_version: "" - debug_logging: false - mutex_profiling: true - cpu_profiling: true - alloc_profiling: true - heap_profiling: true - goroutine_profiling: true - alloc_force_gc: false - api_addr: "" - instance: "" - zone: "" diff --git a/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml b/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml new file mode 100644 index 0000000000..de85714816 --- /dev/null +++ b/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml @@ -0,0 +1,362 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: valdbenchmarkjobs.vald.vdaas.org +spec: + group: vald.vdaas.org + names: + kind: ValdBenchmarkJob + listKind: ValdBenchmarkJobList + plural: valdbenchmarkjobs + singular: valdbenchmarkjob + shortNames: + - vbj + - vbjs + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - jsonPath: .spec.replica + name: REPLICAS + type: integer + - jsonPath: .status + name: STATUS + type: string + schema: + openAPIV3Schema: + description: ValdBenchmarkJob is the Schema for the valdbenchmarkjobs API + type: object + properties: + apiVersion: + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + status: + description: ValdBenchmarkJobStatus defines the observed state of ValdBenchmarkJob + enum: + - NotReady + - Completed + - Available + - Healthy + type: string + spec: + type: object + properties: + client_config: + type: object + properties: + addrs: + type: array + items: + type: string + backoff: + type: object + properties: + backoff_factor: + type: number + backoff_time_limit: + type: string + enable_error_log: + type: boolean + initial_duration: + type: string + jitter_limit: + type: string + maximum_duration: + type: string + retry_count: + type: integer + call_option: + type: object + x-kubernetes-preserve-unknown-fields: true + circuit_breaker: + type: object + properties: + closed_error_rate: + type: number + closed_refresh_timeout: + type: string + half_open_error_rate: + type: number + min_samples: + type: integer + open_timeout: + type: string + connection_pool: + type: object + properties: + enable_dns_resolver: + type: boolean + enable_rebalance: + type: boolean + old_conn_close_duration: + type: string + rebalance_duration: + type: string + size: + type: integer + dial_option: + type: object + properties: + backoff_base_delay: + type: string + backoff_jitter: + type: number + backoff_max_delay: + type: string + backoff_multiplier: + type: number + enable_backoff: + type: boolean + initial_connection_window_size: + type: integer + initial_window_size: + type: integer + insecure: + type: boolean + interceptors: + type: array + items: + type: string + enum: + - TraceInterceptor + keepalive: + type: object + properties: + permit_without_stream: + type: boolean + time: + type: string + timeout: + type: string + max_msg_size: + type: integer + min_connection_timeout: + type: string + net: + type: object + properties: + dialer: + type: object + properties: + dual_stack_enabled: + type: boolean + keepalive: + type: string + timeout: + type: string + dns: + type: object + properties: + cache_enabled: + type: boolean + cache_expiration: + type: string + refresh_duration: + type: string + socket_option: + type: object + properties: + ip_recover_destination_addr: + type: boolean + ip_transparent: + type: boolean + reuse_addr: + type: boolean + reuse_port: + type: boolean + tcp_cork: + type: boolean + tcp_defer_accept: + type: boolean + tcp_fast_open: + type: boolean + tcp_no_delay: + type: boolean + tcp_quick_ack: + type: boolean + tls: + type: object + properties: + ca: + type: string + cert: + type: string + enabled: + type: boolean + insecure_skip_verify: + type: boolean + key: + type: string + read_buffer_size: + type: integer + timeout: + type: string + write_buffer_size: + type: integer + health_check_duration: + type: string + max_recv_msg_size: + type: integer + max_retry_rpc_buffer_size: + type: integer + max_send_msg_size: + type: integer + tls: + type: object + properties: + ca: + type: string + cert: + type: string + enabled: + type: boolean + insecure_skip_verify: + type: boolean + key: + type: string + wait_for_ready: + type: boolean + dataset: + type: object + properties: + group: + type: string + minLength: 1 + indexes: + type: integer + minimum: 0 + name: + type: string + enum: + - fashion-mnist + range: + type: object + properties: + end: + type: integer + minimum: 1 + start: + type: integer + minimum: 1 + required: + - name + - indexes + - group + - range + dimension: + type: integer + minimum: 1 + insert_config: + type: object + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + job_type: + type: string + enum: + - insert + - update + - upsert + - search + - remove + - get_object + - exists + object_config: + type: object + properties: + filter_config: + type: object + properties: + host: + type: string + remove_config: + type: object + properties: + skip_strict_exist_check: + type: boolean + timestamp: + type: string + repetition: + type: integer + minimum: 1 + replica: + type: integer + minimum: 1 + rps: + type: integer + maximum: 65535 + minimum: 0 + rules: + type: array + items: + type: string + search_config: + type: object + properties: + epsilon: + type: number + min_num: + type: integer + num: + type: integer + radius: + type: number + timeout: + type: string + target: + type: object + properties: + host: + type: string + minLength: 1 + port: + type: integer + maximum: 65535 + minimum: 0 + required: + - host + - port + update_config: + type: object + properties: + disable_balance_update: + type: boolean + skip_strict_exist_check: + type: boolean + timestamp: + type: string + upsert_config: + type: object + properties: + disable_balance_update: + type: boolean + skip_strict_exist_check: + type: boolean + timestamp: + type: string diff --git a/k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml b/k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml new file mode 100644 index 0000000000..3451a48b38 --- /dev/null +++ b/k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml @@ -0,0 +1,510 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: valdbenchmarkoperatorreleases.vald.vdaas.org +spec: + group: vald.vdaas.org + names: + kind: ValdBenchmarkOperatorRelease + listKind: ValdBenchmarkOperatorReleaseList + plural: valdbenchmarkoperatorreleases + singular: valdbenchmarkoperatorrelease + shortNames: + - vbor + - vbors + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - jsonPath: .status + name: STATUS + type: string + schema: + openAPIV3Schema: + description: ValdBenchmarkScenario is the Schema for the valdbenchmarkscenarios API + type: object + properties: + apiVersion: + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + status: + description: ValdBenchmarkScenarioStatus defines the observed state of ValdBenchmarkScenario + enum: + - NotReady + - Completed + - Available + - Healthy + type: string + spec: + type: object + properties: + affinity: + type: object + x-kubernetes-preserve-unknown-fields: true + annotations: + type: object + x-kubernetes-preserve-unknown-fields: true + image: + type: object + properties: + pullPolicy: + type: string + enum: + - Always + - Never + - IfNotPresent + repository: + type: string + tag: + type: string + logging: + type: object + properties: + format: + type: string + enum: + - raw + - json + level: + type: string + enum: + - debug + - info + - warn + - error + - fatal + logger: + type: string + enum: + - glg + - zap + name: + type: string + nodeSelector: + type: object + x-kubernetes-preserve-unknown-fields: true + observability: + type: object + properties: + enabled: + type: boolean + otlp: + type: object + properties: + attribute: + type: object + properties: + metrics: + type: object + properties: + enable_cgo: + type: boolean + enable_goroutine: + type: boolean + enable_memory: + type: boolean + enable_version_info: + type: boolean + version_info_labels: + type: array + items: + type: string + namespace: + type: string + node_name: + type: string + pod_name: + type: string + service_name: + type: string + collector_endpoint: + type: string + metrics_export_interval: + type: string + metrics_export_timeout: + type: string + trace_batch_timeout: + type: string + trace_export_timeout: + type: string + trace_max_export_batch_size: + type: integer + trace_max_queue_size: + type: integer + trace: + type: object + properties: + enabled: + type: boolean + sampling_rate: + type: integer + podAnnotations: + type: object + x-kubernetes-preserve-unknown-fields: true + podSecurityContext: + type: object + x-kubernetes-preserve-unknown-fields: true + rbac: + type: object + properties: + create: + type: boolean + name: + type: string + replicas: + type: integer + resources: + type: object + properties: + limits: + type: object + x-kubernetes-preserve-unknown-fields: true + requests: + type: object + x-kubernetes-preserve-unknown-fields: true + securityContext: + type: object + x-kubernetes-preserve-unknown-fields: true + server_config: + type: object + properties: + full_shutdown_duration: + type: string + healths: + type: object + properties: + liveness: + type: object + properties: + enabled: + type: boolean + host: + type: string + livenessProbe: + type: object + properties: + failureThreshold: + type: integer + httpGet: + type: object + properties: + path: + type: string + port: + type: string + scheme: + type: string + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + port: + type: integer + server: + type: object + properties: + http: + type: object + properties: + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + timeout: + type: string + write_timeout: + type: string + mode: + type: string + network: + type: string + probe_wait_time: + type: string + socket_path: + type: string + servicePort: + type: integer + readiness: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: integer + readinessProbe: + type: object + properties: + failureThreshold: + type: integer + httpGet: + type: object + properties: + path: + type: string + port: + type: string + scheme: + type: string + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + server: + type: object + properties: + http: + type: object + properties: + handler_timeout: + type: string + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + write_timeout: + type: string + mode: + type: string + network: + type: string + probe_wait_time: + type: string + socket_path: + type: string + servicePort: + type: integer + startup: + type: object + properties: + enabled: + type: boolean + startupProbe: + type: object + properties: + failureThreshold: + type: integer + httpGet: + type: object + properties: + path: + type: string + port: + type: string + scheme: + type: string + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + metrics: + type: object + properties: + pprof: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: integer + server: + type: object + properties: + http: + type: object + properties: + handler_timeout: + type: string + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + write_timeout: + type: string + mode: + type: string + network: + type: string + probe_wait_time: + type: string + socket_path: + type: string + servers: + type: object + properties: + grpc: + type: object + properties: + enabled: + type: boolean + host: + type: string + name: + type: string + port: + type: integer + server: + type: object + properties: + grpc: + type: object + properties: + bidirectional_stream_concurrency: + type: integer + connection_timeout: + type: string + enable_reflection: + type: boolean + header_table_size: + type: integer + initial_conn_window_size: + type: integer + initial_window_size: + type: integer + interceptors: + type: array + items: + type: string + keepalive: + type: object + properties: + max_conn_age: + type: string + max_conn_age_grace: + type: string + max_conn_idle: + type: string + min_time: + type: string + permit_without_stream: + type: boolean + time: + type: string + timeout: + type: string + max_header_list_size: + type: integer + max_receive_message_size: + type: integer + max_send_msg_size: + type: integer + read_buffer_size: + type: integer + write_buffer_size: + type: integer + mode: + type: string + network: + type: string + probe_wait_time: + type: string + restart: + type: boolean + socket_path: + type: string + servicePort: + type: integer + rest: + type: object + properties: + enabled: + type: boolean + tls: + type: object + properties: + ca: + type: string + cert: + type: string + enabled: + type: boolean + insecure_skip_verify: + type: boolean + key: + type: string + service: + type: object + properties: + annotations: + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + type: boolean + externalTrafficPolicy: + type: string + labels: + type: object + x-kubernetes-preserve-unknown-fields: true + type: + type: string + enum: + - ClusterIP + - LoadBalancer + - NodePort + serviceAccount: + type: object + properties: + create: + type: boolean + name: + type: string + time_zone: + type: string + tolerations: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string diff --git a/k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml b/k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml new file mode 100644 index 0000000000..f705e7df6b --- /dev/null +++ b/k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml @@ -0,0 +1,109 @@ +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: valdbenchmarkscenarios.vald.vdaas.org +spec: + group: vald.vdaas.org + names: + kind: ValdBenchmarkScenario + listKind: ValdBenchmarkScenarioList + plural: valdbenchmarkscenarios + singular: valdbenchmarkscenario + shortNames: + - vbo + - vbos + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - jsonPath: .status + name: STATUS + type: string + schema: + openAPIV3Schema: + description: ValdBenchmarkScenario is the Schema for the valdbenchmarkscenarios API + type: object + properties: + apiVersion: + description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + status: + description: ValdBenchmarkScenarioStatus defines the observed state of ValdBenchmarkScenario + enum: + - NotReady + - Completed + - Available + - Healthy + type: string + spec: + type: object + properties: + dataset: + type: object + properties: + group: + type: string + minLength: 1 + indexes: + type: integer + minimum: 0 + name: + type: string + enum: + - fashion-mnist + range: + type: object + properties: + end: + type: integer + minimum: 1 + start: + type: integer + minimum: 1 + required: + - name + - indexes + - group + - range + jobs: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + target: + type: object + properties: + host: + type: string + minLength: 1 + port: + type: integer + maximum: 65535 + minimum: 0 + required: + - host + - port diff --git a/k8s/tools/benchmark/operator/deployment.yaml b/k8s/tools/benchmark/operator/deployment.yaml index a5a5bd241b..7930e06334 100644 --- a/k8s/tools/benchmark/operator/deployment.yaml +++ b/k8s/tools/benchmark/operator/deployment.yaml @@ -1,3 +1,4 @@ +--- # # Copyright (C) 2019-2023 vdaas.org vald team # @@ -17,25 +18,63 @@ apiVersion: apps/v1 kind: Deployment metadata: name: vald-benchmark-operator + namespace: default labels: app: vald-benchmark-operator + app.kubernetes.io/name: vald-benchmark-operator + helm.sh/chart: vald-benchmark-operator-v1.7.5 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/instance: release-name + app.kubernetes.io/version: v1.7.5 + app.kubernetes.io/component: benchmark-operator spec: replicas: 1 selector: matchLabels: - app: vald-benchmark-operator + name: vald-benchmark-operator template: metadata: labels: - app: vald-benchmark-operator + name: vald-benchmark-operator + app.kubernetes.io/name: vald-benchmark-operator + app.kubernetes.io/instance: release-name + app.kubernetes.io/component: benchmark-operator spec: + serviceAccountName: vald-benchmark-operator containers: - name: vald-benchmark-operator - image: vdaas/vald-benchmark-operator:latest + image: "vdaas/vald-benchmark-operator:v1.7.5" imagePullPolicy: Always - volumeMounts: - - name: vald-benchmark-operator-config - mountPath: /etc/server/ + livenessProbe: + failureThreshold: 2 + httpGet: + path: /liveness + port: liveness + scheme: HTTP + initialDelaySeconds: 15 + periodSeconds: 20 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + failureThreshold: 2 + httpGet: + path: /readiness + port: readiness + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 3 + successThreshold: 1 + timeoutSeconds: 2 + startupProbe: + failureThreshold: 30 + httpGet: + path: /liveness + port: liveness + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 2 ports: - name: liveness protocol: TCP @@ -43,7 +82,25 @@ spec: - name: readiness protocol: TCP containerPort: 3001 - serviceAccountName: vald-benchmark-operator + - name: grpc + protocol: TCP + containerPort: 8081 + - name: pprof + protocol: TCP + containerPort: 6060 + resources: + limits: + cpu: 300m + memory: 300Mi + requests: + cpu: 200m + memory: 200Mi + volumeMounts: + - name: vald-benchmark-operator-config + mountPath: /etc/server + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + restartPolicy: Always volumes: - name: vald-benchmark-operator-config configMap: diff --git a/k8s/tools/benchmark/operator/service.yaml b/k8s/tools/benchmark/operator/service.yaml new file mode 100644 index 0000000000..0cb8ba3770 --- /dev/null +++ b/k8s/tools/benchmark/operator/service.yaml @@ -0,0 +1,38 @@ +--- +# +# Copyright (C) 2019-2023 vdaas.org vald team +# +# 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 +# +# https://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. +# +apiVersion: v1 +kind: Service +metadata: + name: vald-benchmark-operator + labels: + app.kubernetes.io/name: vald-benchmark-operator + helm.sh/chart: vald-benchmark-operator-v1.7.5 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/instance: release-name + app.kubernetes.io/version: v1.7.5 + app.kubernetes.io/component: helm-operator +spec: + ports: + - name: prometheus + port: 6060 + targetPort: 6060 + protocol: TCP + selector: + app.kubernetes.io/name: vald-benchmark-operator + app.kubernetes.io/component: helm-operator + clusterIP: None + type: ClusterIP diff --git a/k8s/tools/benchmark/operator/serviceaccount.yaml b/k8s/tools/benchmark/operator/serviceaccount.yaml index 617a413a16..cf7ac3908d 100644 --- a/k8s/tools/benchmark/operator/serviceaccount.yaml +++ b/k8s/tools/benchmark/operator/serviceaccount.yaml @@ -1,3 +1,4 @@ +--- # # Copyright (C) 2019-2023 vdaas.org vald team # From 46f63be8337e10a21ed8258d899cd5e53b11091a Mon Sep 17 00:00:00 2001 From: Kiichiro YUKAWA Date: Fri, 23 Jun 2023 16:35:41 +0900 Subject: [PATCH 05/11] Refactor helm template and operator logic (#2043) * Implement base of continuous benchmark tool (#1776) * Create Continuous Bench Search Job tool (#1733) * :sparkles: create bench job search tools Signed-off-by: vankichi * :sparkles: add load hdf5 functions Signed-off-by: vankichi * :recycle: fix format Signed-off-by: vankichi * :recycle: fix docker and use hdf5 data Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: refactor benchmark job Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: fix proto Signed-off-by: vankichi * :green_heart: add benchmark job image build ci Signed-off-by: vankichi * :green_heart: invest Signed-off-by: vankichi * Revert ":green_heart: invest" This reverts commit f0f585ccf71b1c95a88559941557a27774096e69. * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply code review Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Hiroto Funakoshi * :sparkles: apply from feedback Signed-off-by: vankichi * Update internal/config/benchmark.go Co-authored-by: Yusuke Kato * :recycle: change directory path Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi * Add crds for continuous benchmark tools (#1789) * :sparkles: add crds for continuous benchmark operator Signed-off-by: vankichi * :sparkles: add benchmark operator/job scheme Signed-off-by: vankichi * :sparkles: rename package names and add doc.go Signed-off-by: vankichi * :sparkles: create runtime object Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply feedback Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato * Add Job reconciler & Change directory constitution of internal/k8s for benchmark (#1825) * :sparkles: :recycle: add Job reconciler & use scenario instead of operator Signed-off-by: vankichi * :recycle: fix format & rename file Signed-off-by: vankichi Signed-off-by: vankichi * Add benchmark operator framework (#1916) * :sparkles: impl benchmark reconciler Signed-off-by: vankichi * :sparkles: create benchmark operator framework Signed-off-by: vankichi * :recycle: remove unness changes Signed-off-by: vankichi Signed-off-by: vankichi * Format code with prettier and gofumpt * impl reconcile logic for create benchmark job (#1923) * :sparkles: impl reconcile logic for create benchmark job Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: fix Signed-off-by: vankichi * :recycle: refactor continuous benchmark's crds Signed-off-by: vankichi * :recycle: resolve error due to update conn bench crds for pkg/tools/benchmark/job Signed-off-by: vankichi * :recycle: refactor continuous benchmark job logic Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: update charts Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: rafactor con bench config and bug fix reconcile logic Signed-off-by: vankichi * :bug: Bugfix: fix typo and recall function logic Signed-off-by: vankichi * :recycle: refactor pkg benchmark job Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :sparkles: impl status handle of continuous benchmark crds (#1955) Signed-off-by: vankichi * Impl benchmark jobs (#1977) * Implement base of continuous benchmark tool (#1776) * Create Continuous Bench Search Job tool (#1733) * :sparkles: create bench job search tools Signed-off-by: vankichi * :sparkles: add load hdf5 functions Signed-off-by: vankichi * :recycle: fix format Signed-off-by: vankichi * :recycle: fix docker and use hdf5 data Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: refactor benchmark job Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: fix proto Signed-off-by: vankichi * :green_heart: add benchmark job image build ci Signed-off-by: vankichi * :green_heart: invest Signed-off-by: vankichi * Revert ":green_heart: invest" This reverts commit f0f585ccf71b1c95a88559941557a27774096e69. * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply code review Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Hiroto Funakoshi * :sparkles: apply from feedback Signed-off-by: vankichi * Update internal/config/benchmark.go Co-authored-by: Yusuke Kato * :recycle: change directory path Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi * Add crds for continuous benchmark tools (#1789) * :sparkles: add crds for continuous benchmark operator Signed-off-by: vankichi * :sparkles: add benchmark operator/job scheme Signed-off-by: vankichi * :sparkles: rename package names and add doc.go Signed-off-by: vankichi * :sparkles: create runtime object Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply feedback Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato * Add Job reconciler & Change directory constitution of internal/k8s for benchmark (#1825) * :sparkles: :recycle: add Job reconciler & use scenario instead of operator Signed-off-by: vankichi * :recycle: fix format & rename file Signed-off-by: vankichi Signed-off-by: vankichi * Add benchmark operator framework (#1916) * :sparkles: impl benchmark reconciler Signed-off-by: vankichi * :sparkles: create benchmark operator framework Signed-off-by: vankichi * :recycle: remove unness changes Signed-off-by: vankichi Signed-off-by: vankichi * Format code with prettier and gofumpt * impl reconcile logic for create benchmark job (#1923) * :sparkles: impl reconcile logic for create benchmark job Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: fix Signed-off-by: vankichi * :recycle: refactor continuous benchmark's crds Signed-off-by: vankichi * :recycle: resolve error due to update conn bench crds for pkg/tools/benchmark/job Signed-off-by: vankichi * :recycle: refactor continuous benchmark job logic Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: update charts Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: rafactor con bench config and bug fix reconcile logic Signed-off-by: vankichi * :bug: Bugfix: fix typo and recall function logic Signed-off-by: vankichi * :recycle: refactor pkg benchmark job Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :sparkles: impl benchmark jobs Signed-off-by: vankichi * :recycle: apply feedback Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :recycle: Refactor helm template and operator logic Signed-off-by: vankichi * :recycle: Add download original dataset URL option Signed-off-by: vankichi * :recycle: Set docker image location at the benchmark operator configmap and use it when information is set Signed-off-by: vankichi * add search algorithm benchmark and update search aggregation algo Signed-off-by: kpango * :sparkles: Add search result aggregation option Signed-off-by: vankichi * style: Format code with prettier and gofumpt * Improve job performance (#2061) * :bug: Fix job function to apply rate limiter * :recycle: Add pyroscope setting Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :bug: Fix build error Signed-off-by: vankichi --------- Signed-off-by: vankichi * :bug: Fix docker file and add concurrencyLimit for job goroutine Signed-off-by: vankichi * :recycle: Fix job_template.go by feedback Signed-off-by: vankichi * :recycle: Fix job logic by feedback Signed-off-by: vankichi * :recycle: Fix Signed-off-by: vankichi --------- Signed-off-by: vankichi Signed-off-by: kpango Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --- .../workflows/dockers-benchmark-job-image.yml | 1 + .../crds/valdbenchmarkjob.yaml | 26 +- .../crds/valdbenchmarkoperatorrelease.yaml | 13 + .../crds/valdbenchmarkscenario.yaml | 10 +- .../job-values.schema.json | 38 +- .../scenario-values.schema.json | 9 +- .../schemas/job-values.yaml | 21 +- .../schemas/scenario-values.yaml | 9 +- .../templates/clusterrole.yaml | 1 + .../templates/configmap.yaml | 3 + .../templates/deployment.yaml | 14 + .../values.schema.json | 21 +- charts/vald-benchmark-operator/values.yaml | 12 + .../values/benchmark-job.yaml | 1 + .../values/benchmark-scenario.yaml | 4 +- dockers/tools/benchmark/job/Dockerfile | 46 ++- dockers/tools/benchmark/operator/Dockerfile | 17 +- go.mod | 1 + internal/config/benchmark.go | 30 +- internal/k8s/client/client.go | 6 + internal/k8s/job/job.go | 3 +- .../k8s/vald/benchmark/api/v1/job_types.go | 32 +- internal/k8s/vald/benchmark/job/job.go | 3 +- .../k8s/vald/benchmark/job/job_template.go | 72 +++- .../vald/benchmark/job/job_template_option.go | 76 +++- .../k8s/vald/benchmark/scenario/scenario.go | 6 +- internal/test/data/hdf5/hdf5.go | 54 +-- internal/test/data/hdf5/hdf5_test.go | 64 ++- internal/test/data/hdf5/option.go | 4 + internal/test/data/hdf5/option_test.go | 387 ++++++++++++++++++ k8s/tools/benchmark/operator/clusterrole.yaml | 1 + k8s/tools/benchmark/operator/configmap.yaml | 3 + .../operator/crds/valdbenchmarkjob.yaml | 26 +- .../crds/valdbenchmarkoperatorrelease.yaml | 13 + .../operator/crds/valdbenchmarkscenario.yaml | 10 +- k8s/tools/benchmark/operator/deployment.yaml | 5 + pkg/tools/benchmark/job/config/config.go | 2 + pkg/tools/benchmark/job/service/insert.go | 77 ++-- pkg/tools/benchmark/job/service/job.go | 83 ++-- pkg/tools/benchmark/job/service/object.go | 143 ++++--- pkg/tools/benchmark/job/service/option.go | 18 +- pkg/tools/benchmark/job/service/remove.go | 71 ++-- pkg/tools/benchmark/job/service/search.go | 181 +++++--- pkg/tools/benchmark/job/service/update.go | 80 ++-- pkg/tools/benchmark/job/service/upsert.go | 78 ++-- pkg/tools/benchmark/job/usecase/benchmarkd.go | 36 +- pkg/tools/benchmark/operator/config/config.go | 9 + .../benchmark/operator/service/operator.go | 135 ++++-- .../benchmark/operator/service/option.go | 22 + .../benchmark/operator/usecase/benchmarkd.go | 6 + 50 files changed, 1513 insertions(+), 470 deletions(-) create mode 100644 internal/test/data/hdf5/option_test.go diff --git a/.github/workflows/dockers-benchmark-job-image.yml b/.github/workflows/dockers-benchmark-job-image.yml index 13d8aa8a16..f38149a7c3 100644 --- a/.github/workflows/dockers-benchmark-job-image.yml +++ b/.github/workflows/dockers-benchmark-job-image.yml @@ -47,6 +47,7 @@ on: - "internal/**" - "!internal/**/*_test.go" - "!internal/db/**" + - "!internal/k8s/**" - "apis/grpc/**" - "pkg/benchmark/job/**" - "cmd/benchmark/job/**" diff --git a/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml b/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml index de85714816..d85ea0c12b 100644 --- a/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml +++ b/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml @@ -240,6 +240,10 @@ spec: type: string wait_for_ready: type: boolean + concurrency_limit: + type: integer + maximum: 65535 + minimum: 0 dataset: type: object properties: @@ -252,6 +256,7 @@ spec: name: type: string enum: + - original - fashion-mnist range: type: object @@ -262,6 +267,11 @@ spec: start: type: integer minimum: 1 + required: + - start + - end + url: + type: string required: - name - indexes @@ -285,7 +295,7 @@ spec: - upsert - search - remove - - get_object + - getobject - exists object_config: type: object @@ -319,6 +329,16 @@ spec: search_config: type: object properties: + aggregation_algorithm: + type: string + enum: + - Unknown + - ConcurrentQueue + - SortSlice + - SortPoolSlice + - PairingHeap + enable_linear_search: + type: boolean epsilon: type: number min_num: @@ -342,6 +362,10 @@ spec: required: - host - port + ttl_seconds_after_finished: + type: integer + maximum: 65535 + minimum: 0 update_config: type: object properties: diff --git a/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml b/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml index 3451a48b38..28ab299701 100644 --- a/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml +++ b/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml @@ -81,6 +81,19 @@ spec: type: string tag: type: string + job_image: + type: object + properties: + pullPolicy: + type: string + enum: + - Always + - Never + - IfNotPresent + repository: + type: string + tag: + type: string logging: type: object properties: diff --git a/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml b/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml index f705e7df6b..871f59b390 100644 --- a/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml +++ b/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml @@ -25,8 +25,8 @@ spec: plural: valdbenchmarkscenarios singular: valdbenchmarkscenario shortNames: - - vbo - - vbos + - vbs + - vbss scope: Namespaced versions: - name: v1 @@ -74,6 +74,7 @@ spec: name: type: string enum: + - original - fashion-mnist range: type: object @@ -84,6 +85,11 @@ spec: start: type: integer minimum: 1 + required: + - start + - end + url: + type: string required: - name - indexes diff --git a/charts/vald-benchmark-operator/job-values.schema.json b/charts/vald-benchmark-operator/job-values.schema.json index 24f2f5b91f..40759dd01b 100644 --- a/charts/vald-benchmark-operator/job-values.schema.json +++ b/charts/vald-benchmark-operator/job-values.schema.json @@ -288,6 +288,12 @@ "wait_for_ready": { "type": "boolean" } } }, + "concurrency_limit": { + "type": "integer", + "description": "concurrency_limit represents the goroutine limit count. It affects the job performance.", + "maximum": 65535, + "minimum": 0 + }, "dataset": { "type": "object", "description": "dataset information", @@ -305,7 +311,7 @@ "name": { "type": "string", "description": "the name of dataset", - "enum": ["fashion-mnist"] + "enum": ["original", "fashion-mnist"] }, "range": { "type": "object", @@ -321,7 +327,12 @@ "description": "start index number", "minimum": 1 } - } + }, + "required": ["start", "end"] + }, + "url": { + "type": "string", + "description": "the dataset url which is used for executing benchmark job with user defined hdf5 file" } }, "required": ["name", "indexes", "group", "range"] @@ -351,7 +362,7 @@ "upsert", "search", "remove", - "get_object", + "getobject", "exists" ] }, @@ -404,6 +415,21 @@ "type": "object", "description": "upsert config", "properties": { + "aggregation_algorithm": { + "type": "string", + "description": "search result aggregation algorithm", + "enum": [ + "Unknown", + "ConcurrentQueue", + "SortSlice", + "SortPoolSlice", + "PairingHeap" + ] + }, + "enable_linear_search": { + "type": "boolean", + "description": "enable linear search for calculation recall" + }, "epsilon": { "type": "number", "description": "epsilon" }, "min_num": { "type": "integer", @@ -435,6 +461,12 @@ }, "required": ["host", "port"] }, + "ttl_seconds_after_finished": { + "type": "integer", + "description": "limits the lifetime of a Job that has finished execution.", + "maximum": 65535, + "minimum": 0 + }, "update_config": { "type": "object", "description": "update config", diff --git a/charts/vald-benchmark-operator/scenario-values.schema.json b/charts/vald-benchmark-operator/scenario-values.schema.json index e3e549e69d..c4aadae03c 100644 --- a/charts/vald-benchmark-operator/scenario-values.schema.json +++ b/charts/vald-benchmark-operator/scenario-values.schema.json @@ -20,7 +20,7 @@ "name": { "type": "string", "description": "the name of dataset", - "enum": ["fashion-mnist"] + "enum": ["original", "fashion-mnist"] }, "range": { "type": "object", @@ -36,7 +36,12 @@ "description": "start index number", "minimum": 1 } - } + }, + "required": ["start", "end"] + }, + "url": { + "type": "string", + "description": "the dataset url which is used for executing benchmark job with user defined hdf5 file" } }, "required": ["name", "indexes", "group", "range"] diff --git a/charts/vald-benchmark-operator/schemas/job-values.yaml b/charts/vald-benchmark-operator/schemas/job-values.yaml index 47fafd98b0..021418e9f5 100644 --- a/charts/vald-benchmark-operator/schemas/job-values.yaml +++ b/charts/vald-benchmark-operator/schemas/job-values.yaml @@ -27,7 +27,7 @@ target: # @schema {"name": "dataset", "type": "object", "required": ["name", "indexes", "group", "range"]} # dataset -- dataset information dataset: - # @schema {"name": "dataset.name", "type": "string", "enum": ["fashion-mnist"] } + # @schema {"name": "dataset.name", "type": "string", "enum": ["original", "fashion-mnist"] } # dataset.name -- the name of dataset name: "fashion-mnist" # @schema {"name": "dataset.indexes", "type": "integer", "minimum": 0} @@ -36,7 +36,7 @@ dataset: # @schema {"name": "dataset.group", "type": "string", "minLength": 1} # dataset.group -- the hdf5 group name of dataset group: "test" - # @schema {"name": "dataset.range", "type": "object", "range": ["start", "port"]} + # @schema {"name": "dataset.range", "type": "object", "required": ["start", "end"]} # dataset.range -- the data range of indexes range: # @schema {"name": "dataset.range.start", "type": "integer", "minimum": 1} @@ -45,6 +45,9 @@ dataset: # @schema {"name": "dataset.range.end", "type": "integer", "minimum": 1} # dataset.range.end -- end index number end: 1000 + # @schema {"name": "dataset.url", "type": "string"} + # dataset.url -- the dataset url which is used for executing benchmark job with user defined hdf5 file + url: "" # @schema {"name": "dimension", "type": "integer", "minimum": 1} # dimension -- vector dimension dimension: 784 @@ -54,7 +57,7 @@ replica: 1 # @schema {"name": "repetition", "type": "integer", "minimum": 1} # repetition -- the number of repeat job repetition: 1 -# @schema {"name": "job_type", "type": "string", "enum": ["insert", "update", "upsert", "search", "remove", "get_object", "exists"]} +# @schema {"name": "job_type", "type": "string", "enum": ["insert", "update", "upsert", "search", "remove", "getobject", "exists"]} # job_type -- job type name job_type: "search" # @schema {"name": "insert_config", "type": "object"} @@ -108,6 +111,12 @@ search_config: # @schema {"name": "search_config.timeout", "type": "string"} # search_config.timeout -- search operation timeout timeout: "10s" + # @schema {"name": "search_config.enable_linear_search", "type": "boolean"} + # search_config.enable_linear_search -- enable linear search for calculation recall + enable_linear_search: true + # @schema {"name": "search_config.aggregation_algorithm", "type": "string", "enum": ["Unknown", "ConcurrentQueue", "SortSlice", "SortPoolSlice", "PairingHeap"]} + # search_config.aggregation_algorithm -- search result aggregation algorithm + aggregation_algorithm: "Unknown" # @schema {"name": "remove_config", "type": "object"} # remove_config -- remove config @@ -358,3 +367,9 @@ rules: [] # @schema {"name": "rps", "type": "integer", "minimum": 0, "maximum": 65535} # rps -- desired request per sec rps: 1000 +# @schema {"name": "concurrency_limit", "type": "integer", "minimum": 0, "maximum": 65535} +# concurrency_limit -- concurrency_limit represents the goroutine limit count. It affects the job performance. +concurrency_limit: 200 +# @schema {"name": "ttl_seconds_after_finished", "type": "integer", "minimum": 0, "maximum": 65535} +# ttl_seconds_after_finished -- limits the lifetime of a Job that has finished execution. +ttl_seconds_after_finished: 10 diff --git a/charts/vald-benchmark-operator/schemas/scenario-values.yaml b/charts/vald-benchmark-operator/schemas/scenario-values.yaml index dedc6b51b5..e33b76f5f5 100644 --- a/charts/vald-benchmark-operator/schemas/scenario-values.yaml +++ b/charts/vald-benchmark-operator/schemas/scenario-values.yaml @@ -28,7 +28,7 @@ target: # @schema {"name": "dataset", "type": "object", "required": ["name", "indexes", "group", "range"]} # dataset -- dataset information dataset: - # @schema {"name": "dataset.name", "type": "string", "enum": ["fashion-mnist"] } + # @schema {"name": "dataset.name", "type": "string", "enum": ["original", "fashion-mnist"] } # dataset.name -- the name of dataset name: "fashion-mnist" # @schema {"name": "dataset.indexes", "type": "integer", "minimum": 0} @@ -37,7 +37,7 @@ dataset: # @schema {"name": "dataset.group", "type": "string", "minLength": 1} # dataset.group -- the hdf5 group name of dataset group: "test" - # @schema {"name": "dataset.range", "type": "object", "range": ["start", "port"]} + # @schema {"name": "dataset.range", "type": "object", "required": ["start", "end"]} # dataset.range -- the data range of indexes range: # @schema {"name": "dataset.range.start", "type": "integer", "minimum": 1} @@ -46,6 +46,9 @@ dataset: # @schema {"name": "dataset.range.end", "type": "integer", "minimum": 1} # dataset.range.end -- end index number end: 1000 + # @schema {"name": "dataset.url", "type": "string"} + # dataset.url -- the dataset url which is used for executing benchmark job with user defined hdf5 file + url: "" # @schema {"name": "jobs", "type": "array", "items": {"type": "object"}} jobs: @@ -80,6 +83,7 @@ jobs: num: 10 min_num: 10 timeout: "10s" + enable_linear_search: true remove_config: skip_strict_exist_check: false timestamp: "" @@ -167,3 +171,4 @@ jobs: insecure_skip_verify: false rules: [] rps: 1000 + ttl_seconds_after_finished: 100 diff --git a/charts/vald-benchmark-operator/templates/clusterrole.yaml b/charts/vald-benchmark-operator/templates/clusterrole.yaml index 63fc0c6812..cd41992a5a 100644 --- a/charts/vald-benchmark-operator/templates/clusterrole.yaml +++ b/charts/vald-benchmark-operator/templates/clusterrole.yaml @@ -55,6 +55,7 @@ rules: verbs: - create - delete + - deletecollection - get - list - patch diff --git a/charts/vald-benchmark-operator/templates/configmap.yaml b/charts/vald-benchmark-operator/templates/configmap.yaml index ceb92f2b55..f00359380f 100644 --- a/charts/vald-benchmark-operator/templates/configmap.yaml +++ b/charts/vald-benchmark-operator/templates/configmap.yaml @@ -38,3 +38,6 @@ data: observability: {{- $observability := dict "Values" .Values.observability}} {{- include "vald.observability" $observability | nindent 6 }} + job_image: + image: "{{ .Values.job_image.repository }}:{{ .Values.job_image.tag }}" + pullPolicy: {{ .Values.job_image.pullPolicy }} diff --git a/charts/vald-benchmark-operator/templates/deployment.yaml b/charts/vald-benchmark-operator/templates/deployment.yaml index bff799a214..5be94d8829 100644 --- a/charts/vald-benchmark-operator/templates/deployment.yaml +++ b/charts/vald-benchmark-operator/templates/deployment.yaml @@ -44,7 +44,16 @@ spec: app.kubernetes.io/component: benchmark-operator {{- with .Values.podAnnotations }} annotations: + {{- if .Values.podAnnotations }} {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.server_config.metrics.pprof.enabeld }} + pyroscope.io/scrape: "true" + pyroscope.io/application-name: {{ .Values.name }} + pyroscope.io/profile-cpu-enabled: "true" + pyroscope.io/profile-mem-enabled: "true" + pyroscope.io/port: "{{ .Values.server_config.metrics.pprof.port }}" + {{- end}} {{- end }} spec: serviceAccountName: {{ .Values.serviceAccount.name }} @@ -113,6 +122,11 @@ spec: volumeMounts: - name: {{ .Values.name }}-config mountPath: /etc/server + env: + - name: JOB_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace terminationMessagePath: /dev/termination-log terminationMessagePolicy: File restartPolicy: Always diff --git a/charts/vald-benchmark-operator/values.schema.json b/charts/vald-benchmark-operator/values.schema.json index 6ed6f1764f..57fb5cd25b 100644 --- a/charts/vald-benchmark-operator/values.schema.json +++ b/charts/vald-benchmark-operator/values.schema.json @@ -16,8 +16,25 @@ "description": "image pull policy", "enum": ["Always", "Never", "IfNotPresent"] }, - "repository": { "type": "string", "description": "image repository" }, - "tag": { "type": "string", "description": "image tag" } + "repository": { + "type": "string", + "description": "job image repository" + }, + "tag": { + "type": "string", + "description": "image tag for job docker image" + } + } + }, + "job_image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string", + "enum": ["Always", "Never", "IfNotPresent"] + }, + "repository": { "type": "string" }, + "tag": { "type": "string" } } }, "logging": { diff --git a/charts/vald-benchmark-operator/values.yaml b/charts/vald-benchmark-operator/values.yaml index 587bcd9121..a6c2b16bef 100644 --- a/charts/vald-benchmark-operator/values.yaml +++ b/charts/vald-benchmark-operator/values.yaml @@ -42,6 +42,18 @@ image: # image.pullPolicy -- image pull policy pullPolicy: Always +# @schema {"name": "job_image", "type": "object"} +job_image: + # @schema {"name": "job_image.repository", "type": "string"} + # image.repository -- job image repository + repository: vdaas/vald-benchmark-job + # @schema {"name": "job_image.tag", "type": "string"} + # image.tag -- image tag for job docker image + tag: v1.7.5 + # @schema {"name": "job_image.pullPolicy", "type": "string", "enum": ["Always", "Never", "IfNotPresent"]} + # image.pullPolicy -- image pull policy + pullPolicy: Always + # @schema {"name": "rbac", "type": "object"} rbac: # @schema {"name": "rbac.create", "type": "boolean"} diff --git a/charts/vald-benchmark-operator/values/benchmark-job.yaml b/charts/vald-benchmark-operator/values/benchmark-job.yaml index c8c13e1f3a..424845cba6 100644 --- a/charts/vald-benchmark-operator/values/benchmark-job.yaml +++ b/charts/vald-benchmark-operator/values/benchmark-job.yaml @@ -40,6 +40,7 @@ spec: num: 10 min_num: 10 timeout: "1m" + enable_linear_search: true target: host: "vald-lb-gateway.default.svc.cluster.local" port: 8081 diff --git a/charts/vald-benchmark-operator/values/benchmark-scenario.yaml b/charts/vald-benchmark-operator/values/benchmark-scenario.yaml index 4a0068eb20..0460e1a151 100644 --- a/charts/vald-benchmark-operator/values/benchmark-scenario.yaml +++ b/charts/vald-benchmark-operator/values/benchmark-scenario.yaml @@ -89,6 +89,7 @@ spec: num: 10 min_num: 10 timeout: "1m" + enable_linear_search: true client_config: health_check_duration: "10s" rps: 2000 @@ -111,7 +112,7 @@ spec: rps: 1000 - job_type: "search" dimension: 784 - repetition: 1 + repetition: 2 replica: 1 rules: [] dataset: @@ -127,6 +128,7 @@ spec: num: 10 min_num: 10 timeout: "1m" + enable_linear_search: false client_config: health_check_duration: "10s" rps: 4000 diff --git a/dockers/tools/benchmark/job/Dockerfile b/dockers/tools/benchmark/job/Dockerfile index 4c378a90a6..a6251e3075 100644 --- a/dockers/tools/benchmark/job/Dockerfile +++ b/dockers/tools/benchmark/job/Dockerfile @@ -19,27 +19,39 @@ ARG DISTROLESS_IMAGE_TAG=nonroot ARG UPX_OPTIONS=-9 ARG MAINTAINER="vdaas.org vald team " -FROM golang:${GO_VERSION} AS builder +FROM ubuntu:devel AS builder ARG UPX_OPTIONS ENV GO111MODULE on +ENV DEBIAN_FRONTEND noninteractive +ENV INITRD No ENV LANG en_US.UTF-8 +ENV GOROOT /opt/go +ENV GOPATH /go +ENV PATH ${PATH}:${GOROOT}/bin:${GOPATH}/bin ENV ORG vdaas ENV REPO vald ENV APP_NAME job ENV PKG tools/benchmark/${APP_NAME} +# skipcq: DOK-DL3008 RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + build-essential \ + libhdf5-dev \ + curl \ upx \ git \ - libhdf5-dev \ && ldconfig \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* RUN mkdir -p ${GOPATH}/src +COPY --from=golang /usr/local/go $GOROOT +RUN mkdir -p "$GOPATH/src" + WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} COPY go.mod . @@ -90,6 +102,36 @@ COPY --from=builder /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/ COPY --from=builder /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/ COPY --from=builder /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/ COPY --from=builder /lib/x86_64-linux-gnu/libm.so.6 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libcrypto.so.3 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libcurl.so.4 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libnghttp2.so.14 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libidn2.so.0 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/librtmp.so.1 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libssh.so.4 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libpsl.so.5 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libssl.so.3 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libgssapi_krb5.so.2 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libldap.so.2 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/liblber.so.2 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libzstd.so.1 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libbrotlidec.so.1 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libunistring.so.2 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libgnutls.so.30 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libhogweed.so.6 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libnettle.so.8 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libgmp.so.10 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libkrb5.so.3 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libk5crypto.so.3 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libkrb5support.so.0 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libcom_err.so.2 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libsasl2.so.2 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libbrotlicommon.so.1 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libp11-kit.so.0 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libtasn1.so.6 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libkeyutils.so.1 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libresolv.so.2 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libffi.so.8 /lib/x86_64-linux-gnu/ +# COPY --from=builder /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/ COPY --from=builder /lib64/ld-linux-x86-64.so.2 /lib64/ COPY --from=builder /usr/bin/${APP_NAME} /go/bin/${APP_NAME} diff --git a/dockers/tools/benchmark/operator/Dockerfile b/dockers/tools/benchmark/operator/Dockerfile index 48f0e1dc54..b930f92d04 100644 --- a/dockers/tools/benchmark/operator/Dockerfile +++ b/dockers/tools/benchmark/operator/Dockerfile @@ -20,18 +20,26 @@ ARG DISTROLESS_IMAGE_TAG=nonroot ARG UPX_OPTIONS=-9 ARG MAINTAINER="vdaas.org vald team " -FROM golang:${GO_VERSION} AS builder - -ARG UPX_OPTIONS +FROM ubuntu:devel AS builder ENV GO111MODULE on +ENV DEBIAN_FRONTEND noninteractive +ENV INITRD No ENV LANG en_US.UTF-8 +ENV GOROOT /opt/go +ENV GOPATH /go +ENV PATH ${PATH}:${GOROOT}/bin:${GOPATH}/bin +# ARG UPX_OPTIONS ENV ORG vdaas ENV REPO vald ENV APP_NAME operator ENV PKG tools/benchmark/${APP_NAME} +# skipcq: DOK-DL3008 RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + build-essential \ + curl \ upx \ git \ && apt-get clean \ @@ -39,6 +47,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN mkdir -p ${GOPATH}/src +COPY --from=golang /usr/local/go $GOROOT +RUN mkdir -p "$GOPATH/src" + WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} COPY go.mod . diff --git a/go.mod b/go.mod index d4c5284809..93a4e53c0d 100755 --- a/go.mod +++ b/go.mod @@ -396,6 +396,7 @@ require ( golang.org/x/text v0.13.0 golang.org/x/tools v0.14.0 golang.org/x/time v0.3.0 + golang.org/x/time v0.3.0 gonum.org/v1/hdf5 v0.0.0-00010101000000-000000000000 gonum.org/v1/plot v0.10.1 google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a diff --git a/internal/config/benchmark.go b/internal/config/benchmark.go index 0361ddb294..84ec55b58a 100644 --- a/internal/config/benchmark.go +++ b/internal/config/benchmark.go @@ -36,6 +36,7 @@ type BenchmarkJob struct { BeforeJobName string `json:"before_job_name,omitempty" yaml:"before_job_name"` BeforeJobNamespace string `json:"before_job_namespace,omitempty" yaml:"before_job_namespace"` RPS int `json:"rps,omitempty" yaml:"rps"` + ConcurrencyLimit int `json:"concurrency_limit,omitempty" yaml:"concurrency_limit"` } // BenchmarkScenario represents the configuration for the internal benchmark scenario. @@ -62,10 +63,13 @@ type BenchmarkDataset struct { Group string `json:"group,omitempty"` Indexes int `json:"indexes,omitempty"` Range *BenchmarkDatasetRange `json:"range,omitempty"` + URL string `json:"url,omitempty"` } func (d *BenchmarkDataset) Bind() *BenchmarkDataset { d.Name = GetActualValue(d.Name) + d.Group = GetActualValue(d.Group) + d.URL = GetActualValue(d.URL) return d } @@ -124,15 +128,18 @@ func (cfg *UpsertConfig) Bind() *UpsertConfig { // SearchConfig defines the desired state of search config type SearchConfig struct { - Epsilon float32 `json:"epsilon,omitempty"` - Radius float32 `json:"radius,omitempty"` - Num int32 `json:"num,omitempty"` - MinNum int32 `json:"min_num,omitempty"` - Timeout string `json:"timeout,omitempty"` + Epsilon float32 `json:"epsilon,omitempty"` + Radius float32 `json:"radius,omitempty"` + Num int32 `json:"num,omitempty"` + MinNum int32 `json:"min_num,omitempty"` + Timeout string `json:"timeout,omitempty"` + EnableLinearSearch bool `json:"enable_linear_search,omitempty"` + AggregationAlgorithm string `json:"aggregation_algorithm,omitempty"` } func (cfg *SearchConfig) Bind() *SearchConfig { cfg.Timeout = GetActualValue(cfg.Timeout) + cfg.AggregationAlgorithm = GetActualValue(cfg.AggregationAlgorithm) return cfg } @@ -225,3 +232,16 @@ func (b *BenchmarkJob) Bind() *BenchmarkJob { func (b *BenchmarkScenario) Bind() *BenchmarkScenario { return b } + +// BenchmarkJobImageInfo represents the docker image information for benchmark job. +type BenchmarkJobImageInfo struct { + Image string `json:"image,omitempty" yaml:"image"` + PullPolicy string `json:"pull_policy,omitempty" yaml:"pull_policy"` +} + +// Bind binds the actual data from the BenchmarkJobImageInfo receiver fields. +func (b *BenchmarkJobImageInfo) Bind() *BenchmarkJobImageInfo { + b.Image = GetActualValue(b.Image) + b.PullPolicy = GetActualValue(b.PullPolicy) + return b +} diff --git a/internal/k8s/client/client.go b/internal/k8s/client/client.go index 2b3647ebd1..057932a200 100644 --- a/internal/k8s/client/client.go +++ b/internal/k8s/client/client.go @@ -20,6 +20,7 @@ package client import ( "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" cli "sigs.k8s.io/controller-runtime/pkg/client" @@ -30,11 +31,16 @@ type ( Object = cli.Object ObjectKey = cli.ObjectKey DeleteAllOfOptions = cli.DeleteAllOfOptions + DeleteOptions = cli.DeleteOptions ListOptions = cli.ListOptions MatchingLabels = cli.MatchingLabels InNamespace = cli.InNamespace ) +const ( + DeletePropagationBackground = metav1.DeletePropagationBackground +) + type Client interface { // Get retrieves an obj for the given object key from the Kubernetes Cluster. // obj must be a struct pointer so that obj can be updated with the response diff --git a/internal/k8s/job/job.go b/internal/k8s/job/job.go index aa07449d08..97ec3de0eb 100644 --- a/internal/k8s/job/job.go +++ b/internal/k8s/job/job.go @@ -30,7 +30,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) // JobWatcher is a type alias for k8s resource controller. @@ -153,7 +152,7 @@ func (r *reconciler) Owns() (client.Object, []builder.OwnsOption) { // Watches returns the kind of the job and the event handler. // It will always return nil. -func (r *reconciler) Watches() (*source.Kind, handler.EventHandler, []builder.WatchesOption) { +func (r *reconciler) Watches() (client.Object, handler.EventHandler, []builder.WatchesOption) { // return &source.Kind{Type: new(corev1.Pod)}, &handler.EnqueueRequestForObject{} return nil, nil, nil } diff --git a/internal/k8s/vald/benchmark/api/v1/job_types.go b/internal/k8s/vald/benchmark/api/v1/job_types.go index 135d1e42e9..729bf27629 100644 --- a/internal/k8s/vald/benchmark/api/v1/job_types.go +++ b/internal/k8s/vald/benchmark/api/v1/job_types.go @@ -23,21 +23,23 @@ import ( ) type BenchmarkJobSpec struct { - Target *BenchmarkTarget `json:"target,omitempty" yaml:"target"` - Dataset *BenchmarkDataset `json:"dataset,omitempty" yaml:"dataset"` - Dimension int `json:"dimension,omitempty" yaml:"dimension"` - Replica int `json:"replica,omitempty" yaml:"replica"` - Repetition int `json:"repetition,omitempty" yaml:"repetition"` - JobType string `json:"job_type,omitempty" yaml:"job_type"` - InsertConfig *config.InsertConfig `json:"insert_config,omitempty" yaml:"insert_config"` - UpdateConfig *config.UpdateConfig `json:"update_config,omitempty" yaml:"update_config"` - UpsertConfig *config.UpsertConfig `json:"upsert_config,omitempty" yaml:"upsert_config"` - SearchConfig *config.SearchConfig `json:"search_config,omitempty" yaml:"search_config"` - RemoveConfig *config.RemoveConfig `json:"remove_config,omitempty" yaml:"remove_config"` - ObjectConfig *config.ObjectConfig `json:"object_config,omitempty" yaml:"object_config"` - ClientConfig *config.GRPCClient `json:"client_config,omitempty" yaml:"client_config"` - Rules []*config.BenchmarkJobRule `json:"rules,omitempty" yaml:"rules"` - RPS int `json:"rps,omitempty" yaml:"rps"` + Target *BenchmarkTarget `json:"target,omitempty"` + Dataset *BenchmarkDataset `json:"dataset,omitempty"` + Dimension int `json:"dimension,omitempty"` + Replica int `json:"replica,omitempty"` + Repetition int `json:"repetition,omitempty"` + JobType string `json:"job_type,omitempty"` + InsertConfig *config.InsertConfig `json:"insert_config,omitempty"` + UpdateConfig *config.UpdateConfig `json:"update_config,omitempty"` + UpsertConfig *config.UpsertConfig `json:"upsert_config,omitempty"` + SearchConfig *config.SearchConfig `json:"search_config,omitempty"` + RemoveConfig *config.RemoveConfig `json:"remove_config,omitempty"` + ObjectConfig *config.ObjectConfig `json:"object_config,omitempty"` + ClientConfig *config.GRPCClient `json:"client_config,omitempty"` + Rules []*config.BenchmarkJobRule `json:"rules,omitempty"` + RPS int `json:"rps,omitempty"` + ConcurrencyLimit int `json:"concurrency_limit,omitempty"` + TTLSecondsAfterFinished int `json:"ttl_seconds_after_finished,omitempty"` } type BenchmarkJobStatus string diff --git a/internal/k8s/vald/benchmark/job/job.go b/internal/k8s/vald/benchmark/job/job.go index 87a7efe22a..6f81ee01fa 100644 --- a/internal/k8s/vald/benchmark/job/job.go +++ b/internal/k8s/vald/benchmark/job/job.go @@ -31,7 +31,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/scheme" - "sigs.k8s.io/controller-runtime/pkg/source" ) type BenchmarkJobWatcher k8s.ResourceController @@ -134,7 +133,7 @@ func (r *reconciler) Owns() (client.Object, []builder.OwnsOption) { return nil, nil } -func (r *reconciler) Watches() (*source.Kind, handler.EventHandler, []builder.WatchesOption) { +func (r *reconciler) Watches() (client.Object, handler.EventHandler, []builder.WatchesOption) { // return &source.Kind{Type: new(corev1.Pod)}, &handler.EnqueueRequestForObject{} return nil, nil, nil } diff --git a/internal/k8s/vald/benchmark/job/job_template.go b/internal/k8s/vald/benchmark/job/job_template.go index 0368e6a5b5..6b1926f0dc 100644 --- a/internal/k8s/vald/benchmark/job/job_template.go +++ b/internal/k8s/vald/benchmark/job/job_template.go @@ -18,37 +18,71 @@ package job import ( - batchv1 "k8s.io/api/batch/v1" + jobs "github.com/vdaas/vald/internal/k8s/job" corev1 "k8s.io/api/core/v1" ) -type benchmarkJobTemplate = batchv1.Job +type ( + ImagePullPolicy corev1.PullPolicy + RestartPolicy corev1.RestartPolicy +) const ( - SvcAccountName = "vald-benchmark-operator" - ContainerName = "vald-benchmark-job" - // TODO: Fix - ContainerImage = "vdaas/vald-benchmark-job:pr-2027" + PullAlways ImagePullPolicy = "Always" + PullNever ImagePullPolicy = "Never" + PullIfNotPresent ImagePullPolicy = "PullIfNotPresent" + + RestartPolicyAlways RestartPolicy = "Always" + RestartPolicyOnFailure RestartPolicy = "OnFailure" + RestartPolicyNever RestartPolicy = "Never" +) - RestartPolicyAlways corev1.RestartPolicy = "Always" - RestartPolicyOnFailure corev1.RestartPolicy = "OnFailure" - RestartPolicyNever corev1.RestartPolicy = "Never" +const ( + svcAccount = "vald-benchmark-operator" ) -// NewBenchmarkJobTemplate creates the job template for crating k8s job resource. -func NewBenchmarkJobTemplate(opts ...BenchmarkJobOption) (benchmarkJobTemplate, error) { - jobTmpl := new(benchmarkJobTemplate) +type BenchmarkJobTpl interface { + CreateJobTpl(opts ...BenchmarkJobOption) (jobs.Job, error) +} + +type benchmarkJobTpl struct { + containerName string + containerImageName string + imagePullPolicy ImagePullPolicy + jobTpl jobs.Job +} + +func NewBenchmarkJob(opts ...BenchmarkJobTplOption) (BenchmarkJobTpl, error) { + bjTpl := new(benchmarkJobTpl) + for _, opt := range append(defaultBenchmarkJobTplOpts, opts...) { + err := opt(bjTpl) + if err != nil { + return nil, err + } + } + return bjTpl, nil +} + +func (b *benchmarkJobTpl) CreateJobTpl(opts ...BenchmarkJobOption) (jobs.Job, error) { for _, opt := range append(defaultBenchmarkJobOpts, opts...) { - err := opt(jobTmpl) + err := opt(&b.jobTpl) if err != nil { - return *jobTmpl, err + return b.jobTpl, err } } - jobTmpl.Spec.Template.Spec.Containers = []corev1.Container{ + // TODO: check enable pprof flag + b.jobTpl.Spec.Template.Annotations = map[string]string{ + "pyroscope.io/scrape": "true", + "pyroscope.io/application-name": "benchmark-job", + "pyroscope.io/profile-cpu-enabled": "true", + "pyroscope.io/profile-mem-enabled": "true", + "pyroscope.io/port": "6060", + } + b.jobTpl.Spec.Template.Spec.Containers = []corev1.Container{ { - Name: ContainerName, - Image: ContainerImage, - ImagePullPolicy: corev1.PullAlways, + Name: b.containerName, + Image: b.containerImageName, + ImagePullPolicy: corev1.PullPolicy(b.imagePullPolicy), LivenessProbe: &corev1.Probe{ InitialDelaySeconds: int32(60), PeriodSeconds: int32(10), @@ -107,5 +141,5 @@ func NewBenchmarkJobTemplate(opts ...BenchmarkJobOption) (benchmarkJobTemplate, }, }, } - return *jobTmpl, nil + return b.jobTpl, nil } diff --git a/internal/k8s/vald/benchmark/job/job_template_option.go b/internal/k8s/vald/benchmark/job/job_template_option.go index f5cc78f875..4ec09250d3 100644 --- a/internal/k8s/vald/benchmark/job/job_template_option.go +++ b/internal/k8s/vald/benchmark/job/job_template_option.go @@ -18,20 +18,60 @@ package job import ( "github.com/vdaas/vald/internal/k8s" + jobs "github.com/vdaas/vald/internal/k8s/job" corev1 "k8s.io/api/core/v1" ) +type BenchmarkJobTplOption func(b *benchmarkJobTpl) error + +var defaultBenchmarkJobTplOpts = []BenchmarkJobTplOption{ + WithContainerName("vald-benchmark-job"), + WithContainerImage("vdaas/vald-benchmark-job"), + WithImagePullPolicy(PullAlways), +} + +// WithContainerName sets the docker container name of benchmark job. +func WithContainerName(name string) BenchmarkJobTplOption { + return func(b *benchmarkJobTpl) error { + if len(name) > 0 { + b.containerName = name + } + return nil + } +} + +// WithContainerImage sets the docker image path for benchmark job. +func WithContainerImage(name string) BenchmarkJobTplOption { + return func(b *benchmarkJobTpl) error { + if len(name) > 0 { + b.containerImageName = name + } + return nil + } +} + +// WithImagePullPolicy sets the docker image pull policy for benchmark job. +func WithImagePullPolicy(p ImagePullPolicy) BenchmarkJobTplOption { + return func(b *benchmarkJobTpl) error { + if len(p) == 0 { + return nil + } + b.imagePullPolicy = p + return nil + } +} + // BenchmarkJobOption represents the option for create benchmark job template. -type BenchmarkJobOption func(b *benchmarkJobTemplate) error +type BenchmarkJobOption func(b *jobs.Job) error var defaultBenchmarkJobOpts = []BenchmarkJobOption{ - WithSvcAccountName(SvcAccountName), + WithSvcAccountName(svcAccount), WithRestartPolicy(RestartPolicyNever), } // WithSvcAccountName sets the service account name for benchmark job. func WithSvcAccountName(name string) BenchmarkJobOption { - return func(b *benchmarkJobTemplate) error { + return func(b *jobs.Job) error { if len(name) > 0 { b.Spec.Template.Spec.ServiceAccountName = name } @@ -40,10 +80,10 @@ func WithSvcAccountName(name string) BenchmarkJobOption { } // WithRestartPolicy sets the job restart policy for benchmark job. -func WithRestartPolicy(rp corev1.RestartPolicy) BenchmarkJobOption { - return func(b *benchmarkJobTemplate) error { +func WithRestartPolicy(rp RestartPolicy) BenchmarkJobOption { + return func(b *jobs.Job) error { if len(rp) > 0 { - b.Spec.Template.Spec.RestartPolicy = rp + b.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicy(rp) } return nil } @@ -51,7 +91,7 @@ func WithRestartPolicy(rp corev1.RestartPolicy) BenchmarkJobOption { // WithBackoffLimit sets the job backoff limit for benchmark job. func WithBackoffLimit(bo int32) BenchmarkJobOption { - return func(b *benchmarkJobTemplate) error { + return func(b *jobs.Job) error { b.Spec.BackoffLimit = &bo return nil } @@ -59,7 +99,7 @@ func WithBackoffLimit(bo int32) BenchmarkJobOption { // WithName sets the job name of benchmark job. func WithName(name string) BenchmarkJobOption { - return func(b *benchmarkJobTemplate) error { + return func(b *jobs.Job) error { b.Name = name return nil } @@ -67,7 +107,7 @@ func WithName(name string) BenchmarkJobOption { // WithNamespace specify namespace where job will execute. func WithNamespace(ns string) BenchmarkJobOption { - return func(b *benchmarkJobTemplate) error { + return func(b *jobs.Job) error { b.Namespace = ns return nil } @@ -75,7 +115,7 @@ func WithNamespace(ns string) BenchmarkJobOption { // WithOwnerRef sets the OwnerReference to the job resource. func WithOwnerRef(refs []k8s.OwnerReference) BenchmarkJobOption { - return func(b *benchmarkJobTemplate) error { + return func(b *jobs.Job) error { if len(refs) > 0 { b.OwnerReferences = refs } @@ -85,7 +125,7 @@ func WithOwnerRef(refs []k8s.OwnerReference) BenchmarkJobOption { // WithCompletions sets the job completion. func WithCompletions(com int32) BenchmarkJobOption { - return func(b *benchmarkJobTemplate) error { + return func(b *jobs.Job) error { if com > 1 { b.Spec.Completions = &com } @@ -95,7 +135,7 @@ func WithCompletions(com int32) BenchmarkJobOption { // WithParallelism sets the job parallelism. func WithParallelism(parallelism int32) BenchmarkJobOption { - return func(b *benchmarkJobTemplate) error { + return func(b *jobs.Job) error { if parallelism > 1 { b.Spec.Parallelism = ¶llelism } @@ -105,10 +145,20 @@ func WithParallelism(parallelism int32) BenchmarkJobOption { // WithLabel sets the label to the job resource. func WithLabel(label map[string]string) BenchmarkJobOption { - return func(b *benchmarkJobTemplate) error { + return func(b *jobs.Job) error { if len(label) > 0 { b.Labels = label } return nil } } + +// WithTTLSecondsAfterFinished sets the TTLSecondsAfterFinished to the job template. +func WithTTLSecondsAfterFinished(ttl int32) BenchmarkJobOption { + return func(b *jobs.Job) error { + if ttl > 0 { + b.Spec.TTLSecondsAfterFinished = &ttl + } + return nil + } +} diff --git a/internal/k8s/vald/benchmark/scenario/scenario.go b/internal/k8s/vald/benchmark/scenario/scenario.go index cde92a808d..d03d1e37da 100644 --- a/internal/k8s/vald/benchmark/scenario/scenario.go +++ b/internal/k8s/vald/benchmark/scenario/scenario.go @@ -29,7 +29,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) type BenchmarkScenarioWatcher k8s.ResourceController @@ -122,7 +121,8 @@ func (r *reconciler) Owns() (client.Object, []builder.OwnsOption) { return nil, nil } -func (r *reconciler) Watches() (*source.Kind, handler.EventHandler, []builder.WatchesOption) { +func (r *reconciler) Watches() (client.Object, handler.EventHandler, []builder.WatchesOption) { // return &source.Kind{Type: new(corev1.Pod)}, &handler.EnqueueRequestForObject{} - return &source.Kind{Type: new(v1.ValdBenchmarkScenario)}, &handler.EnqueueRequestForObject{}, nil + // return &source.Kind{Type: new(v1.ValdBenchmarkScenario)}, &handler.EnqueueRequestForObject{}, nil + return nil, &handler.EnqueueRequestForObject{}, nil } diff --git a/internal/test/data/hdf5/hdf5.go b/internal/test/data/hdf5/hdf5.go index f178a52c2e..b308687cf0 100644 --- a/internal/test/data/hdf5/hdf5.go +++ b/internal/test/data/hdf5/hdf5.go @@ -28,8 +28,8 @@ import ( ) type Data interface { - Download() error - Read() error + Download(url string) error + Read(key Hdf5Key) error GetName() DatasetName GetPath() string GetByGroupName(name string) [][]float32 @@ -41,13 +41,16 @@ type Data interface { type DatasetName int const ( - FashionMNIST784Euclidean DatasetName = iota + Original DatasetName = iota + FashionMNIST784Euclidean ) func (d DatasetName) String() string { switch d { + case Original: + return "original" case FashionMNIST784Euclidean: - return "fashion-mnist-784-euc" + return "fashion-mnist" default: return "" } @@ -68,16 +71,16 @@ func (d DatasetUrl) String() string { } } -type hdf5Key int +type Hdf5Key int const ( - Train hdf5Key = iota + Train Hdf5Key = iota + 1 Test Neighors ) -func (h hdf5Key) String() string { - switch h { +func (key Hdf5Key) String() string { + switch key { case Train: return "train" case Test: @@ -109,8 +112,10 @@ func New(opts ...Option) (Data, error) { // Get downloads the hdf5 file. // https://github.com/erikbern/ann-benchmarks/#data-sets -func (d *data) Download() error { +func (d *data) Download(url string) error { switch d.name { + case Original: + return downloadFile(url, d.path) case FashionMNIST784Euclidean: return downloadFile(FashionMNIST784EuclideanUrl.String(), d.path) default: @@ -118,26 +123,29 @@ func (d *data) Download() error { } } -func (d *data) Read() error { +func (d *data) Read(key Hdf5Key) error { f, err := hdf5.OpenFile(d.path, hdf5.F_ACC_RDONLY) if err != nil { return err } defer f.Close() - // load training data - train, err := ReadDatasetF32(f, Train) - if err != nil { - return err + if key != Test { + // load training data + train, err := ReadDatasetF32(f, Train) + if err != nil { + return err + } + d.train = train } - d.train = train - - // load test data - test, err := ReadDatasetF32(f, Test) - if err != nil { - return err + if key != Train { + // load test data + test, err := ReadDatasetF32(f, Test) + if err != nil { + return err + } + d.test = test } - d.test = test // load neighbors neighbors32, err := ReadDatasetI32(f, Neighors) @@ -230,7 +238,7 @@ func downloadFile(url, path string) error { return nil } -func ReadDatasetF32(file *hdf5.File, key hdf5Key) ([][]float32, error) { +func ReadDatasetF32(file *hdf5.File, key Hdf5Key) ([][]float32, error) { data, err := file.OpenDataset(key.String()) if err != nil { return nil, err @@ -259,7 +267,7 @@ func ReadDatasetF32(file *hdf5.File, key hdf5Key) ([][]float32, error) { return vecs, nil } -func ReadDatasetI32(file *hdf5.File, key hdf5Key) ([][]int32, error) { +func ReadDatasetI32(file *hdf5.File, key Hdf5Key) ([][]int32, error) { data, err := file.OpenDataset(key.String()) if err != nil { return nil, err diff --git a/internal/test/data/hdf5/hdf5_test.go b/internal/test/data/hdf5/hdf5_test.go index d308048781..940a76021b 100644 --- a/internal/test/data/hdf5/hdf5_test.go +++ b/internal/test/data/hdf5/hdf5_test.go @@ -47,37 +47,19 @@ func TestDatasetName_String(t *testing.T) { return nil } tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - want: want{}, - checkFunc: defaultCheckFunc, - beforeFunc: func(t *testing.T,) { - t.Helper() - }, - afterFunc: func(t *testing.T,) { - t.Helper() - }, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - want: want{}, - checkFunc: defaultCheckFunc, - beforeFunc: func(t *testing.T,) { - t.Helper() - }, - afterFunc: func(t *testing.T,) { - t.Helper() - }, - } - }(), - */ + // { + // name: "set original", + // want: want{ + // want: "original", + // }, + // checkFunc: defaultCheckFunc, + // beforeFunc: func(t *testing.T,) { + // t.Helper() + // }, + // afterFunc: func(t *testing.T,) { + // t.Helper() + // }, + // }, } for _, tc := range tests { @@ -180,13 +162,13 @@ func TestDatasetUrl_String(t *testing.T) { } } -func Test_hdf5Key_String(t *testing.T) { +func Test_Hdf5Key_String(t *testing.T) { type want struct { want string } type test struct { name string - h hdf5Key + h Hdf5Key want want checkFunc func(want, string) error beforeFunc func(*testing.T) @@ -353,12 +335,16 @@ func Test_data_Download(t *testing.T) { test [][]float32 neighbors [][]int } + type args struct { + url string + } type want struct { err error } type test struct { name string fields fields + args args want want checkFunc func(want, error) error beforeFunc func(*testing.T) @@ -440,7 +426,7 @@ func Test_data_Download(t *testing.T) { neighbors: test.fields.neighbors, } - err := d.Download() + err := d.Download(test.args.url) if err := checkFunc(test.want, err); err != nil { tt.Errorf("error = %v", err) } @@ -456,6 +442,9 @@ func Test_data_Read(t *testing.T) { test [][]float32 neighbors [][]int } + type args struct { + key Hdf5Key + } type want struct { err error } @@ -463,6 +452,7 @@ func Test_data_Read(t *testing.T) { name string fields fields want want + args args checkFunc func(want, error) error beforeFunc func(*testing.T) afterFunc func(*testing.T) @@ -544,7 +534,7 @@ func Test_data_Read(t *testing.T) { neighbors: test.fields.neighbors, } - err := d.Read() + err := d.Read(test.args.key) if err := checkFunc(test.want, err); err != nil { tt.Errorf("error = %v", err) } @@ -1265,7 +1255,7 @@ func Test_downloadFile(t *testing.T) { func TestReadDatasetF32(t *testing.T) { type args struct { file *hdf5.File - key hdf5Key + key Hdf5Key } type want struct { want [][]float32 @@ -1357,7 +1347,7 @@ func TestReadDatasetF32(t *testing.T) { func TestReadDatasetI32(t *testing.T) { type args struct { file *hdf5.File - key hdf5Key + key Hdf5Key } type want struct { want [][]int32 diff --git a/internal/test/data/hdf5/option.go b/internal/test/data/hdf5/option.go index 6fee9d0ae3..4d02b281f2 100644 --- a/internal/test/data/hdf5/option.go +++ b/internal/test/data/hdf5/option.go @@ -31,6 +31,8 @@ var defaultOptions = []Option{ func WithNameByString(n string) Option { var name DatasetName switch n { + case Original.String(): + name = Original case FashionMNIST784Euclidean.String(): name = FashionMNIST784Euclidean } @@ -40,6 +42,8 @@ func WithNameByString(n string) Option { func WithName(dn DatasetName) Option { return func(d *data) error { switch dn { + case Original: + d.name = dn case FashionMNIST784Euclidean: d.name = dn default: diff --git a/internal/test/data/hdf5/option_test.go b/internal/test/data/hdf5/option_test.go new file mode 100644 index 0000000000..e239c3361d --- /dev/null +++ b/internal/test/data/hdf5/option_test.go @@ -0,0 +1,387 @@ +// +// Copyright (C) 2019-2023 vdaas.org vald team +// +// 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 +// +// https://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 hdf5 is load hdf5 file +package hdf5 + +import ( + "reflect" + "testing" + + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/test/goleak" +) + +func TestWithNameByString(t *testing.T) { + // Change interface type to the type of object you are testing + type args struct { + n string + } + type want struct { + obj data + } + type test struct { + name string + args args + want want + checkFunc func(want want, got data) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", obj, w.obj) + } + return nil + } + */ + + defaultCheckFunc := func(w want, obj data) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", obj, w.obj) + } + return nil + } + + tests := []test{ + { + name: "set original", + args: args{ + n: "original", + }, + want: want{ + obj: data{ + name: Original, + }, + }, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + { + name: "set fashion-mnist", + args: args{ + n: "fashion-mnist", + }, + want: want{ + obj: data{ + name: FashionMNIST784Euclidean, + }, + }, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + got := WithNameByString(test.args.n) + obj := new(data) + _ = got(obj) + if err := checkFunc(test.want, *obj); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func TestWithName(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + dn DatasetName + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + dn:nil, + }, + want: want { + obj: new(T), + }, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + dn:nil, + }, + want: want { + obj: new(T), + }, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + got := WithName(test.args.dn) + obj := new(T) + if err := checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + got := WithName(test.args.dn) + obj := new(T) + got(obj) + if err := checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithFilePath(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + f string + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + f:"", + }, + want: want { + obj: new(T), + }, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + f:"", + }, + want: want { + obj: new(T), + }, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, + } + }(), + */ + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + got := WithFilePath(test.args.f) + obj := new(T) + if err := checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + got := WithFilePath(test.args.f) + obj := new(T) + got(obj) + if err := checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} diff --git a/k8s/tools/benchmark/operator/clusterrole.yaml b/k8s/tools/benchmark/operator/clusterrole.yaml index 70ac264c95..4d3fc54948 100644 --- a/k8s/tools/benchmark/operator/clusterrole.yaml +++ b/k8s/tools/benchmark/operator/clusterrole.yaml @@ -55,6 +55,7 @@ rules: verbs: - create - delete + - deletecollection - get - list - patch diff --git a/k8s/tools/benchmark/operator/configmap.yaml b/k8s/tools/benchmark/operator/configmap.yaml index 05f7b34594..7f6f7a492a 100644 --- a/k8s/tools/benchmark/operator/configmap.yaml +++ b/k8s/tools/benchmark/operator/configmap.yaml @@ -138,3 +138,6 @@ data: trace: enabled: false sampling_rate: 1 + job_image: + image: "vdaas/vald-benchmark-job:v1.7.5" + pullPolicy: Always diff --git a/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml b/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml index de85714816..d85ea0c12b 100644 --- a/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml +++ b/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml @@ -240,6 +240,10 @@ spec: type: string wait_for_ready: type: boolean + concurrency_limit: + type: integer + maximum: 65535 + minimum: 0 dataset: type: object properties: @@ -252,6 +256,7 @@ spec: name: type: string enum: + - original - fashion-mnist range: type: object @@ -262,6 +267,11 @@ spec: start: type: integer minimum: 1 + required: + - start + - end + url: + type: string required: - name - indexes @@ -285,7 +295,7 @@ spec: - upsert - search - remove - - get_object + - getobject - exists object_config: type: object @@ -319,6 +329,16 @@ spec: search_config: type: object properties: + aggregation_algorithm: + type: string + enum: + - Unknown + - ConcurrentQueue + - SortSlice + - SortPoolSlice + - PairingHeap + enable_linear_search: + type: boolean epsilon: type: number min_num: @@ -342,6 +362,10 @@ spec: required: - host - port + ttl_seconds_after_finished: + type: integer + maximum: 65535 + minimum: 0 update_config: type: object properties: diff --git a/k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml b/k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml index 3451a48b38..28ab299701 100644 --- a/k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml +++ b/k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml @@ -81,6 +81,19 @@ spec: type: string tag: type: string + job_image: + type: object + properties: + pullPolicy: + type: string + enum: + - Always + - Never + - IfNotPresent + repository: + type: string + tag: + type: string logging: type: object properties: diff --git a/k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml b/k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml index f705e7df6b..871f59b390 100644 --- a/k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml +++ b/k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml @@ -25,8 +25,8 @@ spec: plural: valdbenchmarkscenarios singular: valdbenchmarkscenario shortNames: - - vbo - - vbos + - vbs + - vbss scope: Namespaced versions: - name: v1 @@ -74,6 +74,7 @@ spec: name: type: string enum: + - original - fashion-mnist range: type: object @@ -84,6 +85,11 @@ spec: start: type: integer minimum: 1 + required: + - start + - end + url: + type: string required: - name - indexes diff --git a/k8s/tools/benchmark/operator/deployment.yaml b/k8s/tools/benchmark/operator/deployment.yaml index 7930e06334..520609518b 100644 --- a/k8s/tools/benchmark/operator/deployment.yaml +++ b/k8s/tools/benchmark/operator/deployment.yaml @@ -98,6 +98,11 @@ spec: volumeMounts: - name: vald-benchmark-operator-config mountPath: /etc/server + env: + - name: JOB_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace terminationMessagePath: /dev/termination-log terminationMessagePolicy: File restartPolicy: Always diff --git a/pkg/tools/benchmark/job/config/config.go b/pkg/tools/benchmark/job/config/config.go index ad1cdb839a..3b1492e25d 100644 --- a/pkg/tools/benchmark/job/config/config.go +++ b/pkg/tools/benchmark/job/config/config.go @@ -104,6 +104,7 @@ func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { } else { cfg.Job.Target = (*config.BenchmarkTarget)(jobResource.Spec.Target) cfg.Job.Dataset = (*config.BenchmarkDataset)(jobResource.Spec.Dataset) + cfg.Job.Dimension = jobResource.Spec.Dimension cfg.Job.Replica = jobResource.Spec.Replica cfg.Job.Repetition = jobResource.Spec.Repetition cfg.Job.JobType = jobResource.Spec.JobType @@ -116,6 +117,7 @@ func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { cfg.Job.ObjectConfig = jobResource.Spec.ObjectConfig cfg.Job.ClientConfig = jobResource.Spec.ClientConfig cfg.Job.RPS = jobResource.Spec.RPS + cfg.Job.ConcurrencyLimit = jobResource.Spec.ConcurrencyLimit if annotations := jobResource.GetAnnotations(); annotations != nil { cfg.Job.BeforeJobName = annotations[JOBNAME_ANNOTATION] cfg.Job.BeforeJobNamespace = annotations[JOBNAMESPACE_ANNOTATION] diff --git a/pkg/tools/benchmark/job/service/insert.go b/pkg/tools/benchmark/job/service/insert.go index 946710df4b..69d16ecc27 100644 --- a/pkg/tools/benchmark/job/service/insert.go +++ b/pkg/tools/benchmark/job/service/insert.go @@ -19,58 +19,73 @@ package service import ( "context" + "math" "strconv" "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "github.com/vdaas/vald/internal/net/grpc/status" ) func (j *job) insert(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking insert") // create data - vecs := j.genVec(j.dataset) + vecs := j.hdf5.GetByGroupName(j.dataset.Group) cfg := &payload.Insert_Config{ SkipStrictExistCheck: j.insertConfig.SkipStrictExistCheck, } if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - for i := 0; i < len(vecs); i++ { - log.Infof("[benchmark job] Start insert: iter = %d", i) - err := j.limiter.Wait(ctx) - if err != nil { - if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) - } - ech <- err - } - res, err := j.client.Insert(ctx, &payload.Insert_Request{ - Vector: &payload.Object_Vector{ - Id: strconv.Itoa(i), - Vector: vecs[i], - }, - Config: cfg, - }) - if err != nil { - select { - case <-ctx.Done(): + eg, egctx := errgroup.New(ctx) + eg.Limitation(j.concurrencyLimit) + for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { + iter := i + eg.Go(func() error { + log.Debugf("[benchmark job] Start insert: iter = %d", iter) + err := j.limiter.Wait(egctx) + if err != nil { + log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) + // return errors.Join(err, context.Canceled) + return nil } select { - case <-ctx.Done(): - return errors.Join(err, context.Canceled) - case ech <- errors.Join(err, ctx.Err()): + case <-egctx.Done(): + return egctx.Err() + case ech <- err: } - default: - st, _ := status.FromError(err) - log.Warnf("[benchmark job] insert error is detected: code = %d, msg = %s", st.Code(), err.Error()) } - } - // TODO: send metrics to the Prometeus - log.Infof("[benchmark job] Finish insert: iter= %d \n%v\n", i, res) + loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) + idx := iter - 1 - (len(vecs) * int(loopCnt)) + res, err := j.client.Insert(egctx, &payload.Insert_Request{ + Vector: &payload.Object_Vector{ + Id: strconv.Itoa(iter), + Vector: vecs[idx], + }, + Config: cfg, + }) + if err != nil { + select { + case <-egctx.Done(): + log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) + return errors.Join(err, egctx.Err()) + default: + // if st, ok := status.FromError(err); ok { + // log.Warnf("[benchmark job] insert error is detected: code = %d, msg = %s", st.Code(), err.Error()) + // } + } + } + // TODO: send metrics to the Prometeus + log.Debugf("[benchmark job] Finish insert: iter= %d \n%v\n", iter, res) + return nil + }) + } + err := eg.Wait() + if err != nil { + log.Warnf("[benchmark job] insert error is detected: err = %s", err.Error()) + return err } log.Info("[benchmark job] Finish benchmarking insert") return nil diff --git a/pkg/tools/benchmark/job/service/job.go b/pkg/tools/benchmark/job/service/job.go index 2736ad898c..2f115ea157 100644 --- a/pkg/tools/benchmark/job/service/job.go +++ b/pkg/tools/benchmark/job/service/job.go @@ -19,7 +19,6 @@ package service import ( "context" - "math" "os" "reflect" "strconv" @@ -100,6 +99,7 @@ type job struct { beforeJobDur time.Duration limiter rate.Limiter rps int + concurrencyLimit int timeout time.Duration timestamp int64 } @@ -186,20 +186,65 @@ func New(opts ...Option) (Job, error) { if j.rps > 0 { j.limiter = rate.NewLimiter(j.rps) } + // If (Range.End - Range.Start) is smaller than Indexes, Indexes are prioritized based on Range.Start. + if (j.dataset.Range.End - j.dataset.Range.Start + 1) < j.dataset.Indexes { + j.dataset.Range.End = j.dataset.Range.Start + j.dataset.Indexes + } + return j, nil } func (j *job) PreStart(ctx context.Context) error { - log.Infof("[benchmark job] start download dataset of %s", j.hdf5.GetName().String()) - if err := j.hdf5.Download(); err != nil { - return err + if j.jobType != GETOBJECT && j.jobType != EXISTS && j.jobType != REMOVE { + log.Infof("[benchmark job] start download dataset of %s", j.hdf5.GetName().String()) + if err := j.hdf5.Download(j.dataset.URL); err != nil { + return err + } + log.Infof("[benchmark job] success download dataset of %s", j.hdf5.GetName().String()) + log.Infof("[benchmark job] start load dataset of %s", j.hdf5.GetName().String()) + var key hdf5.Hdf5Key + switch j.dataset.Group { + case "train": + key = hdf5.Train + case "test": + key = hdf5.Test + case "neighbors": + key = hdf5.Neighors + default: + } + if err := j.hdf5.Read(key); err != nil { + return err + } + log.Infof("[benchmark job] success load dataset of %s", j.hdf5.GetName().String()) } - log.Infof("[benchmark job] success download dataset of %s", j.hdf5.GetName().String()) - log.Infof("[benchmark job] start load dataset of %s", j.hdf5.GetName().String()) - if err := j.hdf5.Read(); err != nil { - return err + // Wait for beforeJob completed if exists + if len(j.beforeJobName) != 0 { + var jobResource v1.ValdBenchmarkJob + log.Info("[benchmark job] check before benchjob is completed or not...") + j.eg.Go(safety.RecoverFunc(func() error { + dt := time.NewTicker(j.beforeJobDur) + defer dt.Stop() + for { + select { + case <-ctx.Done(): + return nil + case <-dt.C: + err := j.k8sClient.Get(ctx, j.beforeJobName, j.beforeJobNamespace, &jobResource) + if err != nil { + return err + } + if jobResource.Status == v1.BenchmarkJobCompleted { + log.Infof("[benchmark job ] before job (%s) is completed, job service will start soon.", j.beforeJobName) + return nil + } + log.Infof("[benchmark job] before job (%s/%s) is not completed...", j.beforeJobName, jobResource.Status) + } + } + })) + if err := j.eg.Wait(); err != nil { + return err + } } - log.Infof("[benchmark job] success load dataset of %s", j.hdf5.GetName().String()) // Wait for beforeJob completed if exists if len(j.beforeJobName) != 0 { var jobResource v1.ValdBenchmarkJob @@ -272,7 +317,6 @@ func (j *job) Start(ctx context.Context) (<-chan error, error) { } return }) - return ech, nil } @@ -301,22 +345,3 @@ func calcRecall(linearRes, searchRes *payload.Search_Response) (recall float64) } return recall / float64(len(lres)) } - -func (j *job) genVec(cfg *config.BenchmarkDataset) [][]float32 { - start := cfg.Range.Start - end := cfg.Range.End - // If (Range.End - Range.Start) is smaller than Indexes, Indexes are prioritized based on Range.Start. - if (end - start + 1) < cfg.Indexes { - end = cfg.Range.Start + cfg.Indexes - } - data := j.hdf5.GetByGroupName(cfg.Group) - if n := math.Ceil(float64(end) / float64(len(data))); n > 1 { - var def [][]float32 - for i := 0; i < int(n-1); i++ { - def = append(def, data...) - } - data = append(data, def...) - } - vectors := data[start-1 : end] - return vectors -} diff --git a/pkg/tools/benchmark/job/service/object.go b/pkg/tools/benchmark/job/service/object.go index c36ebd9fdb..eec7ea773f 100644 --- a/pkg/tools/benchmark/job/service/object.go +++ b/pkg/tools/benchmark/job/service/object.go @@ -22,43 +22,55 @@ import ( "strconv" "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "github.com/vdaas/vald/internal/net/grpc/status" ) func (j *job) exists(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking exists") - for i := 0; i < j.dataset.Indexes; i++ { - err := j.limiter.Wait(ctx) - if err != nil { - if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) - } - ech <- err - } - res, err := j.client.Exists(ctx, &payload.Object_ID{ - Id: strconv.Itoa(i), - }) - if err != nil { - select { - case <-ctx.Done(): + eg, egctx := errgroup.New(ctx) + eg.Limitation(j.concurrencyLimit) + for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { + idx := i + eg.Go(func() error { + log.Debugf("[benchmark job] Start exists: iter = %d", i) + err := j.limiter.Wait(egctx) + if err != nil { + log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) + return nil + // return errors.Join(err, context.Canceled) } select { - case <-ctx.Done(): - return errors.Join(err, context.Canceled) - case ech <- errors.Join(err, ctx.Err()): + case <-egctx.Done(): + return egctx.Err() + case ech <- err: } - default: - st, _ := status.FromError(err) - log.Warnf("[benchmark job] exists error is detected: code = %d, msg = %s", st.Code(), err.Error()) } - } - if res != nil { - log.Infof("[benchmark exists job] iter=%d, Id=%s", i, res.GetId()) - } + res, err := j.client.Exists(egctx, &payload.Object_ID{ + Id: strconv.Itoa(idx), + }) + if err != nil { + select { + case <-egctx.Done(): + log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) + return nil + // return errors.Join(err, egctx.Err()) + default: + // if st, ok := status.FromError(err); ok { + // log.Warnf("[benchmark job] exists error is detected: code = %d, msg = %s", st.Code(), err.Error()) + // } + } + } + log.Debugf("[benchmark job] Finish exists: iter= %d \n%v\n", idx, res) + return nil + }) + } + err := eg.Wait() + if err != nil { + log.Warnf("[benchmark job] exists RPC error is detected: err = %s", err.Error()) + return err } log.Info("[benchmark job] Finish benchmarking exists") return nil @@ -66,17 +78,10 @@ func (j *job) exists(ctx context.Context, ech chan error) error { func (j *job) getObject(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking getObject") - // create data - vecs := j.genVec(j.dataset) - for i := 0; i < len(vecs); i++ { - log.Infof("[benchmark job] Start getObject: iter = %d", i) - err := j.limiter.Wait(ctx) - if err != nil { - if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) - } - ech <- err - } + eg, egctx := errgroup.New(ctx) + eg.Limitation(j.concurrencyLimit) + for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { + log.Infof("[benchmark job] Start get object: iter = %d", i) ft := []*payload.Filter_Target{} if j.objectConfig != nil { for i, target := range j.objectConfig.FilterConfig.Targets { @@ -86,33 +91,53 @@ func (j *job) getObject(ctx context.Context, ech chan error) error { } } } - res, err := j.client.GetObject(ctx, &payload.Object_VectorRequest{ - Id: &payload.Object_ID{ - Id: strconv.Itoa(i), - }, - Filters: &payload.Filter_Config{ - Targets: ft, - }, - }) - if res != nil { - log.Infof("[benchmark get object job] iter=%d, Id=%s, Vec=%v", i, res.GetId(), res.GetVector()) - } - if err != nil { - select { - case <-ctx.Done(): + idx := i + eg.Go(func() error { + log.Debugf("[benchmark job] Start get object: iter = %d", idx) + err := j.limiter.Wait(egctx) + if err != nil { + log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) + // return errors.Join(err, context.Canceled) + return nil } select { - case <-ctx.Done(): - return errors.Join(err, context.Canceled) - case ech <- errors.Join(err, ctx.Err()): + case <-egctx.Done(): + return egctx.Err() + case ech <- err: } - default: - st, _ := status.FromError(err) - log.Warnf("[benchmark job] get object error is detected: code = %d, msg = %s", st.Code(), err.Error()) } - } + res, err := j.client.GetObject(egctx, &payload.Object_VectorRequest{ + Id: &payload.Object_ID{ + Id: strconv.Itoa(idx), + }, + Filters: &payload.Filter_Config{ + Targets: ft, + }, + }) + if err != nil { + select { + case <-egctx.Done(): + log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) + // return errors.Join(err, egctx.Err()) + return nil + default: + // if st, ok := status.FromError(err); ok { + // log.Warnf("[benchmark job] object error is detected: code = %d, msg = %s", st.Code(), err.Error()) + // } + } + } + if res != nil { + log.Infof("[benchmark get object job] iter=%d, Id=%s, Vec=%v", idx, res.GetId(), res.GetVector()) + } + log.Debugf("[benchmark job] Finish get object: iter= %d \n%v\n", idx, res) + return nil + }) + } + err := eg.Wait() + if err != nil { + log.Warnf("[benchmark job] object error is detected: err = %s", err.Error()) + return err } log.Info("[benchmark job] Finish benchmarking getObject") return nil diff --git a/pkg/tools/benchmark/job/service/option.go b/pkg/tools/benchmark/job/service/option.go index 772e638a6f..5b2d470fae 100644 --- a/pkg/tools/benchmark/job/service/option.go +++ b/pkg/tools/benchmark/job/service/option.go @@ -35,7 +35,8 @@ var defaultOpts = []Option{ // TODO: set default config for client WithDimension(748), WithBeforeJobDuration("30s"), - WithRPS(100), + WithRPS(1000), + WithConcurencyLimit(200), } // WithDimension sets the vector's dimension for running benchmark job with dataset. @@ -141,12 +142,15 @@ func WithHdf5(d hdf5.Data) Option { } } -// WithDataset sets the config.BenchmarkDataset including benchmakr dataset name, group name of hdf5.Data, the number of index, start range and end range. +// WithDataset sets the config.BenchmarkDataset including benchmark dataset name, group name of hdf5.Data, the number of index, start range and end range, and original URL which is used for download user defined hdf5. func WithDataset(d *config.BenchmarkDataset) Option { return func(j *job) error { if d == nil { return errors.NewErrInvalidOption("dataset", d) } + if d.Name == hdf5.Original.String() && len(d.URL) == 0 { + return errors.NewErrInvalidOption("dataset", d) + } j.dataset = d return nil } @@ -252,3 +256,13 @@ func WithRPS(rps int) Option { return nil } } + +// WithConcurencyLimit sets the goroutine limit for sending request to the target cluster. +func WithConcurencyLimit(limit int) Option { + return func(j *job) error { + if limit > 0 { + j.concurrencyLimit = limit + } + return nil + } +} diff --git a/pkg/tools/benchmark/job/service/remove.go b/pkg/tools/benchmark/job/service/remove.go index 7e6db88e12..66c604a6dd 100644 --- a/pkg/tools/benchmark/job/service/remove.go +++ b/pkg/tools/benchmark/job/service/remove.go @@ -22,55 +22,64 @@ import ( "strconv" "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "github.com/vdaas/vald/internal/net/grpc/status" ) func (j *job) remove(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking remove") - // create data - vecs := j.genVec(j.dataset) cfg := &payload.Remove_Config{ SkipStrictExistCheck: j.removeConfig.SkipStrictExistCheck, } if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - for i := 0; i < len(vecs); i++ { - log.Infof("[benchmark job] Start remove: iter = %d", i) - err := j.limiter.Wait(ctx) - if err != nil { - if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) - } - ech <- err - } - res, err := j.client.Remove(ctx, &payload.Remove_Request{ - Id: &payload.Object_ID{ - Id: strconv.Itoa(i), - }, - Config: cfg, - }) - if err != nil { - select { - case <-ctx.Done(): + eg, egctx := errgroup.New(ctx) + eg.Limitation(j.concurrencyLimit) + for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { + idx := i + eg.Go(func() error { + log.Debugf("[benchmark job] Start remove: iter = %d", i) + err := j.limiter.Wait(egctx) + if err != nil { + log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) + return nil + // return errors.Join(err, context.Canceled) + } + select { + case <-egctx.Done(): + return egctx.Err() + case ech <- err: } + } + res, err := j.client.Remove(egctx, &payload.Remove_Request{ + Id: &payload.Object_ID{ + Id: strconv.Itoa(idx), + }, + Config: cfg, + }) + if err != nil { select { - case <-ctx.Done(): - return errors.Join(err, context.Canceled) - case ech <- errors.Join(err, ctx.Err()): + case <-egctx.Done(): + log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) + return errors.Join(err, egctx.Err()) + default: + // if st, ok := status.FromError(err); ok { + // log.Warnf("[benchmark job] remove error is detected: code = %d, msg = %s", st.Code(), err.Error()) + // } } - default: - st, _ := status.FromError(err) - log.Warnf("[benchmark job] remove error is detected: code = %d, msg = %s", st.Code(), err.Error()) } - } - log.Infof("[benchmark job] Finish remove: iter= %d \n%v", i, res) + log.Debugf("[benchmark job] Finish remove: iter= %d \n%v", idx, res) + return nil + }) + } + err := eg.Wait() + if err != nil { + log.Warnf("[benchmark job] remove error is detected: err = %s", err.Error()) + return err } - log.Info("[benchmark job] Finish benchmarking remove") return nil } diff --git a/pkg/tools/benchmark/job/service/search.go b/pkg/tools/benchmark/job/service/search.go index dbc9bcbc35..d8fb0d25f9 100644 --- a/pkg/tools/benchmark/job/service/search.go +++ b/pkg/tools/benchmark/job/service/search.go @@ -19,98 +19,151 @@ package service import ( "context" + "math" "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "github.com/vdaas/vald/internal/net/grpc/codes" - "github.com/vdaas/vald/internal/net/grpc/status" ) func (j *job) search(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking search") // create data - vecs := j.genVec(j.dataset) + vecs := j.hdf5.GetByGroupName(j.dataset.Group) cfg := &payload.Search_Config{ Num: uint32(j.searchConfig.Num), MinNum: uint32(j.searchConfig.MinNum), Radius: float32(j.searchConfig.Radius), Epsilon: float32(j.searchConfig.Epsilon), Timeout: j.timeout.Nanoseconds(), + AggregationAlgorithm: func() payload.Search_AggregationAlgorithm { + if len(j.searchConfig.AggregationAlgorithm) > 0 { + if v, ok := payload.Search_AggregationAlgorithm_value[j.searchConfig.AggregationAlgorithm]; ok { + return payload.Search_AggregationAlgorithm(v) + } + } + return 0 + }(), } - lres := make([]*payload.Search_Response, len(vecs)) - for i := 0; i < len(vecs); i++ { - if len(vecs[i]) != j.dimension { - log.Warn("len(vecs) ", len(vecs[i]), "is not matched with ", j.dimension) - continue - } - res, err := j.client.LinearSearch(ctx, &payload.Search_Request{ - Vector: vecs[i], - Config: cfg, - }) - if err != nil { - select { - case <-ctx.Done(): + sres := make([]*payload.Search_Response, j.dataset.Indexes) + eg, egctx := errgroup.New(ctx) + eg.Limitation(j.concurrencyLimit) + for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { + iter := i + eg.Go(func() error { + log.Debugf("[benchmark job] Start search: iter = %d", iter) + err := j.limiter.Wait(egctx) + if err != nil { + log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) + return nil + // return errors.Join(err, context.Canceled) } select { - case <-ctx.Done(): - return errors.Join(err, context.Canceled) - case ech <- errors.Join(err, ctx.Err()): + case <-egctx.Done(): + return egctx.Err() + case ech <- err: } - default: - st, _ := status.FromError(err) - if st.Code() != codes.NotFound { - log.Warnf("[benchmark job] linear search error is detected: code = %d, msg = %s", st.Code(), err.Error()) + } + loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) + idx := iter - 1 - (len(vecs) * int(loopCnt)) + if len(vecs[idx]) != j.dimension { + log.Warn("len(vecs) ", len(vecs[iter]), "is not matched with ", j.dimension) + return nil + } + res, err := j.client.Search(egctx, &payload.Search_Request{ + Vector: vecs[idx], + Config: cfg, + }) + if err != nil { + select { + case <-egctx.Done(): + log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) + return nil + // return errors.Join(err, egctx.Err()) + default: + // if st, ok := status.FromError(err); ok { + // if st.Code() != codes.NotFound { + // log.Warnf("[benchmark job] search error is detected: code = %d, msg = %s", st.Code(), err.Error()) + // } + // } } } - } - lres[i] = res - } - sres := make([]*payload.Search_Response, len(vecs)) - log.Infof("[benchmark job] Start search") - for i := 0; i < len(vecs); i++ { - if len(vecs[i]) != j.dimension { - log.Warn("len(vecs) ", len(vecs[i]), "is not matched with ", j.dimension) - continue - } - err := j.limiter.Wait(ctx) - if err != nil { - if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) + if res != nil && j.searchConfig.EnableLinearSearch { + sres[iter-j.dataset.Range.Start] = res } - ech <- err - } - res, err := j.client.Search(ctx, &payload.Search_Request{ - Vector: vecs[i], - Config: cfg, + log.Debugf("[benchmark job] Finish search: iter = %d, len = %d", iter, len(res.Results)) + return nil }) - log.Infof("[benchmark job] search %d", i) - if err != nil { - select { - case <-ctx.Done(): - if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) + } + err := eg.Wait() + if err != nil { + log.Warnf("[benchmark job] search error is detected: err = %s", err.Error()) + return err + } + if j.searchConfig.EnableLinearSearch { + lres := make([]*payload.Search_Response, j.dataset.Indexes) + for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { + iter := i + eg.Go(func() error { + err := j.limiter.Wait(egctx) + if err != nil { + log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) + if errors.Is(err, context.Canceled) { + // return errors.Join(err, context.Canceled) + return nil + } + select { + case <-egctx.Done(): + return egctx.Err() + case ech <- err: + } } - select { - case <-ctx.Done(): - return errors.Join(err, context.Canceled) - case ech <- errors.Join(err, ctx.Err()): + log.Debugf("[benchmark job] Start linear search: iter = %d", iter) + loopCnt := math.Floor(float64(i-1) / float64(len(vecs))) + idx := iter - 1 - (len(vecs) * int(loopCnt)) + if len(vecs[idx]) != j.dimension { + log.Warn("len(vecs) ", len(vecs[idx]), "is not matched with ", j.dimension) + return nil } - default: - st, _ := status.FromError(err) - if st.Code() != codes.NotFound { - log.Warnf("[benchmark job] search error is detected: code = %d, msg = %s", st.Code(), err.Error()) + res, err := j.client.LinearSearch(egctx, &payload.Search_Request{ + Vector: vecs[idx], + Config: cfg, + }) + if err != nil { + select { + case <-egctx.Done(): + log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) + return errors.Join(err, egctx.Err()) + default: + // if st, ok := status.FromError(err); ok { + // if st.Code() != codes.NotFound { + // log.Warnf("[benchmark job] linear search error is detected: code = %d, msg = %s", st.Code(), err.Error()) + // } + // } + } } - } + if res != nil { + lres[idx-j.dataset.Range.Start] = res + } + log.Debugf("[benchmark job] Finish linear search: iter = %d", iter) + return nil + }) } - sres[i] = res - } - recall := make([]float64, len(vecs)) - for i := 0; i < len(vecs); i++ { - recall[i] = calcRecall(lres[i], sres[i]) - log.Info("[branch job] search recall: ", recall[i]) + err := eg.Wait() + if err != nil { + log.Warnf("[benchmark job] linear search error is detected: err = %s", err.Error()) + return err + } + recall := make([]float64, j.dataset.Indexes) + cnt := float64(0) + for i := 0; i < j.dataset.Indexes; i++ { + recall[i] = calcRecall(lres[i], sres[i]) + log.Info("[branch job] search recall: ", recall[i]) + cnt += recall[i] + } + log.Info("[benchmark job] Total search recall: ", (cnt / float64(len(vecs)))) } log.Info("[benchmark job] Finish benchmarking search") return nil diff --git a/pkg/tools/benchmark/job/service/update.go b/pkg/tools/benchmark/job/service/update.go index ac7a59a596..2c7ab12c49 100644 --- a/pkg/tools/benchmark/job/service/update.go +++ b/pkg/tools/benchmark/job/service/update.go @@ -19,18 +19,19 @@ package service import ( "context" + "math" "strconv" "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "github.com/vdaas/vald/internal/net/grpc/status" ) func (j *job) update(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking update") // create data - vecs := j.genVec(j.dataset) + vecs := j.hdf5.GetByGroupName(j.dataset.Group) cfg := &payload.Update_Config{ SkipStrictExistCheck: j.updateConfig.SkipStrictExistCheck, DisableBalancedUpdate: j.updateConfig.DisableBalancedUpdate, @@ -38,41 +39,56 @@ func (j *job) update(ctx context.Context, ech chan error) error { if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - for i := 0; i < len(vecs); i++ { - log.Infof("[benchmark job] Start update: iter = %d", i) - err := j.limiter.Wait(ctx) - if err != nil { - if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) - } - ech <- err - } - res, err := j.client.Update(ctx, &payload.Update_Request{ - Vector: &payload.Object_Vector{ - Id: strconv.Itoa(i), - Vector: vecs[i], - }, - Config: cfg, - }) - if err != nil { - select { - case <-ctx.Done(): + eg, egctx := errgroup.New(ctx) + eg.Limitation(j.concurrencyLimit) + for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { + iter := i + eg.Go(func() error { + log.Debugf("[benchmark job] Start update: iter = %d", iter) + err := j.limiter.Wait(egctx) + if err != nil { + log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) + // return errors.Join(err, context.Canceled) + return nil + } + select { + case <-egctx.Done(): + return egctx.Err() + case ech <- err: } + } + loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) + idx := iter - 1 - (len(vecs) * int(loopCnt)) + res, err := j.client.Update(egctx, &payload.Update_Request{ + Vector: &payload.Object_Vector{ + Id: strconv.Itoa(iter), + Vector: vecs[idx], + }, + Config: cfg, + }) + if err != nil { select { - case <-ctx.Done(): - return errors.Join(err, context.Canceled) - case ech <- errors.Join(err, ctx.Err()): + case <-egctx.Done(): + log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) + return errors.Join(err, egctx.Err()) + default: + // if st, ok := status.FromError(err); ok { + // log.Warnf("[benchmark job] update error is detected: code = %d, msg = %s\n", st.Code(), err.Error()) + // } } - default: - st, _ := status.FromError(err) - log.Warnf("[benchmark job] update error is detected: code = %d, msg = %s\n", st.Code(), err.Error()) } - } - if res != nil { - log.Infof("[benchmark job] iter=%d, Name=%s, Uuid=%s, Ips=%v", i, res.Name, res.Uuid, res.Ips) - } + if res != nil { + log.Infof("[benchmark job] iter=%d, Name=%s, Uuid=%s, Ips=%v", iter, res.Name, res.Uuid, res.Ips) + } + log.Debugf("[benchmark job] Finish update: iter = %d", iter) + return nil + }) + } + err := eg.Wait() + if err != nil { + log.Warnf("[benchmark job] update error is detected: err = %s", err.Error()) + return err } log.Info("[benchmark job] Finish benchmarking upsert") return nil diff --git a/pkg/tools/benchmark/job/service/upsert.go b/pkg/tools/benchmark/job/service/upsert.go index 4b3d664a83..a31f337972 100644 --- a/pkg/tools/benchmark/job/service/upsert.go +++ b/pkg/tools/benchmark/job/service/upsert.go @@ -19,18 +19,19 @@ package service import ( "context" + "math" "strconv" "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "github.com/vdaas/vald/internal/net/grpc/status" ) func (j *job) upsert(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking upsert") // create data - vecs := j.genVec(j.dataset) + vecs := j.hdf5.GetByGroupName(j.dataset.Group) cfg := &payload.Upsert_Config{ SkipStrictExistCheck: j.upsertConfig.SkipStrictExistCheck, DisableBalancedUpdate: j.upsertConfig.DisableBalancedUpdate, @@ -38,43 +39,56 @@ func (j *job) upsert(ctx context.Context, ech chan error) error { if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - for i := 0; i < len(vecs); i++ { - log.Infof("[benchmark job] Start upsert: iter = %d", i) - err := j.limiter.Wait(ctx) - if err != nil { - if errors.Is(err, context.Canceled) { - return errors.Join(err, context.Canceled) - } - ech <- err - } - res, err := j.client.Upsert(ctx, &payload.Upsert_Request{ - Vector: &payload.Object_Vector{ - Id: strconv.Itoa(i), - Vector: vecs[i], - }, - Config: cfg, - }) - if err != nil { - select { - case <-ctx.Done(): + eg, egctx := errgroup.New(ctx) + eg.Limitation(j.concurrencyLimit) + for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { + iter := i + eg.Go(func() error { + log.Debugf("[benchmark job] Start upsert: iter = %d", iter) + err := j.limiter.Wait(egctx) + if err != nil { + log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { return errors.Join(err, context.Canceled) } select { - case <-ctx.Done(): - return errors.Join(err, context.Canceled) - case ech <- errors.Join(err, ctx.Err()): + case <-egctx.Done(): + return egctx.Err() + case ech <- err: + } + } + loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) + idx := iter - 1 - (len(vecs) * int(loopCnt)) + res, err := j.client.Upsert(egctx, &payload.Upsert_Request{ + Vector: &payload.Object_Vector{ + Id: strconv.Itoa(iter), + Vector: vecs[idx], + }, + Config: cfg, + }) + if err != nil { + select { + case <-egctx.Done(): + log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) + return errors.Join(err, egctx.Err()) + default: + // if st, ok := status.FromError(err); ok { + // log.Warnf("[benchmark job] upsert error is detected: code = %d, msg = %s", st.Code(), err.Error()) + // } } - default: - st, _ := status.FromError(err) - log.Warnf("[benchmark job] upsert error is detected: code = %d, msg = %s", st.Code(), err.Error()) } - } - if res != nil { - log.Infof("[benchmark job] iter=%d, Name=%s, Uuid=%s, Ips=%v", i, res.Name, res.Uuid, res.Ips) - } + if res != nil { + log.Infof("[benchmark job] iter=%d, Name=%s, Uuid=%s, Ips=%v", idx, res.Name, res.Uuid, res.Ips) + } + log.Debugf("[benchmark job] Finish upsert: iter = %d", iter) + return nil + }) + } + err := eg.Wait() + if err != nil { + log.Warnf("[benchmark job] upsert error is detected: err = %s", err.Error()) + return err } - log.Info("[benchmark job] Finish benchmarking upsert") return nil } diff --git a/pkg/tools/benchmark/job/usecase/benchmarkd.go b/pkg/tools/benchmark/job/usecase/benchmarkd.go index f714bf9563..400c6d580a 100644 --- a/pkg/tools/benchmark/job/usecase/benchmarkd.go +++ b/pkg/tools/benchmark/job/usecase/benchmarkd.go @@ -79,7 +79,6 @@ func New(cfg *config.Config) (r runner.Runner, err error) { if err != nil { return nil, err } - d, err := hdf5.New( hdf5.WithNameByString(cfg.Job.Dataset.Name), ) @@ -87,7 +86,6 @@ func New(cfg *config.Config) (r runner.Runner, err error) { return nil, err } log.Info("pkg/tools/benchmark/job/cmd success d") - job, err := service.New( service.WithErrGroup(eg), service.WithValdClient(vcli), @@ -105,6 +103,7 @@ func New(cfg *config.Config) (r runner.Runner, err error) { service.WithBeforeJobNamespace(cfg.Job.BeforeJobNamespace), service.WithK8sClient(cfg.K8sClient), service.WithRPS(cfg.Job.RPS), + service.WithConcurencyLimit(cfg.Job.ConcurrencyLimit), ) if err != nil { return nil, err @@ -144,6 +143,39 @@ func New(cfg *config.Config) (r runner.Runner, err error) { } } + if len(cfg.Server.MetricsServers) == 0 { + cfg.Server.MetricsServers = []*iconf.Server{ + { + Name: "pprof", + Host: "0.0.0.0", + Port: uint16(6060), + HTTP: &iconf.HTTP{ + HandlerTimeout: "5s", + IdleTimeout: "2s", + ReadHeaderTimeout: "1s", + ReadTimeout: "1s", + ShutdownDuration: "5s", + WriteTimeout: "1m", + }, + Mode: "REST", + Network: "tcp", + ProbeWaitTime: "3s", + SocketOption: &iconf.SocketOption{ + IPRecoverDestinationAddr: false, + IPTransparent: false, + ReuseAddr: true, + ReusePort: true, + TCPCork: false, + TCPDeferAccept: true, + TCPFastOpen: true, + TCPNoDelay: true, + TCPQuickAck: true, + }, + SocketPath: "", + }, + } + } + srv, err := starter.New( starter.WithConfig(cfg.Server), starter.WithREST(func(sc *iconf.Server) []server.Option { diff --git a/pkg/tools/benchmark/operator/config/config.go b/pkg/tools/benchmark/operator/config/config.go index 45cec91f51..cf9acbdfd1 100644 --- a/pkg/tools/benchmark/operator/config/config.go +++ b/pkg/tools/benchmark/operator/config/config.go @@ -37,6 +37,9 @@ type Config struct { // Scenario represents benchmark scenario configurations Scenario *config.BenchmarkScenario `json:"scenario" yaml:"scenario"` + + // JobImage represents the location of Docker image for benchmark job and its ImagePullPolicy + JobImage *config.BenchmarkJobImageInfo `json:"job_image" yaml:"job_image"` } // NewConfig represents the set config from the given setting file path. @@ -58,6 +61,12 @@ func NewConfig(path string) (cfg *Config, err error) { cfg.Observability = cfg.Observability.Bind() } + if cfg.JobImage != nil { + cfg.JobImage = cfg.JobImage.Bind() + } else { + cfg.JobImage = new(config.BenchmarkJobImageInfo) + } + if cfg.Scenario != nil { cfg.Scenario = cfg.Scenario.Bind() } else { diff --git a/pkg/tools/benchmark/operator/service/operator.go b/pkg/tools/benchmark/operator/service/operator.go index 57b6cb4d15..c3c6872859 100644 --- a/pkg/tools/benchmark/operator/service/operator.go +++ b/pkg/tools/benchmark/operator/service/operator.go @@ -47,19 +47,22 @@ type scenario struct { const ( Scenario = "scenario" + ScenarioKind = "ValdBenchmarkScenario" BenchmarkName = "benchmark-name" BeforeJobName = "before-job-name" BeforeJobNamespace = "before-job-namespace" ) type operator struct { - jobNamespace string - scenarios atomic.Pointer[map[string]*scenario] - benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] - jobs atomic.Pointer[map[string]string] - rcd time.Duration // reconcile check duration - eg errgroup.Group - ctrl k8s.Controller + jobNamespace string + jobImage string + jobImagePullPolicy string + scenarios atomic.Pointer[map[string]*scenario] + benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + jobs atomic.Pointer[map[string]string] + rcd time.Duration // reconcile check duration + eg errgroup.Group + ctrl k8s.Controller } // New creates the new scenario struct to handle vald benchmark job scenario. @@ -251,6 +254,11 @@ func (o *operator) benchJobReconcile(ctx context.Context, benchJobList map[strin if err != nil { log.Warnf("[reconcile benchmark job resource] failed to delete old version job: job name=%s, version=%d\t%s", oldJob.GetName(), oldJob.GetGeneration(), err.Error()) } + // create new version job + err = o.createJob(ctx, job) + if err != nil { + log.Errorf("[reconcile benchmark job resource] failed to create new version job: %s", err.Error()) + } cbjl[k] = &job } } else if oldJob.Status == "" { @@ -326,7 +334,7 @@ func (o *operator) benchScenarioReconcile(ctx context.Context, scenarioList map[ // create new benchmark job resources of new version. jobNames, err := o.createBenchmarkJob(ctx, sc) if err != nil { - log.Errorf("[reconcile benchmark scenario resource] failed to create benchmark job resource: %s", err.Error()) + log.Errorf("[reconcile benchmark scenario resource] failed to create new version benchmark job resource: %s", err.Error()) } cbsl[name] = &scenario{ Crd: &sc, @@ -373,12 +381,18 @@ func (o *operator) deleteBenchmarkJob(ctx context.Context, name string, generati // deleteJob deletes job resource according to given benchmark job name and generation. func (o *operator) deleteJob(ctx context.Context, name string, generation int64) error { - opts := new(client.DeleteAllOfOptions) - client.MatchingLabels(map[string]string{ - BenchmarkName: name + strconv.Itoa(int(generation)), - }).ApplyToDeleteAllOf(opts) - client.InNamespace(o.jobNamespace).ApplyToDeleteAllOf(opts) - return o.ctrl.GetManager().GetClient().DeleteAllOf(ctx, &job.Job{}, opts) + cj := new(job.Job) + err := o.ctrl.GetManager().GetClient().Get(ctx, client.ObjectKey{ + Namespace: o.jobNamespace, + Name: name, + }, cj) + if err != nil { + return err + } + opts := new(client.DeleteOptions) + deleteProgation := client.DeletePropagationBackground + opts.PropagationPolicy = &deleteProgation + return o.ctrl.GetManager().GetClient().Delete(ctx, cj, opts) } // createBenchmarkJob creates the ValdBenchmarkJob crd for running job. @@ -434,10 +448,18 @@ func (o *operator) createBenchmarkJob(ctx context.Context, scenario v1.ValdBench // createJob creates benchmark job from benchmark job resource. func (o *operator) createJob(ctx context.Context, bjr v1.ValdBenchmarkJob) error { label := map[string]string{ - BenchmarkName: bjr.GetName() + strconv.Itoa(int(bjr.Generation)), + BenchmarkName: bjr.GetName() + strconv.Itoa(int(bjr.GetGeneration())), } - job, err := benchjob.NewBenchmarkJobTemplate( - benchjob.WithName(bjr.Name), + job, err := benchjob.NewBenchmarkJob( + benchjob.WithContainerName(bjr.GetName()), + benchjob.WithContainerImage(o.jobImage), + benchjob.WithImagePullPolicy(benchjob.ImagePullPolicy(o.jobImagePullPolicy)), + ) + if err != nil { + return err + } + tpl, err := job.CreateJobTpl( + benchjob.WithName(bjr.GetName()), benchjob.WithNamespace(bjr.Namespace), benchjob.WithLabel(label), benchjob.WithCompletions(int32(bjr.Spec.Repetition)), @@ -450,14 +472,15 @@ func (o *operator) createJob(ctx context.Context, bjr v1.ValdBenchmarkJob) error UID: bjr.UID, }, }), + benchjob.WithTTLSecondsAfterFinished(int32(bjr.Spec.TTLSecondsAfterFinished)), ) if err != nil { - return err + return nil } // create job c := o.ctrl.GetManager().GetClient() - if err = c.Create(ctx, &job); err != nil { - return errors.ErrFailedToCreateJob(err, job.GetName()) + if err = c.Create(ctx, &tpl); err != nil { + return errors.ErrFailedToCreateJob(err, tpl.GetName()) } return nil } @@ -546,37 +569,59 @@ func (o *operator) checkAtomics() error { cjl := o.getAtomicJob() cbjl := o.getAtomicBenchJob() cbsl := o.getAtomicScenario() - if len(cjl) == 0 { - if len(cbjl) > 0 || len(cbsl) > 0 { - log.Error("mismatch atomics: job=%v, benchjob=%v, scenario=%v", cjl, cbjl, cbsl) - return errors.ErrMismatchBenchmarkAtomics(cjl, cbjl, cbsl) - } - return nil - } else if len(cbjl) == 0 { - log.Error("mismatch atomics: job=%v, benchjob=%v, scenario=%v", cjl, cbjl, cbsl) + bjCompletedCnt := 0 + bjAvailableCnt := 0 + + if len(cbjl) == 0 && len(cbsl) > 0 && len(cjl) > 0 { + log.Errorf("mismatch atomics: job=%v, benchjob=%v, scenario=%v", cjl, cbjl, cbsl) return errors.ErrMismatchBenchmarkAtomics(cjl, cbjl, cbsl) } - jobCounter := len(cjl) - scenarioBenchCounter := 0 - for sc := range cbsl { - scenarioBenchCounter += len(cbsl[sc].BenchJobStatus) - } - for jobName := range cjl { - if benchJob := cbjl[jobName]; benchJob != nil { - jobCounter-- - if owner := benchJob.GetOwnerReferences(); len(owner) > 0 { - scenarioName := owner[0].Name - if scenario := cbsl[scenarioName]; scenario != nil { - if _, ok := scenario.BenchJobStatus[benchJob.GetName()]; ok { - scenarioBenchCounter-- - } + + for _, bj := range cbjl { + // check bench and job + if bj.Status == v1.BenchmarkJobCompleted { + bjCompletedCnt++ + } else { + bjAvailableCnt++ + if ns, ok := cjl[bj.GetName()]; !ok || ns != bj.GetNamespace() { + log.Errorf("mismatch atomics: job=%v, benchjob=%v, scenario=%v", cjl, cbjl, cbsl) + return errors.ErrMismatchBenchmarkAtomics(cjl, cbjl, cbsl) + } + } + // check scenario and bench + if owners := bj.GetOwnerReferences(); len(owners) > 0 { + var scenarioName string + for _, o := range owners { + if o.Kind == ScenarioKind { + scenarioName = o.Name + } + } + if sc := cbsl[scenarioName]; sc != nil { + if sc.BenchJobStatus[bj.Name] != bj.Status { + log.Errorf("mismatch atomics: job=%v, benchjob=%v, scenario=%v", cjl, cbjl, cbsl) + return errors.ErrMismatchBenchmarkAtomics(cjl, cbjl, cbsl) } + } else { + log.Errorf("mismatch atomics: job=%v, benchjob=%v, scenario=%v", cjl, cbjl, cbsl) + return errors.ErrMismatchBenchmarkAtomics(cjl, cbjl, cbsl) } } } - if jobCounter != 0 || scenarioBenchCounter != 0 { - log.Error("mismatch atomics: job=%v, benchjob=%v, scenario=%v", cjl, cbjl, cbsl) - return errors.ErrMismatchBenchmarkAtomics(cjl, cbjl, cbsl) + // check benchmarkjob status list and scenario benchmark job status list + if len(cbsl) > 0 { + for _, sc := range cbsl { + for _, status := range sc.BenchJobStatus { + if status == v1.BenchmarkJobCompleted { + bjCompletedCnt-- + } else { + bjAvailableCnt-- + } + } + } + if bjAvailableCnt != 0 || bjCompletedCnt != 0 { + log.Errorf("mismatch atomics: job=%v, benchjob=%v, scenario=%v", cjl, cbjl, cbsl) + return errors.ErrMismatchBenchmarkAtomics(cjl, cbjl, cbsl) + } } return nil } diff --git a/pkg/tools/benchmark/operator/service/option.go b/pkg/tools/benchmark/operator/service/option.go index a34242f5a5..f38c10f38e 100644 --- a/pkg/tools/benchmark/operator/service/option.go +++ b/pkg/tools/benchmark/operator/service/option.go @@ -28,6 +28,8 @@ import ( type Option func(o *operator) error var defaultOpts = []Option{ + WithJobImage("vdaas/vald-benchmark-job"), + WithJobImagePullPolicy("Always"), WithReconcileCheckDuration("10s"), WithJobNamespace("default"), } @@ -66,3 +68,23 @@ func WithJobNamespace(ns string) Option { return nil } } + +// WithJobImage sets the benchmark job docker image info. +func WithJobImage(image string) Option { + return func(o *operator) error { + if len(image) > 0 { + o.jobImage = image + } + return nil + } +} + +// WithJobImagePullPolicy sets the benchmark job docker image pullPolicy. +func WithJobImagePullPolicy(p string) Option { + return func(o *operator) error { + if len(p) > 0 { + o.jobImagePullPolicy = p + } + return nil + } +} diff --git a/pkg/tools/benchmark/operator/usecase/benchmarkd.go b/pkg/tools/benchmark/operator/usecase/benchmarkd.go index 9f77e9ae15..e867473bc5 100644 --- a/pkg/tools/benchmark/operator/usecase/benchmarkd.go +++ b/pkg/tools/benchmark/operator/usecase/benchmarkd.go @@ -19,6 +19,7 @@ package usecase import ( "context" + "os" iconf "github.com/vdaas/vald/internal/config" "github.com/vdaas/vald/internal/errgroup" @@ -48,6 +49,8 @@ type run struct { observability observability.Observability } +var JOB_NAMESPACE = os.Getenv("JOB_NAMESPACE") + func New(cfg *config.Config) (r runner.Runner, err error) { log.Info("pkg/tools/benchmark/scenario/cmd start") @@ -57,6 +60,9 @@ func New(cfg *config.Config) (r runner.Runner, err error) { operator, err := service.New( service.WithErrGroup(eg), + service.WithJobNamespace(JOB_NAMESPACE), + service.WithJobImage(cfg.JobImage.Image), + service.WithJobImagePullPolicy(cfg.JobImage.PullPolicy), ) if err != nil { return nil, err From 9a9b981eb00e666aa8a2b5243ad9a681d2530a23 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 04:03:29 +0000 Subject: [PATCH 06/11] style: Format code with prettier and gofumpt --- apis/docs/v1/docs.md | 1 + 1 file changed, 1 insertion(+) diff --git a/apis/docs/v1/docs.md b/apis/docs/v1/docs.md index 83195cb6da..0b459b9ca6 100644 --- a/apis/docs/v1/docs.md +++ b/apis/docs/v1/docs.md @@ -128,6 +128,7 @@ - [apis/proto/v1/benchmark/benchmark.proto](#apis_proto_v1_benchmark_benchmark-proto) - [Controller](#benchmark-v1-Controller) - [Job](#benchmark-v1-Job) + - [Scalar Value Types](#scalar-value-types) From ed8564457f4c7e3ed460d3ae9de6b32b08d06f30 Mon Sep 17 00:00:00 2001 From: Kiichiro YUKAWA Date: Fri, 25 Aug 2023 12:16:42 +0900 Subject: [PATCH 07/11] Update charts and improvement conbench (#2119) * Implement base of continuous benchmark tool (#1776) * Create Continuous Bench Search Job tool (#1733) * :sparkles: create bench job search tools Signed-off-by: vankichi * :sparkles: add load hdf5 functions Signed-off-by: vankichi * :recycle: fix format Signed-off-by: vankichi * :recycle: fix docker and use hdf5 data Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: refactor benchmark job Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: fix proto Signed-off-by: vankichi * :green_heart: add benchmark job image build ci Signed-off-by: vankichi * :green_heart: invest Signed-off-by: vankichi * Revert ":green_heart: invest" This reverts commit f0f585ccf71b1c95a88559941557a27774096e69. * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply code review Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Hiroto Funakoshi * :sparkles: apply from feedback Signed-off-by: vankichi * Update internal/config/benchmark.go Co-authored-by: Yusuke Kato * :recycle: change directory path Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi * Add crds for continuous benchmark tools (#1789) * :sparkles: add crds for continuous benchmark operator Signed-off-by: vankichi * :sparkles: add benchmark operator/job scheme Signed-off-by: vankichi * :sparkles: rename package names and add doc.go Signed-off-by: vankichi * :sparkles: create runtime object Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply feedback Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato * Add Job reconciler & Change directory constitution of internal/k8s for benchmark (#1825) * :sparkles: :recycle: add Job reconciler & use scenario instead of operator Signed-off-by: vankichi * :recycle: fix format & rename file Signed-off-by: vankichi Signed-off-by: vankichi * Add benchmark operator framework (#1916) * :sparkles: impl benchmark reconciler Signed-off-by: vankichi * :sparkles: create benchmark operator framework Signed-off-by: vankichi * :recycle: remove unness changes Signed-off-by: vankichi Signed-off-by: vankichi * Format code with prettier and gofumpt * impl reconcile logic for create benchmark job (#1923) * :sparkles: impl reconcile logic for create benchmark job Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: fix Signed-off-by: vankichi * :recycle: refactor continuous benchmark's crds Signed-off-by: vankichi * :recycle: resolve error due to update conn bench crds for pkg/tools/benchmark/job Signed-off-by: vankichi * :recycle: refactor continuous benchmark job logic Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: update charts Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: rafactor con bench config and bug fix reconcile logic Signed-off-by: vankichi * :bug: Bugfix: fix typo and recall function logic Signed-off-by: vankichi * :recycle: refactor pkg benchmark job Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :sparkles: impl status handle of continuous benchmark crds (#1955) Signed-off-by: vankichi * Impl benchmark jobs (#1977) * Implement base of continuous benchmark tool (#1776) * Create Continuous Bench Search Job tool (#1733) * :sparkles: create bench job search tools Signed-off-by: vankichi * :sparkles: add load hdf5 functions Signed-off-by: vankichi * :recycle: fix format Signed-off-by: vankichi * :recycle: fix docker and use hdf5 data Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: refactor benchmark job Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: fix proto Signed-off-by: vankichi * :green_heart: add benchmark job image build ci Signed-off-by: vankichi * :green_heart: invest Signed-off-by: vankichi * Revert ":green_heart: invest" This reverts commit f0f585ccf71b1c95a88559941557a27774096e69. * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply code review Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Hiroto Funakoshi * :sparkles: apply from feedback Signed-off-by: vankichi * Update internal/config/benchmark.go Co-authored-by: Yusuke Kato * :recycle: change directory path Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi * Add crds for continuous benchmark tools (#1789) * :sparkles: add crds for continuous benchmark operator Signed-off-by: vankichi * :sparkles: add benchmark operator/job scheme Signed-off-by: vankichi * :sparkles: rename package names and add doc.go Signed-off-by: vankichi * :sparkles: create runtime object Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply feedback Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato * Add Job reconciler & Change directory constitution of internal/k8s for benchmark (#1825) * :sparkles: :recycle: add Job reconciler & use scenario instead of operator Signed-off-by: vankichi * :recycle: fix format & rename file Signed-off-by: vankichi Signed-off-by: vankichi * Add benchmark operator framework (#1916) * :sparkles: impl benchmark reconciler Signed-off-by: vankichi * :sparkles: create benchmark operator framework Signed-off-by: vankichi * :recycle: remove unness changes Signed-off-by: vankichi Signed-off-by: vankichi * Format code with prettier and gofumpt * impl reconcile logic for create benchmark job (#1923) * :sparkles: impl reconcile logic for create benchmark job Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: fix Signed-off-by: vankichi * :recycle: refactor continuous benchmark's crds Signed-off-by: vankichi * :recycle: resolve error due to update conn bench crds for pkg/tools/benchmark/job Signed-off-by: vankichi * :recycle: refactor continuous benchmark job logic Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: update charts Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: rafactor con bench config and bug fix reconcile logic Signed-off-by: vankichi * :bug: Bugfix: fix typo and recall function logic Signed-off-by: vankichi * :recycle: refactor pkg benchmark job Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :sparkles: impl benchmark jobs Signed-off-by: vankichi * :recycle: apply feedback Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * create helm template for benchmark operator (#2027) * :sparkles: create helm template for benchmark operator Signed-off-by: vankichi * :sparkles: refactor helm template Signed-off-by: vankichi * style: Format code with gofumpt and prettier --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * Refactor helm template and operator logic (#2043) * Implement base of continuous benchmark tool (#1776) * Create Continuous Bench Search Job tool (#1733) * :sparkles: create bench job search tools Signed-off-by: vankichi * :sparkles: add load hdf5 functions Signed-off-by: vankichi * :recycle: fix format Signed-off-by: vankichi * :recycle: fix docker and use hdf5 data Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: refactor benchmark job Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: fix proto Signed-off-by: vankichi * :green_heart: add benchmark job image build ci Signed-off-by: vankichi * :green_heart: invest Signed-off-by: vankichi * Revert ":green_heart: invest" This reverts commit f0f585ccf71b1c95a88559941557a27774096e69. * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply code review Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Hiroto Funakoshi * :sparkles: apply from feedback Signed-off-by: vankichi * Update internal/config/benchmark.go Co-authored-by: Yusuke Kato * :recycle: change directory path Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi * Add crds for continuous benchmark tools (#1789) * :sparkles: add crds for continuous benchmark operator Signed-off-by: vankichi * :sparkles: add benchmark operator/job scheme Signed-off-by: vankichi * :sparkles: rename package names and add doc.go Signed-off-by: vankichi * :sparkles: create runtime object Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply feedback Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato * Add Job reconciler & Change directory constitution of internal/k8s for benchmark (#1825) * :sparkles: :recycle: add Job reconciler & use scenario instead of operator Signed-off-by: vankichi * :recycle: fix format & rename file Signed-off-by: vankichi Signed-off-by: vankichi * Add benchmark operator framework (#1916) * :sparkles: impl benchmark reconciler Signed-off-by: vankichi * :sparkles: create benchmark operator framework Signed-off-by: vankichi * :recycle: remove unness changes Signed-off-by: vankichi Signed-off-by: vankichi * Format code with prettier and gofumpt * impl reconcile logic for create benchmark job (#1923) * :sparkles: impl reconcile logic for create benchmark job Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: fix Signed-off-by: vankichi * :recycle: refactor continuous benchmark's crds Signed-off-by: vankichi * :recycle: resolve error due to update conn bench crds for pkg/tools/benchmark/job Signed-off-by: vankichi * :recycle: refactor continuous benchmark job logic Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: update charts Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: rafactor con bench config and bug fix reconcile logic Signed-off-by: vankichi * :bug: Bugfix: fix typo and recall function logic Signed-off-by: vankichi * :recycle: refactor pkg benchmark job Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :sparkles: impl status handle of continuous benchmark crds (#1955) Signed-off-by: vankichi * Impl benchmark jobs (#1977) * Implement base of continuous benchmark tool (#1776) * Create Continuous Bench Search Job tool (#1733) * :sparkles: create bench job search tools Signed-off-by: vankichi * :sparkles: add load hdf5 functions Signed-off-by: vankichi * :recycle: fix format Signed-off-by: vankichi * :recycle: fix docker and use hdf5 data Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: refactor benchmark job Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: fix proto Signed-off-by: vankichi * :green_heart: add benchmark job image build ci Signed-off-by: vankichi * :green_heart: invest Signed-off-by: vankichi * Revert ":green_heart: invest" This reverts commit f0f585ccf71b1c95a88559941557a27774096e69. * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply code review Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Hiroto Funakoshi * :sparkles: apply from feedback Signed-off-by: vankichi * Update internal/config/benchmark.go Co-authored-by: Yusuke Kato * :recycle: change directory path Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi * Add crds for continuous benchmark tools (#1789) * :sparkles: add crds for continuous benchmark operator Signed-off-by: vankichi * :sparkles: add benchmark operator/job scheme Signed-off-by: vankichi * :sparkles: rename package names and add doc.go Signed-off-by: vankichi * :sparkles: create runtime object Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply feedback Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato * Add Job reconciler & Change directory constitution of internal/k8s for benchmark (#1825) * :sparkles: :recycle: add Job reconciler & use scenario instead of operator Signed-off-by: vankichi * :recycle: fix format & rename file Signed-off-by: vankichi Signed-off-by: vankichi * Add benchmark operator framework (#1916) * :sparkles: impl benchmark reconciler Signed-off-by: vankichi * :sparkles: create benchmark operator framework Signed-off-by: vankichi * :recycle: remove unness changes Signed-off-by: vankichi Signed-off-by: vankichi * Format code with prettier and gofumpt * impl reconcile logic for create benchmark job (#1923) * :sparkles: impl reconcile logic for create benchmark job Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: fix Signed-off-by: vankichi * :recycle: refactor continuous benchmark's crds Signed-off-by: vankichi * :recycle: resolve error due to update conn bench crds for pkg/tools/benchmark/job Signed-off-by: vankichi * :recycle: refactor continuous benchmark job logic Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: update charts Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: rafactor con bench config and bug fix reconcile logic Signed-off-by: vankichi * :bug: Bugfix: fix typo and recall function logic Signed-off-by: vankichi * :recycle: refactor pkg benchmark job Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :sparkles: impl benchmark jobs Signed-off-by: vankichi * :recycle: apply feedback Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :recycle: Refactor helm template and operator logic Signed-off-by: vankichi * :recycle: Add download original dataset URL option Signed-off-by: vankichi * :recycle: Set docker image location at the benchmark operator configmap and use it when information is set Signed-off-by: vankichi * add search algorithm benchmark and update search aggregation algo Signed-off-by: kpango * :sparkles: Add search result aggregation option Signed-off-by: vankichi * style: Format code with prettier and gofumpt * Improve job performance (#2061) * :bug: Fix job function to apply rate limiter * :recycle: Add pyroscope setting Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :bug: Fix build error Signed-off-by: vankichi --------- Signed-off-by: vankichi * :bug: Fix docker file and add concurrencyLimit for job goroutine Signed-off-by: vankichi * :recycle: Fix job_template.go by feedback Signed-off-by: vankichi * :recycle: Fix job logic by feedback Signed-off-by: vankichi * :recycle: Fix Signed-off-by: vankichi --------- Signed-off-by: vankichi Signed-off-by: kpango Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * Impl benchmark jobs (#1977) * Implement base of continuous benchmark tool (#1776) * Create Continuous Bench Search Job tool (#1733) * :sparkles: create bench job search tools Signed-off-by: vankichi * :sparkles: add load hdf5 functions Signed-off-by: vankichi * :recycle: fix format Signed-off-by: vankichi * :recycle: fix docker and use hdf5 data Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: refactor benchmark job Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: fix proto Signed-off-by: vankichi * :green_heart: add benchmark job image build ci Signed-off-by: vankichi * :green_heart: invest Signed-off-by: vankichi * Revert ":green_heart: invest" This reverts commit f0f585ccf71b1c95a88559941557a27774096e69. * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply code review Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Hiroto Funakoshi * :sparkles: apply from feedback Signed-off-by: vankichi * Update internal/config/benchmark.go Co-authored-by: Yusuke Kato * :recycle: change directory path Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi * Add crds for continuous benchmark tools (#1789) * :sparkles: add crds for continuous benchmark operator Signed-off-by: vankichi * :sparkles: add benchmark operator/job scheme Signed-off-by: vankichi * :sparkles: rename package names and add doc.go Signed-off-by: vankichi * :sparkles: create runtime object Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply feedback Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato * Add Job reconciler & Change directory constitution of internal/k8s for benchmark (#1825) * :sparkles: :recycle: add Job reconciler & use scenario instead of operator Signed-off-by: vankichi * :recycle: fix format & rename file Signed-off-by: vankichi Signed-off-by: vankichi * Add benchmark operator framework (#1916) * :sparkles: impl benchmark reconciler Signed-off-by: vankichi * :sparkles: create benchmark operator framework Signed-off-by: vankichi * :recycle: remove unness changes Signed-off-by: vankichi Signed-off-by: vankichi * Format code with prettier and gofumpt * impl reconcile logic for create benchmark job (#1923) * :sparkles: impl reconcile logic for create benchmark job Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: fix Signed-off-by: vankichi * :recycle: refactor continuous benchmark's crds Signed-off-by: vankichi * :recycle: resolve error due to update conn bench crds for pkg/tools/benchmark/job Signed-off-by: vankichi * :recycle: refactor continuous benchmark job logic Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: update charts Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: rafactor con bench config and bug fix reconcile logic Signed-off-by: vankichi * :bug: Bugfix: fix typo and recall function logic Signed-off-by: vankichi * :recycle: refactor pkg benchmark job Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :sparkles: impl benchmark jobs Signed-off-by: vankichi * :recycle: apply feedback Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * Refactor helm template and operator logic (#2043) * Implement base of continuous benchmark tool (#1776) * Create Continuous Bench Search Job tool (#1733) * :sparkles: create bench job search tools Signed-off-by: vankichi * :sparkles: add load hdf5 functions Signed-off-by: vankichi * :recycle: fix format Signed-off-by: vankichi * :recycle: fix docker and use hdf5 data Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: refactor benchmark job Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: fix proto Signed-off-by: vankichi * :green_heart: add benchmark job image build ci Signed-off-by: vankichi * :green_heart: invest Signed-off-by: vankichi * Revert ":green_heart: invest" This reverts commit f0f585ccf71b1c95a88559941557a27774096e69. * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply code review Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Hiroto Funakoshi * :sparkles: apply from feedback Signed-off-by: vankichi * Update internal/config/benchmark.go Co-authored-by: Yusuke Kato * :recycle: change directory path Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi * Add crds for continuous benchmark tools (#1789) * :sparkles: add crds for continuous benchmark operator Signed-off-by: vankichi * :sparkles: add benchmark operator/job scheme Signed-off-by: vankichi * :sparkles: rename package names and add doc.go Signed-off-by: vankichi * :sparkles: create runtime object Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply feedback Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato * Add Job reconciler & Change directory constitution of internal/k8s for benchmark (#1825) * :sparkles: :recycle: add Job reconciler & use scenario instead of operator Signed-off-by: vankichi * :recycle: fix format & rename file Signed-off-by: vankichi Signed-off-by: vankichi * Add benchmark operator framework (#1916) * :sparkles: impl benchmark reconciler Signed-off-by: vankichi * :sparkles: create benchmark operator framework Signed-off-by: vankichi * :recycle: remove unness changes Signed-off-by: vankichi Signed-off-by: vankichi * Format code with prettier and gofumpt * impl reconcile logic for create benchmark job (#1923) * :sparkles: impl reconcile logic for create benchmark job Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: fix Signed-off-by: vankichi * :recycle: refactor continuous benchmark's crds Signed-off-by: vankichi * :recycle: resolve error due to update conn bench crds for pkg/tools/benchmark/job Signed-off-by: vankichi * :recycle: refactor continuous benchmark job logic Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: update charts Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: rafactor con bench config and bug fix reconcile logic Signed-off-by: vankichi * :bug: Bugfix: fix typo and recall function logic Signed-off-by: vankichi * :recycle: refactor pkg benchmark job Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :sparkles: impl status handle of continuous benchmark crds (#1955) Signed-off-by: vankichi * Impl benchmark jobs (#1977) * Implement base of continuous benchmark tool (#1776) * Create Continuous Bench Search Job tool (#1733) * :sparkles: create bench job search tools Signed-off-by: vankichi * :sparkles: add load hdf5 functions Signed-off-by: vankichi * :recycle: fix format Signed-off-by: vankichi * :recycle: fix docker and use hdf5 data Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: refactor benchmark job Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :recycle: fix proto Signed-off-by: vankichi * :green_heart: add benchmark job image build ci Signed-off-by: vankichi * :green_heart: invest Signed-off-by: vankichi * Revert ":green_heart: invest" This reverts commit f0f585ccf71b1c95a88559941557a27774096e69. * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply code review Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Hiroto Funakoshi * :sparkles: apply from feedback Signed-off-by: vankichi * Update internal/config/benchmark.go Co-authored-by: Yusuke Kato * :recycle: change directory path Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi * Add crds for continuous benchmark tools (#1789) * :sparkles: add crds for continuous benchmark operator Signed-off-by: vankichi * :sparkles: add benchmark operator/job scheme Signed-off-by: vankichi * :sparkles: rename package names and add doc.go Signed-off-by: vankichi * :sparkles: create runtime object Signed-off-by: vankichi * Apply suggestions from code review Co-authored-by: Yusuke Kato * :recycle: apply feedback Signed-off-by: vankichi Signed-off-by: vankichi Co-authored-by: Yusuke Kato * Add Job reconciler & Change directory constitution of internal/k8s for benchmark (#1825) * :sparkles: :recycle: add Job reconciler & use scenario instead of operator Signed-off-by: vankichi * :recycle: fix format & rename file Signed-off-by: vankichi Signed-off-by: vankichi * Add benchmark operator framework (#1916) * :sparkles: impl benchmark reconciler Signed-off-by: vankichi * :sparkles: create benchmark operator framework Signed-off-by: vankichi * :recycle: remove unness changes Signed-off-by: vankichi Signed-off-by: vankichi * Format code with prettier and gofumpt * impl reconcile logic for create benchmark job (#1923) * :sparkles: impl reconcile logic for create benchmark job Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: fix Signed-off-by: vankichi * :recycle: refactor continuous benchmark's crds Signed-off-by: vankichi * :recycle: resolve error due to update conn bench crds for pkg/tools/benchmark/job Signed-off-by: vankichi * :recycle: refactor continuous benchmark job logic Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: update charts Signed-off-by: vankichi * Format code with prettier and gofumpt * :recycle: rafactor con bench config and bug fix reconcile logic Signed-off-by: vankichi * :bug: Bugfix: fix typo and recall function logic Signed-off-by: vankichi * :recycle: refactor pkg benchmark job Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :sparkles: impl benchmark jobs Signed-off-by: vankichi * :recycle: apply feedback Signed-off-by: vankichi --------- Signed-off-by: vankichi Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :recycle: Refactor helm template and operator logic Signed-off-by: vankichi * :recycle: Add download original dataset URL option Signed-off-by: vankichi * :recycle: Set docker image location at the benchmark operator configmap and use it when information is set Signed-off-by: vankichi * add search algorithm benchmark and update search aggregation algo Signed-off-by: kpango * :sparkles: Add search result aggregation option Signed-off-by: vankichi * style: Format code with prettier and gofumpt * Improve job performance (#2061) * :bug: Fix job function to apply rate limiter * :recycle: Add pyroscope setting Signed-off-by: vankichi * :recycle: fix Signed-off-by: vankichi * :bug: Fix build error Signed-off-by: vankichi --------- Signed-off-by: vankichi * :bug: Fix docker file and add concurrencyLimit for job goroutine Signed-off-by: vankichi * :recycle: Fix job_template.go by feedback Signed-off-by: vankichi * :recycle: Fix job logic by feedback Signed-off-by: vankichi * :recycle: Fix Signed-off-by: vankichi --------- Signed-off-by: vankichi Signed-off-by: kpango Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * :recycle: Add noise to vector when update/upsert and update crd Signed-off-by: vankichi * :recycle: add deepmerge func for override default config by user-defined config Signed-off-by: vankichi * :recycle: refactor deepmerge Signed-off-by: vankichi * style: Format code with prettier and gofumpt * style: Format code with prettier and gofumpt * :recycle: change add noise func Signed-off-by: vankichi * :recycle: use golang/sync/error for prevent OOM KILL Signed-off-by: vankichi * :recycle: fix config adn add test Signed-off-by: vankichi * style: Format code with prettier and gofumpt --------- Signed-off-by: vankichi Signed-off-by: kpango Co-authored-by: Yusuke Kato Co-authored-by: Hiroto Funakoshi Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --- apis/docs/v1/docs.md | 25 + .../crds/valdbenchmarkjob.yaml | 438 ++++++++++ .../job-values.schema.json | 748 +++++++++++++++++- .../scenario-values.schema.json | 2 +- .../schemas/job-values.yaml | 476 ++++++++++- .../values.schema.json | 2 +- cmd/tools/benchmark/job/sample.yaml | 296 ++++--- go.mod | 5 +- go.sum | 4 +- internal/config/config.go | 130 +++ internal/config/config_test.go | 459 ++++++++++- internal/errors/config.go | 18 + internal/k8s/client/client.go | 4 +- .../k8s/vald/benchmark/api/v1/job_types.go | 36 +- .../operator/crds/valdbenchmarkjob.yaml | 438 ++++++++++ pkg/tools/benchmark/job/config/config.go | 247 ++++-- .../benchmark/job/handler/grpc/handler.go | 6 +- pkg/tools/benchmark/job/service/insert.go | 8 +- pkg/tools/benchmark/job/service/job.go | 38 +- pkg/tools/benchmark/job/service/object.go | 14 +- pkg/tools/benchmark/job/service/remove.go | 8 +- pkg/tools/benchmark/job/service/search.go | 11 +- pkg/tools/benchmark/job/service/update.go | 10 +- pkg/tools/benchmark/job/service/upsert.go | 10 +- pkg/tools/benchmark/job/usecase/benchmarkd.go | 33 - .../operator/handler/grpc/handler.go | 4 - 26 files changed, 3172 insertions(+), 298 deletions(-) diff --git a/apis/docs/v1/docs.md b/apis/docs/v1/docs.md index 0b459b9ca6..e84a065c14 100644 --- a/apis/docs/v1/docs.md +++ b/apis/docs/v1/docs.md @@ -126,6 +126,7 @@ - [ResourceInfo](#rpc-v1-ResourceInfo) - [RetryInfo](#rpc-v1-RetryInfo) - [apis/proto/v1/benchmark/benchmark.proto](#apis_proto_v1_benchmark_benchmark-proto) + - [Controller](#benchmark-v1-Controller) - [Job](#benchmark-v1-Job) @@ -1402,6 +1403,30 @@ reached. | ----------- | ----------------------------------------------------- | ----- | ------------------------------------------------------------------------- | | retry_delay | [google.protobuf.Duration](#google-protobuf-Duration) | | Clients should wait at least this long between retrying the same request. | + + +

Top

+ +## apis/proto/v1/benchmark/benchmark.proto + + + +### Controller + +TODO define API spec here + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ----------- | + + + +### Job + +TODO define API spec here + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ----------- | + ## Scalar Value Types | .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | diff --git a/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml b/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml index d85ea0c12b..f5669958ee 100644 --- a/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml +++ b/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml @@ -280,6 +280,34 @@ spec: dimension: type: integer minimum: 1 + global_config: + type: object + properties: + logging: + type: object + properties: + format: + type: string + enum: + - raw + - json + level: + type: string + enum: + - debug + - info + - warn + - error + - fatal + logger: + type: string + enum: + - glg + - zap + time_zone: + type: string + version: + type: string insert_config: type: object properties: @@ -349,6 +377,416 @@ spec: type: number timeout: type: string + server_config: + type: object + properties: + healths: + type: object + properties: + liveness: + type: object + properties: + enabled: + type: boolean + host: + type: string + livenessProbe: + type: object + properties: + failureThreshold: + type: integer + httpGet: + type: object + properties: + path: + type: string + port: + type: string + scheme: + type: string + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + port: + type: integer + maximum: 65535 + minimum: 0 + server: + type: object + properties: + http: + type: object + properties: + handler_timeout: + type: string + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + write_timeout: + type: string + mode: + type: string + network: + type: string + enum: + - tcp + - tcp4 + - tcp6 + - udp + - udp4 + - udp6 + - unix + - unixgram + - unixpacket + probe_wait_time: + type: string + socket_option: + type: object + properties: + ip_recover_destination_addr: + type: boolean + ip_transparent: + type: boolean + reuse_addr: + type: boolean + reuse_port: + type: boolean + tcp_cork: + type: boolean + tcp_defer_accept: + type: boolean + tcp_fast_open: + type: boolean + tcp_no_delay: + type: boolean + tcp_quick_ack: + type: boolean + socket_path: + type: string + servicePort: + type: integer + maximum: 65535 + minimum: 0 + readiness: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: integer + maximum: 65535 + minimum: 0 + readinessProbe: + type: object + properties: + failureThreshold: + type: integer + httpGet: + type: object + properties: + path: + type: string + port: + type: string + scheme: + type: string + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + server: + type: object + properties: + http: + type: object + properties: + handler_timeout: + type: string + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + write_timeout: + type: string + mode: + type: string + network: + type: string + enum: + - tcp + - tcp4 + - tcp6 + - udp + - udp4 + - udp6 + - unix + - unixgram + - unixpacket + probe_wait_time: + type: string + socket_option: + type: object + properties: + ip_recover_destination_addr: + type: boolean + ip_transparent: + type: boolean + reuse_addr: + type: boolean + reuse_port: + type: boolean + tcp_cork: + type: boolean + tcp_defer_accept: + type: boolean + tcp_fast_open: + type: boolean + tcp_no_delay: + type: boolean + tcp_quick_ack: + type: boolean + socket_path: + type: string + servicePort: + type: integer + maximum: 65535 + minimum: 0 + startup: + type: object + properties: + enabled: + type: boolean + port: + type: integer + maximum: 65535 + minimum: 0 + startupProbe: + type: object + properties: + failureThreshold: + type: integer + httpGet: + type: object + properties: + path: + type: string + port: + type: string + scheme: + type: string + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + servers: + type: object + properties: + grpc: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: integer + maximum: 65535 + minimum: 0 + server: + type: object + properties: + grpc: + type: object + properties: + bidirectional_stream_concurrency: + type: integer + connection_timeout: + type: string + enable_reflection: + type: boolean + header_table_size: + type: integer + initial_conn_window_size: + type: integer + initial_window_size: + type: integer + interceptors: + type: array + items: + type: string + enum: + - RecoverInterceptor + - AccessLogInterceptor + - TraceInterceptor + - MetricInterceptor + keepalive: + type: object + properties: + max_conn_age: + type: string + max_conn_age_grace: + type: string + max_conn_idle: + type: string + min_time: + type: string + permit_without_stream: + type: boolean + time: + type: string + timeout: + type: string + max_header_list_size: + type: integer + max_receive_message_size: + type: integer + max_send_message_size: + type: integer + read_buffer_size: + type: integer + write_buffer_size: + type: integer + mode: + type: string + network: + type: string + enum: + - tcp + - tcp4 + - tcp6 + - udp + - udp4 + - udp6 + - unix + - unixgram + - unixpacket + probe_wait_time: + type: string + restart: + type: boolean + socket_option: + type: object + properties: + ip_recover_destination_addr: + type: boolean + ip_transparent: + type: boolean + reuse_addr: + type: boolean + reuse_port: + type: boolean + tcp_cork: + type: boolean + tcp_defer_accept: + type: boolean + tcp_fast_open: + type: boolean + tcp_no_delay: + type: boolean + tcp_quick_ack: + type: boolean + socket_path: + type: string + servicePort: + type: integer + maximum: 65535 + minimum: 0 + rest: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: integer + maximum: 65535 + minimum: 0 + server: + type: object + properties: + http: + type: object + properties: + handler_timeout: + type: string + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + write_timeout: + type: string + mode: + type: string + network: + type: string + enum: + - tcp + - tcp4 + - tcp6 + - udp + - udp4 + - udp6 + - unix + - unixgram + - unixpacket + probe_wait_time: + type: string + socket_option: + type: object + properties: + ip_recover_destination_addr: + type: boolean + ip_transparent: + type: boolean + reuse_addr: + type: boolean + reuse_port: + type: boolean + tcp_cork: + type: boolean + tcp_defer_accept: + type: boolean + tcp_fast_open: + type: boolean + tcp_no_delay: + type: boolean + tcp_quick_ack: + type: boolean + socket_path: + type: string + servicePort: + type: integer + maximum: 65535 + minimum: 0 target: type: object properties: diff --git a/charts/vald-benchmark-operator/job-values.schema.json b/charts/vald-benchmark-operator/job-values.schema.json index 40759dd01b..582302dfdf 100644 --- a/charts/vald-benchmark-operator/job-values.schema.json +++ b/charts/vald-benchmark-operator/job-values.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft-07/schema#", "title": "Values", "type": "object", "properties": { @@ -342,6 +342,24 @@ "description": "vector dimension", "minimum": 1 }, + "global_config": { + "type": "object", + "properties": { + "logging": { + "type": "object", + "properties": { + "format": { "type": "string", "enum": ["raw", "json"] }, + "level": { + "type": "string", + "enum": ["debug", "info", "warn", "error", "fatal"] + }, + "logger": { "type": "string", "enum": ["glg", "zap"] } + } + }, + "time_zone": { "type": "string" }, + "version": { "type": "string" } + } + }, "insert_config": { "type": "object", "description": "insert config", @@ -443,6 +461,734 @@ } } }, + "server_config": { + "type": "object", + "properties": { + "healths": { + "type": "object", + "properties": { + "liveness": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "liveness server enabled" + }, + "host": { + "type": "string", + "description": "liveness server host" + }, + "livenessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer", + "description": "liveness probe failure threshold" + }, + "httpGet": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "liveness probe path" + }, + "port": { + "type": "string", + "description": "liveness probe port" + }, + "scheme": { + "type": "string", + "description": "liveness probe scheme" + } + } + }, + "initialDelaySeconds": { + "type": "integer", + "description": "liveness probe initial delay seconds" + }, + "periodSeconds": { + "type": "integer", + "description": "liveness probe period seconds" + }, + "successThreshold": { + "type": "integer", + "description": "liveness probe success threshold" + }, + "timeoutSeconds": { + "type": "integer", + "description": "liveness probe timeout seconds" + } + } + }, + "port": { + "type": "integer", + "description": "liveness server port", + "maximum": 65535, + "minimum": 0 + }, + "server": { + "type": "object", + "properties": { + "http": { + "type": "object", + "properties": { + "handler_timeout": { + "type": "string", + "description": "liveness server handler timeout" + }, + "idle_timeout": { + "type": "string", + "description": "liveness server idle timeout" + }, + "read_header_timeout": { + "type": "string", + "description": "liveness server read header timeout" + }, + "read_timeout": { + "type": "string", + "description": "liveness server read timeout" + }, + "shutdown_duration": { + "type": "string", + "description": "liveness server shutdown duration" + }, + "write_timeout": { + "type": "string", + "description": "liveness server write timeout" + } + } + }, + "mode": { + "type": "string", + "description": "liveness server mode" + }, + "network": { + "type": "string", + "description": "mysql network", + "enum": [ + "tcp", + "tcp4", + "tcp6", + "udp", + "udp4", + "udp6", + "unix", + "unixgram", + "unixpacket" + ] + }, + "probe_wait_time": { + "type": "string", + "description": "liveness server probe wait time" + }, + "socket_option": { + "type": "object", + "properties": { + "ip_recover_destination_addr": { + "type": "boolean", + "description": "server listen socket option for ip_recover_destination_addr functionality" + }, + "ip_transparent": { + "type": "boolean", + "description": "server listen socket option for ip_transparent functionality" + }, + "reuse_addr": { + "type": "boolean", + "description": "server listen socket option for reuse_addr functionality" + }, + "reuse_port": { + "type": "boolean", + "description": "server listen socket option for reuse_port functionality" + }, + "tcp_cork": { + "type": "boolean", + "description": "server listen socket option for tcp_cork functionality" + }, + "tcp_defer_accept": { + "type": "boolean", + "description": "server listen socket option for tcp_defer_accept functionality" + }, + "tcp_fast_open": { + "type": "boolean", + "description": "server listen socket option for tcp_fast_open functionality" + }, + "tcp_no_delay": { + "type": "boolean", + "description": "server listen socket option for tcp_no_delay functionality" + }, + "tcp_quick_ack": { + "type": "boolean", + "description": "server listen socket option for tcp_quick_ack functionality" + } + } + }, + "socket_path": { + "type": "string", + "description": "mysql socket_path" + } + } + }, + "servicePort": { + "type": "integer", + "description": "liveness server service port", + "maximum": 65535, + "minimum": 0 + } + } + }, + "readiness": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "readiness server enabled" + }, + "host": { + "type": "string", + "description": "readiness server host" + }, + "port": { + "type": "integer", + "description": "readiness server port", + "maximum": 65535, + "minimum": 0 + }, + "readinessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer", + "description": "readiness probe failure threshold" + }, + "httpGet": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "readiness probe path" + }, + "port": { + "type": "string", + "description": "readiness probe port" + }, + "scheme": { + "type": "string", + "description": "readiness probe scheme" + } + } + }, + "initialDelaySeconds": { + "type": "integer", + "description": "readiness probe initial delay seconds" + }, + "periodSeconds": { + "type": "integer", + "description": "readiness probe period seconds" + }, + "successThreshold": { + "type": "integer", + "description": "readiness probe success threshold" + }, + "timeoutSeconds": { + "type": "integer", + "description": "readiness probe timeout seconds" + } + } + }, + "server": { + "type": "object", + "properties": { + "http": { + "type": "object", + "properties": { + "handler_timeout": { + "type": "string", + "description": "readiness server handler timeout" + }, + "idle_timeout": { + "type": "string", + "description": "readiness server idle timeout" + }, + "read_header_timeout": { + "type": "string", + "description": "readiness server read header timeout" + }, + "read_timeout": { + "type": "string", + "description": "readiness server read timeout" + }, + "shutdown_duration": { + "type": "string", + "description": "readiness server shutdown duration" + }, + "write_timeout": { + "type": "string", + "description": "readiness server write timeout" + } + } + }, + "mode": { + "type": "string", + "description": "readiness server mode" + }, + "network": { + "type": "string", + "description": "mysql network", + "enum": [ + "tcp", + "tcp4", + "tcp6", + "udp", + "udp4", + "udp6", + "unix", + "unixgram", + "unixpacket" + ] + }, + "probe_wait_time": { + "type": "string", + "description": "readiness server probe wait time" + }, + "socket_option": { + "type": "object", + "properties": { + "ip_recover_destination_addr": { + "type": "boolean", + "description": "server listen socket option for ip_recover_destination_addr functionality" + }, + "ip_transparent": { + "type": "boolean", + "description": "server listen socket option for ip_transparent functionality" + }, + "reuse_addr": { + "type": "boolean", + "description": "server listen socket option for reuse_addr functionality" + }, + "reuse_port": { + "type": "boolean", + "description": "server listen socket option for reuse_port functionality" + }, + "tcp_cork": { + "type": "boolean", + "description": "server listen socket option for tcp_cork functionality" + }, + "tcp_defer_accept": { + "type": "boolean", + "description": "server listen socket option for tcp_defer_accept functionality" + }, + "tcp_fast_open": { + "type": "boolean", + "description": "server listen socket option for tcp_fast_open functionality" + }, + "tcp_no_delay": { + "type": "boolean", + "description": "server listen socket option for tcp_no_delay functionality" + }, + "tcp_quick_ack": { + "type": "boolean", + "description": "server listen socket option for tcp_quick_ack functionality" + } + } + }, + "socket_path": { + "type": "string", + "description": "mysql socket_path" + } + } + }, + "servicePort": { + "type": "integer", + "description": "readiness server service port", + "maximum": 65535, + "minimum": 0 + } + } + }, + "startup": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "startup server enabled" + }, + "port": { + "type": "integer", + "description": "startup server port", + "maximum": 65535, + "minimum": 0 + }, + "startupProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer", + "description": "startup probe failure threshold" + }, + "httpGet": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "startup probe path" + }, + "port": { + "type": "string", + "description": "startup probe port" + }, + "scheme": { + "type": "string", + "description": "startup probe scheme" + } + } + }, + "initialDelaySeconds": { + "type": "integer", + "description": "startup probe initial delay seconds" + }, + "periodSeconds": { + "type": "integer", + "description": "startup probe period seconds" + }, + "successThreshold": { + "type": "integer", + "description": "startup probe success threshold" + }, + "timeoutSeconds": { + "type": "integer", + "description": "startup probe timeout seconds" + } + } + } + } + } + } + }, + "servers": { + "type": "object", + "properties": { + "grpc": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "gRPC server enabled" + }, + "host": { "type": "string", "description": "gRPC server host" }, + "port": { + "type": "integer", + "description": "gRPC server port", + "maximum": 65535, + "minimum": 0 + }, + "server": { + "type": "object", + "properties": { + "grpc": { + "type": "object", + "properties": { + "bidirectional_stream_concurrency": { + "type": "integer", + "description": "gRPC server bidirectional stream concurrency" + }, + "connection_timeout": { + "type": "string", + "description": "gRPC server connection timeout" + }, + "enable_reflection": { + "type": "boolean", + "description": "gRPC server reflection option" + }, + "header_table_size": { + "type": "integer", + "description": "gRPC server header table size" + }, + "initial_conn_window_size": { + "type": "integer", + "description": "gRPC server initial connection window size" + }, + "initial_window_size": { + "type": "integer", + "description": "gRPC server initial window size" + }, + "interceptors": { + "type": "array", + "description": "gRPC server interceptors", + "items": { + "type": "string", + "enum": [ + "RecoverInterceptor", + "AccessLogInterceptor", + "TraceInterceptor", + "MetricInterceptor" + ] + } + }, + "keepalive": { + "type": "object", + "properties": { + "max_conn_age": { + "type": "string", + "description": "gRPC server keep alive max connection age" + }, + "max_conn_age_grace": { + "type": "string", + "description": "gRPC server keep alive max connection age grace" + }, + "max_conn_idle": { + "type": "string", + "description": "gRPC server keep alive max connection idle" + }, + "min_time": { + "type": "string", + "description": "gRPC server keep alive min_time" + }, + "permit_without_stream": { + "type": "boolean", + "description": "gRPC server keep alive permit_without_stream" + }, + "time": { + "type": "string", + "description": "gRPC server keep alive time" + }, + "timeout": { + "type": "string", + "description": "gRPC server keep alive timeout" + } + } + }, + "max_header_list_size": { + "type": "integer", + "description": "gRPC server max header list size" + }, + "max_receive_message_size": { + "type": "integer", + "description": "gRPC server max receive message size" + }, + "max_send_message_size": { + "type": "integer", + "description": "gRPC server max send message size" + }, + "read_buffer_size": { + "type": "integer", + "description": "gRPC server read buffer size" + }, + "write_buffer_size": { + "type": "integer", + "description": "gRPC server write buffer size" + } + } + }, + "mode": { + "type": "string", + "description": "gRPC server server mode" + }, + "network": { + "type": "string", + "description": "mysql network", + "enum": [ + "tcp", + "tcp4", + "tcp6", + "udp", + "udp4", + "udp6", + "unix", + "unixgram", + "unixpacket" + ] + }, + "probe_wait_time": { + "type": "string", + "description": "gRPC server probe wait time" + }, + "restart": { + "type": "boolean", + "description": "gRPC server restart" + }, + "socket_option": { + "type": "object", + "properties": { + "ip_recover_destination_addr": { + "type": "boolean", + "description": "server listen socket option for ip_recover_destination_addr functionality" + }, + "ip_transparent": { + "type": "boolean", + "description": "server listen socket option for ip_transparent functionality" + }, + "reuse_addr": { + "type": "boolean", + "description": "server listen socket option for reuse_addr functionality" + }, + "reuse_port": { + "type": "boolean", + "description": "server listen socket option for reuse_port functionality" + }, + "tcp_cork": { + "type": "boolean", + "description": "server listen socket option for tcp_cork functionality" + }, + "tcp_defer_accept": { + "type": "boolean", + "description": "server listen socket option for tcp_defer_accept functionality" + }, + "tcp_fast_open": { + "type": "boolean", + "description": "server listen socket option for tcp_fast_open functionality" + }, + "tcp_no_delay": { + "type": "boolean", + "description": "server listen socket option for tcp_no_delay functionality" + }, + "tcp_quick_ack": { + "type": "boolean", + "description": "server listen socket option for tcp_quick_ack functionality" + } + } + }, + "socket_path": { + "type": "string", + "description": "mysql socket_path" + } + } + }, + "servicePort": { + "type": "integer", + "description": "gRPC server service port", + "maximum": 65535, + "minimum": 0 + } + } + }, + "rest": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "REST server enabled" + }, + "host": { "type": "string", "description": "REST server host" }, + "port": { + "type": "integer", + "description": "REST server port", + "maximum": 65535, + "minimum": 0 + }, + "server": { + "type": "object", + "properties": { + "http": { + "type": "object", + "properties": { + "handler_timeout": { + "type": "string", + "description": "REST server handler timeout" + }, + "idle_timeout": { + "type": "string", + "description": "REST server idle timeout" + }, + "read_header_timeout": { + "type": "string", + "description": "REST server read header timeout" + }, + "read_timeout": { + "type": "string", + "description": "REST server read timeout" + }, + "shutdown_duration": { + "type": "string", + "description": "REST server shutdown duration" + }, + "write_timeout": { + "type": "string", + "description": "REST server write timeout" + } + } + }, + "mode": { + "type": "string", + "description": "REST server server mode" + }, + "network": { + "type": "string", + "description": "mysql network", + "enum": [ + "tcp", + "tcp4", + "tcp6", + "udp", + "udp4", + "udp6", + "unix", + "unixgram", + "unixpacket" + ] + }, + "probe_wait_time": { + "type": "string", + "description": "REST server probe wait time" + }, + "socket_option": { + "type": "object", + "properties": { + "ip_recover_destination_addr": { + "type": "boolean", + "description": "server listen socket option for ip_recover_destination_addr functionality" + }, + "ip_transparent": { + "type": "boolean", + "description": "server listen socket option for ip_transparent functionality" + }, + "reuse_addr": { + "type": "boolean", + "description": "server listen socket option for reuse_addr functionality" + }, + "reuse_port": { + "type": "boolean", + "description": "server listen socket option for reuse_port functionality" + }, + "tcp_cork": { + "type": "boolean", + "description": "server listen socket option for tcp_cork functionality" + }, + "tcp_defer_accept": { + "type": "boolean", + "description": "server listen socket option for tcp_defer_accept functionality" + }, + "tcp_fast_open": { + "type": "boolean", + "description": "server listen socket option for tcp_fast_open functionality" + }, + "tcp_no_delay": { + "type": "boolean", + "description": "server listen socket option for tcp_no_delay functionality" + }, + "tcp_quick_ack": { + "type": "boolean", + "description": "server listen socket option for tcp_quick_ack functionality" + } + } + }, + "socket_path": { + "type": "string", + "description": "mysql socket_path" + } + } + }, + "servicePort": { + "type": "integer", + "description": "REST server service port", + "maximum": 65535, + "minimum": 0 + } + } + } + } + } + } + }, "target": { "type": "object", "description": "target cluster location", diff --git a/charts/vald-benchmark-operator/scenario-values.schema.json b/charts/vald-benchmark-operator/scenario-values.schema.json index c4aadae03c..7817e50c38 100644 --- a/charts/vald-benchmark-operator/scenario-values.schema.json +++ b/charts/vald-benchmark-operator/scenario-values.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft-07/schema#", "title": "Values", "type": "object", "properties": { diff --git a/charts/vald-benchmark-operator/schemas/job-values.yaml b/charts/vald-benchmark-operator/schemas/job-values.yaml index 021418e9f5..a795d321b6 100644 --- a/charts/vald-benchmark-operator/schemas/job-values.yaml +++ b/charts/vald-benchmark-operator/schemas/job-values.yaml @@ -19,9 +19,8 @@ target: # @schema {"name": "target.host", "type": "string", "minLength": 1} # target.host -- target cluster host - host: - "vald-lb-gateway.default.svc.cluster.local" - # @schema {"name": "target.port", "type": "integer", "minimum": 0, "maximum": 65535} + host: "vald-lb-gateway.default.svc.cluster.local" + # @schema {"name": "target.port", "type": "integer", "minimum": 0, "maximum": 65535} # target.port -- target cluster port port: 8081 # @schema {"name": "dataset", "type": "object", "required": ["name", "indexes", "group", "range"]} @@ -57,6 +56,18 @@ replica: 1 # @schema {"name": "repetition", "type": "integer", "minimum": 1} # repetition -- the number of repeat job repetition: 1 +# @schema {"name": "rules", "type": "array", "items": {"type": "string"}} +# rules -- executing rule +rules: [] +# @schema {"name": "rps", "type": "integer", "minimum": 0, "maximum": 65535} +# rps -- desired request per sec +rps: 1000 +# @schema {"name": "concurrency_limit", "type": "integer", "minimum": 0, "maximum": 65535} +# concurrency_limit -- concurrency_limit represents the goroutine limit count. It affects the job performance. +concurrency_limit: 200 +# @schema {"name": "ttl_seconds_after_finished", "type": "integer", "minimum": 0, "maximum": 65535} +# ttl_seconds_after_finished -- limits the lifetime of a Job that has finished execution. +ttl_seconds_after_finished: 10 # @schema {"name": "job_type", "type": "string", "enum": ["insert", "update", "upsert", "search", "remove", "getobject", "exists"]} # job_type -- job type name job_type: "search" @@ -117,7 +128,6 @@ search_config: # @schema {"name": "search_config.aggregation_algorithm", "type": "string", "enum": ["Unknown", "ConcurrentQueue", "SortSlice", "SortPoolSlice", "PairingHeap"]} # search_config.aggregation_algorithm -- search result aggregation algorithm aggregation_algorithm: "Unknown" - # @schema {"name": "remove_config", "type": "object"} # remove_config -- remove config remove_config: @@ -361,15 +371,449 @@ client_config: # @schema {"name": "client_config.tls.insecure_skip_verify", "type": "boolean"} # client_config.tls.insecure_skip_verify -- enable/disable skip SSL certificate verification insecure_skip_verify: false -# @schema {"name": "rules", "type": "array", "items": {"type": "string"}} -# rules -- executing rule -rules: [] -# @schema {"name": "rps", "type": "integer", "minimum": 0, "maximum": 65535} -# rps -- desired request per sec -rps: 1000 -# @schema {"name": "concurrency_limit", "type": "integer", "minimum": 0, "maximum": 65535} -# concurrency_limit -- concurrency_limit represents the goroutine limit count. It affects the job performance. -concurrency_limit: 200 -# @schema {"name": "ttl_seconds_after_finished", "type": "integer", "minimum": 0, "maximum": 65535} -# ttl_seconds_after_finished -- limits the lifetime of a Job that has finished execution. -ttl_seconds_after_finished: 10 +# @schema {"name": "global_config", "type": "object"} +global_config: + # @schema {"name": "global_config.version", "type": "string", "default": "v0.0.1"} + # version -- version info + version: "v0.0.1" + # @schema {"name": "global_config.time_zone", "type": "string"} + # time_zone -- Time zone + time_zone: UTC + # @schema {"name": "global_config.logging", "type": "object", "anchor": "logging"} + logging: + # @schema {"name": "global_config.logging.logger", "type": "string", "enum": ["glg", "zap"]} + # logging.logger -- logger name. + # currently logger must be `glg` or `zap`. + logger: glg + # @schema {"name": "global_config.logging.level", "type": "string", "enum": ["debug", "info", "warn", "error", "fatal"]} + # logging.level -- logging level. + # logging level must be `debug`, `info`, `warn`, `error` or `fatal`. + level: debug + # @schema {"name": "global_config.logging.format", "type": "string", "enum": ["raw", "json"]} + # logging.format -- logging format. + # logging format must be `raw` or `json` + format: raw +# @schema {"name": "server_config", "type": "object", "anchor": "server_config"} +server_config: + # @schema {"name": "server_config.servers", "type": "object"} + servers: + # @schema {"name": "server_config.servers.rest", "type": "object"} + rest: + # @schema {"name": "server_config.servers.rest.enabled", "type": "boolean"} + # server_config.servers.rest.enabled -- REST server enabled + enabled: false + # @schema {"name": "server_config.servers.rest.host", "type": "string"} + # server_config.servers.rest.host -- REST server host + host: 0.0.0.0 + # @schema {"name": "server_config.servers.rest.port", "type": "integer", "minimum": 0, "maximum": 65535} + # server_config.servers.rest.port -- REST server port + port: 8080 + # @schema {"name": "server_config.servers.rest.servicePort", "type": "integer", "minimum": 0, "maximum": 65535} + # server_config.servers.rest.servicePort -- REST server service port + servicePort: 8080 + # @schema {"name": "server_config.servers.rest.server", "type": "object"} + server: + # @schema {"name": "server_config.servers.rest.server.mode", "type": "string"} + # server_config.servers.rest.server.mode -- REST server server mode + mode: REST + # @schema {"name": "server_config.servers.rest.server.probe_wait_time", "type": "string"} + # server_config.servers.rest.server.probe_wait_time -- REST server probe wait time + probe_wait_time: 3s + # @schema {"name": "server_config.servers.rest.server.network", "type": "string", "enum": ["tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "unix", "unixgram", "unixpacket"]} + # server_config.servers.rest.server.network -- mysql network + network: tcp + # @schema {"name": "server_config.servers.rest.server.socket_path", "type": "string"} + # server_config.servers.rest.server.socket_path -- mysql socket_path + socket_path: "" + # @schema {"name": "server_config.servers.rest.server.http", "type": "object"} + http: + # @schema {"name": "server_config.servers.rest.server.http.shutdown_duration", "type": "string"} + # server_config.servers.rest.server.http.shutdown_duration -- REST server shutdown duration + shutdown_duration: 5s + # @schema {"name": "server_config.servers.rest.server.http.handler_timeout", "type": "string"} + # server_config.servers.rest.server.http.handler_timeout -- REST server handler timeout + handler_timeout: 5s + # @schema {"name": "server_config.servers.rest.server.http.idle_timeout", "type": "string"} + # server_config.servers.rest.server.http.idle_timeout -- REST server idle timeout + idle_timeout: 2s + # @schema {"name": "server_config.servers.rest.server.http.read_header_timeout", "type": "string"} + # server_config.servers.rest.server.http.read_header_timeout -- REST server read header timeout + read_header_timeout: 1s + # @schema {"name": "server_config.servers.rest.server.http.read_timeout", "type": "string"} + # server_config.servers.rest.server.http.read_timeout -- REST server read timeout + read_timeout: 1s + # @schema {"name": "server_config.servers.rest.server.http.write_timeout", "type": "string"} + # server_config.servers.rest.server.http.write_timeout -- REST server write timeout + write_timeout: 1s + # @schema {"name": "server_config.servers.rest.server.socket_option", "type": "object", "anchor": "socket_option"} + socket_option: + # @schema {"name": "server_config.servers.rest.server.socket_option.reuse_port", "type": "boolean"} + # server_config.servers.rest.server.socket_option.reuse_port -- server listen socket option for reuse_port functionality + reuse_port: true + # @schema {"name": "server_config.servers.rest.server.socket_option.reuse_addr", "type": "boolean"} + # server_config.servers.rest.server.socket_option.reuse_addr -- server listen socket option for reuse_addr functionality + reuse_addr: true + # @schema {"name": "server_config.servers.rest.server.socket_option.tcp_fast_open", "type": "boolean"} + # server_config.servers.rest.server.socket_option.tcp_fast_open -- server listen socket option for tcp_fast_open functionality + tcp_fast_open: true + # @schema {"name": "server_config.servers.rest.server.socket_option.tcp_no_delay", "type": "boolean"} + # server_config.servers.rest.server.socket_option.tcp_no_delay -- server listen socket option for tcp_no_delay functionality + tcp_no_delay: true + # @schema {"name": "server_config.servers.rest.server.socket_option.tcp_cork", "type": "boolean"} + # server_config.servers.rest.server.socket_option.tcp_cork -- server listen socket option for tcp_cork functionality + tcp_cork: false + # @schema {"name": "server_config.servers.rest.server.socket_option.tcp_quick_ack", "type": "boolean"} + # server_config.servers.rest.server.socket_option.tcp_quick_ack -- server listen socket option for tcp_quick_ack functionality + tcp_quick_ack: true + # @schema {"name": "server_config.servers.rest.server.socket_option.tcp_defer_accept", "type": "boolean"} + # server_config.servers.rest.server.socket_option.tcp_defer_accept -- server listen socket option for tcp_defer_accept functionality + tcp_defer_accept: true + # @schema {"name": "server_config.servers.rest.server.socket_option.ip_transparent", "type": "boolean"} + # server_config.servers.rest.server.socket_option.ip_transparent -- server listen socket option for ip_transparent functionality + ip_transparent: false + # @schema {"name": "server_config.servers.rest.server.socket_option.ip_recover_destination_addr", "type": "boolean"} + # server_config.servers.rest.server.socket_option.ip_recover_destination_addr -- server listen socket option for ip_recover_destination_addr functionality + ip_recover_destination_addr: false + # @schema {"name": "server_config.servers.grpc", "type": "object"} + grpc: + # @schema {"name": "server_config.servers.grpc.enabled", "type": "boolean"} + # server_config.servers.grpc.enabled -- gRPC server enabled + enabled: true + # @schema {"name": "server_config.servers.grpc.host", "type": "string"} + # server_config.servers.grpc.host -- gRPC server host + host: 0.0.0.0 + # @schema {"name": "server_config.servers.grpc.port", "type": "integer", "minimum": 0, "maximum": 65535} + # server_config.servers.grpc.port -- gRPC server port + port: 8081 + # @schema {"name": "server_config.servers.grpc.servicePort", "type": "integer", "minimum": 0, "maximum": 65535} + # server_config.servers.grpc.servicePort -- gRPC server service port + servicePort: 8081 + # @schema {"name": "server_config.servers.grpc.server", "type": "object"} + server: + # @schema {"name": "server_config.servers.grpc.server.mode", "type": "string"} + # server_config.servers.grpc.server.mode -- gRPC server server mode + mode: GRPC + # @schema {"name": "server_config.servers.grpc.server.probe_wait_time", "type": "string"} + # server_config.servers.grpc.server.probe_wait_time -- gRPC server probe wait time + probe_wait_time: "3s" + # @schema {"name": "server_config.servers.grpc.server.network", "type": "string", "enum": ["tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "unix", "unixgram", "unixpacket"]} + # server_config.servers.grpc.server.network -- mysql network + network: tcp + # @schema {"name": "server_config.servers.grpc.server.socket_path", "type": "string"} + # server_config.servers.grpc.server.socket_path -- mysql socket_path + socket_path: "" + # @schema {"name": "server_config.servers.grpc.server.grpc", "type": "object"} + grpc: + # @schema {"name": "server_config.servers.grpc.server.grpc.bidirectional_stream_concurrency", "type": "integer"} + # server_config.servers.grpc.server.grpc.bidirectional_stream_concurrency -- gRPC server bidirectional stream concurrency + bidirectional_stream_concurrency: 20 + # @schema {"name": "server_config.servers.grpc.server.grpc.max_receive_message_size", "type": "integer"} + # server_config.servers.grpc.server.grpc.max_receive_message_size -- gRPC server max receive message size + max_receive_message_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.max_send_message_size", "type": "integer"} + # server_config.servers.grpc.server.grpc.max_send_message_size -- gRPC server max send message size + max_send_message_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.initial_window_size", "type": "integer"} + # server_config.servers.grpc.server.grpc.initial_window_size -- gRPC server initial window size + initial_window_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.initial_conn_window_size", "type": "integer"} + # server_config.servers.grpc.server.grpc.initial_conn_window_size -- gRPC server initial connection window size + initial_conn_window_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive", "type": "object"} + keepalive: + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.max_conn_idle", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.max_conn_idle -- gRPC server keep alive max connection idle + max_conn_idle: "" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.max_conn_age", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.max_conn_age -- gRPC server keep alive max connection age + max_conn_age: "" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.max_conn_age_grace", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.max_conn_age_grace -- gRPC server keep alive max connection age grace + max_conn_age_grace: "" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.time", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.time -- gRPC server keep alive time + time: "120s" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.timeout", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.timeout -- gRPC server keep alive timeout + timeout: "30s" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.min_time", "type": "string"} + # server_config.servers.grpc.server.grpc.keepalive.min_time -- gRPC server keep alive min_time + min_time: "60s" + # @schema {"name": "server_config.servers.grpc.server.grpc.keepalive.permit_without_stream", "type": "boolean"} + # server_config.servers.grpc.server.grpc.keepalive.permit_without_stream -- gRPC server keep alive permit_without_stream + permit_without_stream: true + # @schema {"name": "server_config.servers.grpc.server.grpc.write_buffer_size", "type": "integer"} + # server_config.servers.grpc.server.grpc.write_buffer_size -- gRPC server write buffer size + write_buffer_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.read_buffer_size", "type": "integer"} + # server_config.servers.grpc.server.grpc.read_buffer_size -- gRPC server read buffer size + read_buffer_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.connection_timeout", "type": "string"} + # server_config.servers.grpc.server.grpc.connection_timeout -- gRPC server connection timeout + connection_timeout: "" + # @schema {"name": "server_config.servers.grpc.server.grpc.max_header_list_size", "type": "integer"} + # server_config.servers.grpc.server.grpc.max_header_list_size -- gRPC server max header list size + max_header_list_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.header_table_size", "type": "integer"} + # server_config.servers.grpc.server.grpc.header_table_size -- gRPC server header table size + header_table_size: 0 + # @schema {"name": "server_config.servers.grpc.server.grpc.interceptors", "type": "array", "items": {"type": "string", "enum": ["RecoverInterceptor", "AccessLogInterceptor", "TraceInterceptor", "MetricInterceptor"]}} + # server_config.servers.grpc.server.grpc.interceptors -- gRPC server interceptors + interceptors: + - "RecoverInterceptor" + # @schema {"name": "server_config.servers.grpc.server.grpc.enable_reflection", "type": "boolean"} + # server_config.servers.grpc.server.grpc.enable_reflection -- gRPC server reflection option + enable_reflection: true + # @schema {"name": "server_config.servers.grpc.server.socket_option", "alias": "socket_option"} + socket_option: + # server_config.servers.grpc.server.socket_option.reuse_port -- server listen socket option for reuse_port functionality + reuse_port: true + # server_config.servers.grpc.server.socket_option.reuse_addr -- server listen socket option for reuse_addr functionality + reuse_addr: true + # server_config.servers.grpc.server.socket_option.tcp_fast_open -- server listen socket option for tcp_fast_open functionality + tcp_fast_open: true + # server_config.servers.grpc.server.socket_option.tcp_no_delay -- server listen socket option for tcp_no_delay functionality + tcp_no_delay: true + # server_config.servers.grpc.server.socket_option.tcp_cork -- server listen socket option for tcp_cork functionality + tcp_cork: false + # server_config.servers.grpc.server.socket_option.tcp_quick_ack -- server listen socket option for tcp_quick_ack functionality + tcp_quick_ack: true + # server_config.servers.grpc.server.socket_option.tcp_defer_accept -- server listen socket option for tcp_defer_accept functionality + tcp_defer_accept: true + # server_config.servers.grpc.server.socket_option.ip_transparent -- server listen socket option for ip_transparent functionality + ip_transparent: false + # server_config.servers.grpc.server.socket_option.ip_recover_destination_addr -- server listen socket option for ip_recover_destination_addr functionality + ip_recover_destination_addr: false + # @schema {"name": "server_config.servers.grpc.server.restart", "type": "boolean"} + # server_config.servers.grpc.server.restart -- gRPC server restart + restart: true + # @schema {"name": "server_config.healths", "type": "object"} + healths: + # @schema {"name": "server_config.healths.startup", "type": "object"} + startup: + # @schema {"name": "server_config.healths.startup.enabled", "type": "boolean"} + # server_config.healths.startup.enabled -- startup server enabled + enabled: true + # @schema {"name": "server_config.healths.startup.port", "type": "integer", "minimum": 0, "maximum": 65535} + # server_config.healths.startup.port -- startup server port + port: 3000 + # @schema {"name": "server_config.healths.startup.startupProbe", "type": "object"} + startupProbe: + # @schema {"name": "server_config.healths.startup.startupProbe.httpGet", "type": "object"} + httpGet: + # @schema {"name": "server_config.healths.startup.startupProbe.httpGet.path", "type": "string"} + # server_config.healths.startup.startupProbe.httpGet.path -- startup probe path + path: /liveness + # @schema {"name": "server_config.healths.startup.startupProbe.httpGet.port", "type": "string"} + # server_config.healths.startup.startupProbe.httpGet.port -- startup probe port + port: liveness + # @schema {"name": "server_config.healths.startup.startupProbe.httpGet.scheme", "type": "string"} + # server_config.healths.startup.startupProbe.httpGet.scheme -- startup probe scheme + scheme: HTTP + # @schema {"name": "server_config.healths.startup.startupProbe.initialDelaySeconds", "type": "integer"} + # server_config.healths.startup.startupProbe.initialDelaySeconds -- startup probe initial delay seconds + initialDelaySeconds: 5 + # @schema {"name": "server_config.healths.startup.startupProbe.timeoutSeconds", "type": "integer"} + # server_config.healths.startup.startupProbe.timeoutSeconds -- startup probe timeout seconds + timeoutSeconds: 2 + # @schema {"name": "server_config.healths.startup.startupProbe.successThreshold", "type": "integer"} + # server_config.healths.startup.startupProbe.successThreshold -- startup probe success threshold + successThreshold: 1 + # @schema {"name": "server_config.healths.startup.startupProbe.failureThreshold", "type": "integer"} + # server_config.healths.startup.startupProbe.failureThreshold -- startup probe failure threshold + failureThreshold: 30 + # @schema {"name": "server_config.healths.startup.startupProbe.periodSeconds", "type": "integer"} + # server_config.healths.startup.startupProbe.periodSeconds -- startup probe period seconds + periodSeconds: 5 + # @schema {"name": "server_config.healths.liveness", "type": "object"} + liveness: + # @schema {"name": "server_config.healths.liveness.enabled", "type": "boolean"} + # server_config.healths.liveness.enabled -- liveness server enabled + enabled: true + # @schema {"name": "server_config.healths.liveness.host", "type": "string"} + # server_config.healths.liveness.host -- liveness server host + host: 0.0.0.0 + # @schema {"name": "server_config.healths.liveness.port", "type": "integer", "minimum": 0, "maximum": 65535} + # server_config.healths.liveness.port -- liveness server port + port: 3000 + # @schema {"name": "server_config.healths.liveness.servicePort", "type": "integer", "minimum": 0, "maximum": 65535} + # server_config.healths.liveness.servicePort -- liveness server service port + servicePort: 3000 + # @schema {"name": "server_config.healths.liveness.livenessProbe", "type": "object"} + livenessProbe: + # @schema {"name": "server_config.healths.liveness.livenessProbe.httpGet", "type": "object"} + httpGet: + # @schema {"name": "server_config.healths.liveness.livenessProbe.httpGet.path", "type": "string"} + # server_config.healths.liveness.livenessProbe.httpGet.path -- liveness probe path + path: /liveness + # @schema {"name": "server_config.healths.liveness.livenessProbe.httpGet.port", "type": "string"} + # server_config.healths.liveness.livenessProbe.httpGet.port -- liveness probe port + port: liveness + # @schema {"name": "server_config.healths.liveness.livenessProbe.httpGet.scheme", "type": "string"} + # server_config.healths.liveness.livenessProbe.httpGet.scheme -- liveness probe scheme + scheme: HTTP + # @schema {"name": "server_config.healths.liveness.livenessProbe.initialDelaySeconds", "type": "integer"} + # server_config.healths.liveness.livenessProbe.initialDelaySeconds -- liveness probe initial delay seconds + initialDelaySeconds: 5 + # @schema {"name": "server_config.healths.liveness.livenessProbe.timeoutSeconds", "type": "integer"} + # server_config.healths.liveness.livenessProbe.timeoutSeconds -- liveness probe timeout seconds + timeoutSeconds: 2 + # @schema {"name": "server_config.healths.liveness.livenessProbe.successThreshold", "type": "integer"} + # server_config.healths.liveness.livenessProbe.successThreshold -- liveness probe success threshold + successThreshold: 1 + # @schema {"name": "server_config.healths.liveness.livenessProbe.failureThreshold", "type": "integer"} + # server_config.healths.liveness.livenessProbe.failureThreshold -- liveness probe failure threshold + failureThreshold: 2 + # @schema {"name": "server_config.healths.liveness.livenessProbe.periodSeconds", "type": "integer"} + # server_config.healths.liveness.livenessProbe.periodSeconds -- liveness probe period seconds + periodSeconds: 3 + # @schema {"name": "server_config.healths.liveness.server", "type": "object"} + server: + # @schema {"name": "server_config.healths.liveness.server.mode", "type": "string"} + # server_config.healths.liveness.server.mode -- liveness server mode + mode: "" + # @schema {"name": "server_config.healths.liveness.server.probe_wait_time", "type": "string"} + # server_config.healths.liveness.server.probe_wait_time -- liveness server probe wait time + probe_wait_time: "3s" + # @schema {"name": "server_config.healths.liveness.server.network", "type": "string", "enum": ["tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "unix", "unixgram", "unixpacket"]} + # server_config.healths.liveness.server.network -- mysql network + network: tcp + # @schema {"name": "server_config.healths.liveness.server.socket_path", "type": "string"} + # server_config.healths.liveness.server.socket_path -- mysql socket_path + socket_path: "" + # @schema {"name": "server_config.healths.liveness.server.http", "type": "object"} + http: + # @schema {"name": "server_config.healths.liveness.server.http.shutdown_duration", "type": "string"} + # server_config.healths.liveness.server.http.shutdown_duration -- liveness server shutdown duration + shutdown_duration: "5s" + # @schema {"name": "server_config.healths.liveness.server.http.handler_timeout", "type": "string"} + # server_config.healths.liveness.server.http.handler_timeout -- liveness server handler timeout + handler_timeout: "" + # @schema {"name": "server_config.healths.liveness.server.http.idle_timeout", "type": "string"} + # server_config.healths.liveness.server.http.idle_timeout -- liveness server idle timeout + idle_timeout: "" + # @schema {"name": "server_config.healths.liveness.server.http.read_header_timeout", "type": "string"} + # server_config.healths.liveness.server.http.read_header_timeout -- liveness server read header timeout + read_header_timeout: "" + # @schema {"name": "server_config.healths.liveness.server.http.read_timeout", "type": "string"} + # server_config.healths.liveness.server.http.read_timeout -- liveness server read timeout + read_timeout: "" + # @schema {"name": "server_config.healths.liveness.server.http.write_timeout", "type": "string"} + # server_config.healths.liveness.server.http.write_timeout -- liveness server write timeout + write_timeout: "" + # @schema {"name": "server_config.healths.liveness.server.socket_option", "alias": "socket_option"} + socket_option: + # server_config.healths.liveness.server.socket_option.reuse_port -- server listen socket option for reuse_port functionality + reuse_port: true + # server_config.healths.liveness.server.socket_option.reuse_addr -- server listen socket option for reuse_addr functionality + reuse_addr: true + # server_config.healths.liveness.server.socket_option.tcp_fast_open -- server listen socket option for tcp_fast_open functionality + tcp_fast_open: true + # server_config.healths.liveness.server.socket_option.tcp_no_delay -- server listen socket option for tcp_no_delay functionality + tcp_no_delay: true + # server_config.healths.liveness.server.socket_option.tcp_cork -- server listen socket option for tcp_cork functionality + tcp_cork: false + # server_config.healths.liveness.server.socket_option.tcp_quick_ack -- server listen socket option for tcp_quick_ack functionality + tcp_quick_ack: true + # server_config.healths.liveness.server.socket_option.tcp_defer_accept -- server listen socket option for tcp_defer_accept functionality + tcp_defer_accept: true + # server_config.healths.liveness.server.socket_option.ip_transparent -- server listen socket option for ip_transparent functionality + ip_transparent: false + # server_config.healths.liveness.server.socket_option.ip_recover_destination_addr -- server listen socket option for ip_recover_destination_addr functionality + ip_recover_destination_addr: false + # @schema {"name": "server_config.healths.readiness", "type": "object"} + readiness: + # @schema {"name": "server_config.healths.readiness.enabled", "type": "boolean"} + # server_config.healths.readiness.enabled -- readiness server enabled + enabled: true + # @schema {"name": "server_config.healths.readiness.host", "type": "string"} + # server_config.healths.readiness.host -- readiness server host + host: 0.0.0.0 + # @schema {"name": "server_config.healths.readiness.port", "type": "integer", "minimum": 0, "maximum": 65535} + # server_config.healths.readiness.port -- readiness server port + port: 3001 + # @schema {"name": "server_config.healths.readiness.servicePort", "type": "integer", "minimum": 0, "maximum": 65535} + # server_config.healths.readiness.servicePort -- readiness server service port + servicePort: 3001 + # @schema {"name": "server_config.healths.readiness.readinessProbe", "type": "object"} + readinessProbe: + # @schema {"name": "server_config.healths.readiness.readinessProbe.httpGet", "type": "object"} + httpGet: + # @schema {"name": "server_config.healths.readiness.readinessProbe.httpGet.path", "type": "string"} + # server_config.healths.readiness.readinessProbe.httpGet.path -- readiness probe path + path: /readiness + # @schema {"name": "server_config.healths.readiness.readinessProbe.httpGet.port", "type": "string"} + # server_config.healths.readiness.readinessProbe.httpGet.port -- readiness probe port + port: readiness + # @schema {"name": "server_config.healths.readiness.readinessProbe.httpGet.scheme", "type": "string"} + # server_config.healths.readiness.readinessProbe.httpGet.scheme -- readiness probe scheme + scheme: HTTP + # @schema {"name": "server_config.healths.readiness.readinessProbe.initialDelaySeconds", "type": "integer"} + # server_config.healths.readiness.readinessProbe.initialDelaySeconds -- readiness probe initial delay seconds + initialDelaySeconds: 10 + # @schema {"name": "server_config.healths.readiness.readinessProbe.timeoutSeconds", "type": "integer"} + # server_config.healths.readiness.readinessProbe.timeoutSeconds -- readiness probe timeout seconds + timeoutSeconds: 2 + # @schema {"name": "server_config.healths.readiness.readinessProbe.successThreshold", "type": "integer"} + # server_config.healths.readiness.readinessProbe.successThreshold -- readiness probe success threshold + successThreshold: 1 + # @schema {"name": "server_config.healths.readiness.readinessProbe.failureThreshold", "type": "integer"} + # server_config.healths.readiness.readinessProbe.failureThreshold -- readiness probe failure threshold + failureThreshold: 2 + # @schema {"name": "server_config.healths.readiness.readinessProbe.periodSeconds", "type": "integer"} + # server_config.healths.readiness.readinessProbe.periodSeconds -- readiness probe period seconds + periodSeconds: 3 + # @schema {"name": "server_config.healths.readiness.server", "type": "object"} + server: + # @schema {"name": "server_config.healths.readiness.server.mode", "type": "string"} + # server_config.healths.readiness.server.mode -- readiness server mode + mode: "" + # @schema {"name": "server_config.healths.readiness.server.probe_wait_time", "type": "string"} + # server_config.healths.readiness.server.probe_wait_time -- readiness server probe wait time + probe_wait_time: "3s" + # @schema {"name": "server_config.healths.readiness.server.network", "type": "string", "enum": ["tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "unix", "unixgram", "unixpacket"]} + # server_config.healths.readiness.server.network -- mysql network + network: tcp + # @schema {"name": "server_config.healths.readiness.server.socket_path", "type": "string"} + # server_config.healths.readiness.server.socket_path -- mysql socket_path + socket_path: "" + # @schema {"name": "server_config.healths.readiness.server.http", "type": "object"} + http: + # @schema {"name": "server_config.healths.readiness.server.http.shutdown_duration", "type": "string"} + # server_config.healths.readiness.server.http.shutdown_duration -- readiness server shutdown duration + shutdown_duration: "0s" + # @schema {"name": "server_config.healths.readiness.server.http.handler_timeout", "type": "string"} + # server_config.healths.readiness.server.http.handler_timeout -- readiness server handler timeout + handler_timeout: "" + # @schema {"name": "server_config.healths.readiness.server.http.idle_timeout", "type": "string"} + # server_config.healths.readiness.server.http.idle_timeout -- readiness server idle timeout + idle_timeout: "" + # @schema {"name": "server_config.healths.readiness.server.http.read_header_timeout", "type": "string"} + # server_config.healths.readiness.server.http.read_header_timeout -- readiness server read header timeout + read_header_timeout: "" + # @schema {"name": "server_config.healths.readiness.server.http.read_timeout", "type": "string"} + # server_config.healths.readiness.server.http.read_timeout -- readiness server read timeout + read_timeout: "" + # @schema {"name": "server_config.healths.readiness.server.http.write_timeout", "type": "string"} + # server_config.healths.readiness.server.http.write_timeout -- readiness server write timeout + write_timeout: "" + # @schema {"name": "server_config.healths.readiness.server.socket_option", "alias": "socket_option"} + socket_option: + # server_config.healths.readiness.server.socket_option.reuse_port -- server listen socket option for reuse_port functionality + reuse_port: true + # server_config.healths.readiness.server.socket_option.reuse_addr -- server listen socket option for reuse_addr functionality + reuse_addr: true + # server_config.healths.readiness.server.socket_option.tcp_fast_oepn -- server listen socket option for tcp_fast_open functionality + tcp_fast_open: true + # server_config.healths.readiness.server.socket_option.tcp_no_delay -- server listen socket option for tcp_no_delay functionality + tcp_no_delay: true + # server_config.healths.readiness.server.socket_option.tcp_cork -- server listen socket option for tcp_cork functionality + tcp_cork: false + # server_config.healths.readiness.server.socket_option.tcp_quick_ack -- server listen socket option for tcp_quick_ack functionality + tcp_quick_ack: true + # server_config.healths.readiness.server.socket_option.tcp_defer_accept -- server listen socket option for tcp_defer_accept functionality + tcp_defer_accept: true + # server_config.healths.readiness.server.socket_option.ip_transparent -- server listen socket option for ip_transparent functionality + ip_transparent: false + # server_config.healths.readiness.server.socket_option.ip_recover_destination_addr -- server listen socket option for ip_recover_destination_addr functionality + ip_recover_destination_addr: false diff --git a/charts/vald-benchmark-operator/values.schema.json b/charts/vald-benchmark-operator/values.schema.json index 57fb5cd25b..8e4ded670d 100644 --- a/charts/vald-benchmark-operator/values.schema.json +++ b/charts/vald-benchmark-operator/values.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft-07/schema#", "title": "Values", "type": "object", "properties": { diff --git a/cmd/tools/benchmark/job/sample.yaml b/cmd/tools/benchmark/job/sample.yaml index b9ee53ab51..d49500d6b1 100644 --- a/cmd/tools/benchmark/job/sample.yaml +++ b/cmd/tools/benchmark/job/sample.yaml @@ -24,58 +24,121 @@ server_config: - name: grpc host: 0.0.0.0 port: 8081 + probe_wait_time: 3s + socket_path: "" + mode: GRPC grpc: bidirectional_stream_concurrency: 20 - connection_timeout: "" - header_table_size: 0 - initial_conn_window_size: 0 - initial_window_size: 0 - interceptors: [] + max_receive_message_size: 0 + max_send_message_size: 0 + initial_window_size: 1048576 + initial_conn_window_size: 2097152 keepalive: + max_conn_idle: "" max_conn_age: "" max_conn_age_grace: "" - max_conn_idle: "" - time: "" - timeout: "" - max_header_list_size: 0 - max_receive_message_size: 0 - max_send_message_size: 0 - read_buffer_size: 0 + time: "3h" + timeout: "60s" + min_time: "10m" + permit_without_stream: true write_buffer_size: 0 - mode: GRPC - probe_wait_time: 60s + read_buffer_size: 0 + connection_timeout: "" + max_header_list_size: 0 + header_table_size: 0 + interceptors: + - "RecoverInterceptor" + enable_reflection: true + socket_option: + reuse_port: true + reuse_addr: true + tcp_fast_open: false + tcp_no_delay: false + tcp_cork: false + tcp_quick_ack: false + tcp_defer_accept: false + ip_transparent: false + ip_recover_destination_addr: false restart: true health_check_servers: - name: liveness host: 0.0.0.0 port: 3000 + mode: "" + probe_wait_time: "3s" + network: tcp + socket_path: "" http: + shutdown_duration: "5s" handler_timeout: "" idle_timeout: "" read_header_timeout: "" read_timeout: "" - shutdown_duration: 5s write_timeout: "" - mode: "" - probe_wait_time: 60s + socket_option: + reuse_port: true + reuse_addr: true + tcp_fast_open: true + tcp_no_delay: true + tcp_cork: false + tcp_quick_ack: true + tcp_defer_accept: false + ip_transparent: false + ip_recover_destination_addr: false - name: readiness host: 0.0.0.0 port: 3001 + mode: "" + probe_wait_time: "3s" + network: tcp + socket_path: "" http: + shutdown_duration: "0s" handler_timeout: "" idle_timeout: "" read_header_timeout: "" read_timeout: "" - shutdown_duration: 0s write_timeout: "" - mode: "" - probe_wait_time: 60s + socket_option: + reuse_port: true + reuse_addr: true + tcp_fast_open: true + tcp_no_delay: true + tcp_cork: false + tcp_quick_ack: true + tcp_defer_accept: false + ip_transparent: false + ip_recover_destination_addr: false metrics_servers: + - name: pprof + host: 0.0.0.0 + port: 8081 + probe_wait_time: "3s" + socket_path: "" + mode: REST + network: tcp + http: + handler_timeout: "5s" + idle_timeout: "2s" + read_header_timeout: "1s" + read_timeout: "1s" + shutdown_duration: "5s" + write_timeout: "1m" + socket_option: + reuse_port: true + reuse_addr: true + tcp_fast_open: false + tcp_no_delay: false + tcp_cork: false + tcp_quick_ack: false + tcp_defer_accept: false + ip_transparent: false + ip_recover_destination_addr: false startup_strategy: - liveness - - grpc - readiness - full_shutdown_duration: 600s + - grpc + full_shutdown_duration: 30s tls: ca: /path/to/ca cert: /path/to/cert @@ -83,86 +146,115 @@ server_config: key: /path/to/key observability: enabled: false - collector: - duration: 5s - metrics: - enable_cgo: true - enable_goroutine: true - enable_memory: true - enable_version_info: true - version_info_labels: - - vald_version - - server_name - - git_commit - - build_time - - go_version - - go_os - - go_arch - - ngt_version + otlp: + collector_endpoint: "" + attribute: + namespace: _MY_POD_NAMESPACE_ + pod_name: _MY_POD_NAME_ + node_name: _MY_NODE_NAME_ + service_name: vald-benchmark + trace_batch_timeout: "1s" + trace_export_timeout: "1m" + trace_max_export_batch_size: 1024 + trace_max_queue_size: 256 + metrics: + enable_cgo: true + enable_goroutine: true + enable_memory: true + enable_version_info: true + version_info_labels: + - vald_version + - server_name + - git_commit + - build_time + - go_version + - go_os + - go_arch + - ngt_version trace: enabled: false - sampling_rate: 1 - prometheus: - enabled: false - endpoint: /metrics - namespace: vald - jaeger: - enabled: false - collector_endpoint: "" - agent_endpoint: "jaeger-agent.default.svc.cluster.local:6831" - username: "" - password: "" - service_name: "vald-benchmark-job" - buffer_max_count: 10 - stackdriver: - project_id: "" - client: - api_key: "" - audiences: [] - authentication_enabled: true - credentials_file: "" - credentials_json: "" - endpoint: "" - quota_project: "" - request_reason: "" - scopes: [] - telemetry_enabled: true - user_agent: "" - exporter: - bundle_count_threshold: 0 - bundle_delay_threshold: "0" - location: "" - metric_prefix: vald.vdaas.org - monitoring_enabled: false - number_of_workers: 1 - reporting_interval: 1m - skip_cmd: false - timeout: 5s - trace_spans_buffer_max_bytes: 0 - tracing_enabled: false - profiler: - enabled: false - service: "vald-benchmark-job" - service_version: "" - debug_logging: false - mutex_profiling: true - cpu_profiling: true - alloc_profiling: true - heap_profiling: true - goroutine_profiling: true - alloc_force_gc: false - api_addr: "" - instance: "" - zone: "" job: - job_type: "search" - dimension: 784 - iter: 100 - num: 10 - minNum: 10 - radius: -1 - epsilon: 0.1 - timeout: 5s - gateway_client: - addrs: - - vald-lb-gateway.default.svc.cluster.local:8081 + replica: 1 + repetition: 1 + before_job_name: "" + before_job_namespace: "" + rps: 200 + concurrency_limit: 200 + client_config: + health_check_duration: "1s" + connection_pool: + enable_dns_resolver: true + enable_rebalance: true + rebalance_duration: "30m" + size: 3 + old_conn_close_duration: "2m" + backoff: + initial_duration: "5ms" + backoff_time_limit: "5s" + maximum_duration: "5s" + jitter_limit: "100ms" + backoff_factor: 1.1 + retry_count: 100 + enable_error_log: true + circuit_breaker: + closed_error_rate: 0.7 + half_open_error_rate: 0.5 + min_samples: 1000 + open_timeout: "1s" + closed_refresh_timeout: "10s" + call_option: + wait_for_ready: true + max_retry_rpc_buffer_size: 0 + max_recv_msg_size: 0 + max_send_msg_size: 0 + dial_option: + write_buffer_size: 0 + read_buffer_size: 0 + initial_window_size: 1048576 + initial_connection_window_size: 2097152 + max_msg_size: 0 + backoff_max_delay: "120s" + backoff_base_delay: "1s" + backoff_multiplier: 1.6 + backoff_jitter: 0.2 + min_connection_timeout: "20s" + enable_backoff: false + insecure: true + timeout: "" + interceptors: [] + net: + dns: + cache_enabled: true + refresh_duration: "30m" + cache_expiration: "1h" + dialer: + timeout: "" + keepalive: "" + fallback_delay: "" + dual_stack_enabled: true + tls: + enabled: false + cert: /path/to/cert + key: /path/to/key + ca: /path/to/ca + insecure_skip_verify: false + socket_option: + reuse_port: true + reuse_addr: true + tcp_fast_open: false + tcp_no_delay: false + tcp_cork: false + tcp_quick_ack: false + tcp_defer_accept: false + ip_transparent: false + ip_recover_destination_addr: false + keepalive: + time: "120s" + timeout: "30s" + permit_without_stream: false + tls: + enabled: false + cert: /path/to/cert + key: /path/to/key + ca: /path/to/ca + insecure_skip_verify: false diff --git a/go.mod b/go.mod index 93a4e53c0d..47547ee8b1 100755 --- a/go.mod +++ b/go.mod @@ -386,7 +386,7 @@ require ( go.opentelemetry.io/otel/trace v1.11.2 go.uber.org/automaxprocs v0.0.0-00010101000000-000000000000 go.uber.org/goleak v1.2.1 - go.uber.org/ratelimit v0.2.0 + go.uber.org/ratelimit v0.3.0 go.uber.org/zap v1.24.0 gocloud.dev v0.0.0-00010101000000-000000000000 golang.org/x/net v0.17.0 @@ -396,7 +396,6 @@ require ( golang.org/x/text v0.13.0 golang.org/x/tools v0.14.0 golang.org/x/time v0.3.0 - golang.org/x/time v0.3.0 gonum.org/v1/hdf5 v0.0.0-00010101000000-000000000000 gonum.org/v1/plot v0.10.1 google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a @@ -421,7 +420,7 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect - github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect + github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/campoy/embedmd v1.0.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect diff --git a/go.sum b/go.sum index 81b12244a5..5bf82041d1 100644 --- a/go.sum +++ b/go.sum @@ -170,8 +170,6 @@ github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyR github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/stroke v0.0.0-20221221101821-bd29b49d73f0/go.mod h1:ccdDYaY5+gO+cbnQdFxEXqfy0RkoV25H3jLXUDNM3wg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -621,6 +619,8 @@ golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8= golang.org/x/exp/shiny v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= diff --git a/internal/config/config.go b/internal/config/config.go index 201b3d9c0b..27e9be31f8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -19,9 +19,11 @@ package config import ( "bytes" + "fmt" "io/fs" "os" "path/filepath" + "reflect" "github.com/vdaas/vald/internal/conv" "github.com/vdaas/vald/internal/encoding/json" @@ -131,3 +133,131 @@ func ToRawYaml(data interface{}) string { } return buf.String() } + +// Merge merges multiple objects to one object. +// the value of each field is prioritized the value of last index of `objs`. +// if the length of `objs` is zero, it returns initial value of type T. +func Merge[T any](objs ...T) (dst T, err error) { + switch len(objs) { + case 0: + return dst, nil + case 1: + dst = objs[0] + return dst, nil + default: + dst = objs[0] + visited := make(map[uintptr]bool) + rdst := reflect.ValueOf(&dst) + for _, src := range objs[1:] { + err = deepMerge(rdst, reflect.ValueOf(&src), visited, "") + if err != nil { + return dst, err + } + } + } + return dst, err +} + +func deepMerge(dst, src reflect.Value, visited map[uintptr]bool, fieldPath string) (err error) { + if !src.IsValid() || src.IsZero() { + return nil + } else if !dst.IsValid() { + dst = src + log.Info(dst.Type(), dst, src) + } + dType := dst.Type() + sType := src.Type() + if dType != sType { + return errors.ErrNotMatchFieldType(fieldPath, dType, sType) + } + sKind := src.Kind() + if sKind == reflect.Ptr { + src = src.Elem() + } + if sKind == reflect.Struct && src.CanAddr() { + addr := src.Addr().Pointer() + if visited[addr] { + return nil + } + if src.NumField() > 1 { + visited[addr] = true + } + } + switch dst.Kind() { + case reflect.Ptr: + if dst.IsNil() { + dst.Set(reflect.New(dst.Type().Elem())) + } + return deepMerge(dst.Elem(), src, visited, fieldPath) + case reflect.Struct: + dnum := dst.NumField() + snum := src.NumField() + if dnum != snum { + return errors.ErrNotMatchFieldNum(fieldPath, dnum, snum) + } + for i := 0; i < dnum; i++ { + dstField := dst.Field(i) + if dstField.CanSet() { + nf := fmt.Sprintf("%s.%s(%d)", fieldPath, dType.Field(i).Name, i) + if err = deepMerge(dstField, src.Field(i), visited, nf); err != nil { + return errors.ErrDeepMergeKind(dst.Kind().String(), nf, err) + } + } + } + case reflect.Slice: + srcLen := src.Len() + if srcLen > 0 { + if dst.IsNil() { + dst.Set(reflect.MakeSlice(dType, srcLen, srcLen)) + } else { + diffLen := srcLen - dst.Len() + if diffLen > 0 { + dst.Set(reflect.AppendSlice(dst, reflect.MakeSlice(dType, diffLen, diffLen))) + } + } + for i := 0; i < srcLen; i++ { + nf := fmt.Sprintf("%s[%d]", fieldPath, i) + if err = deepMerge(dst.Index(i), src.Index(i), visited, nf); err != nil { + return errors.ErrDeepMergeKind(dst.Kind().String(), nf, err) + } + } + } + case reflect.Array: + srcLen := src.Len() + if srcLen != dst.Len() { + return errors.ErrNotMatchArrayLength(fieldPath, dst.Len(), srcLen) + } + for i := 0; i < srcLen; i++ { + nf := fmt.Sprintf("%s[%d]", fieldPath, i) + if err = deepMerge(dst.Index(i), src.Index(i), visited, nf); err != nil { + return errors.ErrDeepMergeKind(dst.Kind().String(), nf, err) + } + } + case reflect.Map: + if dst.IsNil() { + dst.Set(reflect.MakeMapWithSize(dType, src.Len())) + } + dElem := dType.Elem() + for _, key := range src.MapKeys() { + vdst := dst.MapIndex(key) + // fmt.Println(vdst.IsValid(), key, vdst) + if !vdst.IsValid() { + vdst = reflect.New(dElem).Elem() + } + nf := fmt.Sprintf("%s[%s]", fieldPath, key) + if vdst.CanSet() { + if err = deepMerge(vdst, src.MapIndex(key), visited, nf); err != nil { + return errors.Errorf("error in array at %s: %w", nf, err) + } + dst.SetMapIndex(key, vdst) + } else { + dst.SetMapIndex(key, src.MapIndex(key)) + } + } + default: + if dst.CanSet() { + dst.Set(src) + } + } + return nil +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go index deb563cbc1..7f931915a9 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -18,6 +18,7 @@ package config import ( + "encoding/json" "io/fs" "os" "reflect" @@ -1431,4 +1432,460 @@ func TestToRawYaml(t *testing.T) { } } -// NOT IMPLEMENTED BELOW +func TestMerge(t *testing.T) { + type config struct { + // Config GlobalConfig + Discoverer *Discoverer + } + + type args struct { + objs []*config + } + type want struct { + wantDst *config + err error + } + type test struct { + name string + args args + want want + checkFunc func(want, *config, error) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, gotDst *config, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + if !reflect.DeepEqual(gotDst, w.wantDst) { + gb, _ := json.Marshal(gotDst) + wb, _ := json.Marshal(w.wantDst) + return errors.Errorf("got: \"%s\",\n\t\t\t\twant: \"%s\"", string(gb), string(wb)) + } + return nil + } + defaultBeforeFunc := func(t *testing.T, _ args) { + t.Helper() + } + defaultAfterFunc := func(t *testing.T, _ args) { + t.Helper() + } + + // dst + dst := &config{ + Discoverer: &Discoverer{ + Name: "dst", + Namespace: "dst", + DiscoveryDuration: "1m", + Net: &Net{ + DNS: &DNS{ + RefreshDuration: "2s", + CacheExpiration: "10s", + }, + Dialer: &Dialer{ + Timeout: "2s", + Keepalive: "1m", + FallbackDelay: "2s", + DualStackEnabled: true, + }, + SocketOption: &SocketOption{ + ReusePort: true, + ReuseAddr: true, + TCPFastOpen: false, + TCPNoDelay: true, + TCPCork: true, + TCPQuickAck: false, + TCPDeferAccept: true, + IPTransparent: true, + IPRecoverDestinationAddr: true, + }, + TLS: &TLS{ + Enabled: false, + Cert: "/path/to/cert", + Key: "/path/to/key", + CA: "/path/to/ca", + InsecureSkipVerify: false, + }, + }, + Selectors: &Selectors{ + Pod: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "dst", + "vald.vdaas.org/pod": "dst", + }, + Fields: map[string]string{ + "vald.vdaas.org": "dst", + "vald.vdaas.org/pod": "dst", + }, + }, + Node: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "dst", + "vald.vdaas.org/node": "dst", + }, + Fields: map[string]string{ + "vald.vdaas.org": "dst", + "vald.vdaas.org/node": "dst", + }, + }, + NodeMetrics: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "dst", + "vald.vdaas.org/node": "dst", + }, + Fields: map[string]string{ + "vald.vdaas.org": "dst", + "vald.vdaas.org/node": "dst", + }, + }, + PodMetrics: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "dst", + "vald.vdaas.org/pod": "dst", + }, + Fields: map[string]string{ + "vald.vdaas.org": "dst", + "vald.vdaas.org/pod": "dst", + }, + }, + }, + }, + } + // src + src := &config{ + Discoverer: &Discoverer{ + Name: "src", + Namespace: "src", + DiscoveryDuration: "10m", + Net: &Net{ + DNS: &DNS{ + RefreshDuration: "20s", + CacheExpiration: "1s", + }, + Dialer: &Dialer{ + Timeout: "20s", + Keepalive: "10m", + FallbackDelay: "20s", + DualStackEnabled: true, + }, + SocketOption: &SocketOption{ + TCPFastOpen: true, + }, + TLS: &TLS{ + Cert: "/path/to/cert", + Key: "/path/to/key", + CA: "/path/to/ca", + InsecureSkipVerify: false, + }, + }, + Selectors: &Selectors{ + Pod: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/pod": "src", + }, + Fields: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/pod": "src", + }, + }, + Node: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/src": "src", + }, + Fields: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/node": "src", + }, + }, + NodeMetrics: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/node": "src", + }, + Fields: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/node": "src", + }, + }, + PodMetrics: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/pod": "src", + }, + Fields: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/pod": "src", + }, + }, + }, + }, + } + w := &config{ + Discoverer: &Discoverer{ + Name: "src", + Namespace: "src", + DiscoveryDuration: "10m", + Net: &Net{ + DNS: &DNS{ + CacheEnabled: false, + RefreshDuration: "20s", + CacheExpiration: "1s", + }, + Dialer: &Dialer{ + Timeout: "20s", + Keepalive: "10m", + FallbackDelay: "20s", + DualStackEnabled: true, + }, + SocketOption: &SocketOption{ + ReusePort: true, + ReuseAddr: true, + TCPFastOpen: true, + TCPNoDelay: true, + TCPCork: true, + TCPQuickAck: false, + TCPDeferAccept: true, + IPTransparent: true, + IPRecoverDestinationAddr: true, + }, + TLS: &TLS{ + Enabled: false, + Cert: "/path/to/cert", + Key: "/path/to/key", + CA: "/path/to/ca", + InsecureSkipVerify: false, + }, + }, + Selectors: &Selectors{ + Pod: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/pod": "src", + }, + Fields: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/pod": "src", + }, + }, + Node: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/node": "dst", + "vald.vdaas.org/src": "src", + }, + Fields: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/node": "src", + }, + }, + NodeMetrics: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/node": "src", + }, + Fields: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/node": "src", + }, + }, + PodMetrics: &Selector{ + Labels: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/pod": "src", + }, + Fields: map[string]string{ + "vald.vdaas.org": "src", + "vald.vdaas.org/pod": "src", + }, + }, + }, + }, + } + + tests := []test{ + { + name: "return nil config when len(objs) is 0.", + args: args{ + objs: []*config{}, + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: defaultBeforeFunc, + afterFunc: defaultAfterFunc, + }, + { + name: "return dst config when len(objs) is 1.", + args: args{ + objs: []*config{ + dst, + }, + }, + want: want{ + wantDst: dst, + }, + checkFunc: defaultCheckFunc, + beforeFunc: defaultBeforeFunc, + afterFunc: defaultAfterFunc, + }, + { + name: "return merged config when len(objs) is 2.", + args: args{ + objs: []*config{ + dst, + src, + }, + }, + want: want{ + wantDst: w, + }, + checkFunc: defaultCheckFunc, + beforeFunc: defaultBeforeFunc, + afterFunc: defaultAfterFunc, + }, + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + gotDst, err := Merge(test.args.objs...) + t.Log("err: \t", err, "\n\t", gotDst, "\n\t", test.want.wantDst) + if err := checkFunc(test.want, gotDst, err); err != nil { + tt.Errorf("error: \n\t\t\t\t%v", err) + } + }) + } +} + +func Test_deepMerge(t *testing.T) { + type config struct { + Slice []int + GlobalConfig + } + type args struct { + dst reflect.Value + src reflect.Value + visited map[uintptr]bool + fieldPath string + } + type want struct { + err error + } + type test struct { + name string + args args + want want + checkFunc func(want, error) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + return nil + } + defaultBeforeFunc := func(t *testing.T, _ args) { + t.Helper() + } + defaultAfterFunc := func(t *testing.T, _ args) { + t.Helper() + } + tests := []test{ + func() test { + dst := &config{ + GlobalConfig: GlobalConfig{ + Version: "v0.0.1", + TZ: "UTC", + Logging: &Logging{ + Logger: "glg", + Level: "debug", + Format: "raw", + }, + }, + } + src := &config{ + GlobalConfig: GlobalConfig{ + Version: "v1.0.1", + TZ: "JST", + Logging: &Logging{ + Logger: "glg", + Format: "json", + }, + }, + } + visited := make(map[uintptr]bool) + return test{ + name: "success merge struct by src", + args: args{ + dst: reflect.ValueOf(dst), + src: reflect.ValueOf(src), + visited: visited, + fieldPath: "", + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: defaultBeforeFunc, + afterFunc: defaultAfterFunc, + } + }(), + func() test { + dst := &config{ + Slice: []int{1, 2, 3}, + } + src := &config{ + Slice: []int{4, 5}, + } + visited := make(map[uintptr]bool) + return test{ + name: "success merge struct by slice", + args: args{ + dst: reflect.ValueOf(dst), + src: reflect.ValueOf(src), + visited: visited, + fieldPath: "", + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: defaultBeforeFunc, + afterFunc: defaultAfterFunc, + } + }(), + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + err := deepMerge(test.args.dst, test.args.src, test.args.visited, test.args.fieldPath) + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} diff --git a/internal/errors/config.go b/internal/errors/config.go index 5908c3cda0..d2b3f16fa7 100644 --- a/internal/errors/config.go +++ b/internal/errors/config.go @@ -17,10 +17,28 @@ // Package errors provides error types and function package errors +import "reflect" + var ( ErrInvalidConfig = New("component config is invalid") ErrUnsupportedConfigFileType = func(ext string) error { return Errorf("unsupported file type: %s", ext) } + + ErrNotMatchFieldType = func(path string, dType, sType reflect.Type) error { + return Errorf("types do not match at %s: %v vs %v", path, dType, sType) + } + + ErrNotMatchFieldNum = func(path string, dNum, sNum int) error { + return Errorf("number of fields do not match at %s, dst: %d, src: %d", path, dNum, sNum) + } + + ErrNotMatchArrayLength = func(path string, dLen, sLen int) error { + return Errorf("array length do not match at %s, dst: %d, src: %d", path, dLen, sLen) + } + + ErrDeepMergeKind = func(kind string, nf string, err error) error { + return Errorf("error in %s at %s: %w", kind, nf, err) + } ) diff --git a/internal/k8s/client/client.go b/internal/k8s/client/client.go index 057932a200..62ee649f03 100644 --- a/internal/k8s/client/client.go +++ b/internal/k8s/client/client.go @@ -53,8 +53,8 @@ type Client interface { } type client struct { - scheme *runtime.Scheme - reader cli.Reader + scheme *runtime.Scheme `json:"scheme,omitempty" yaml:"scheme"` + reader cli.Reader `json:"reader,omitempty" yaml:"reader"` } func New(opts ...Option) (Client, error) { diff --git a/internal/k8s/vald/benchmark/api/v1/job_types.go b/internal/k8s/vald/benchmark/api/v1/job_types.go index 729bf27629..9b8bbf1f0b 100644 --- a/internal/k8s/vald/benchmark/api/v1/job_types.go +++ b/internal/k8s/vald/benchmark/api/v1/job_types.go @@ -23,23 +23,25 @@ import ( ) type BenchmarkJobSpec struct { - Target *BenchmarkTarget `json:"target,omitempty"` - Dataset *BenchmarkDataset `json:"dataset,omitempty"` - Dimension int `json:"dimension,omitempty"` - Replica int `json:"replica,omitempty"` - Repetition int `json:"repetition,omitempty"` - JobType string `json:"job_type,omitempty"` - InsertConfig *config.InsertConfig `json:"insert_config,omitempty"` - UpdateConfig *config.UpdateConfig `json:"update_config,omitempty"` - UpsertConfig *config.UpsertConfig `json:"upsert_config,omitempty"` - SearchConfig *config.SearchConfig `json:"search_config,omitempty"` - RemoveConfig *config.RemoveConfig `json:"remove_config,omitempty"` - ObjectConfig *config.ObjectConfig `json:"object_config,omitempty"` - ClientConfig *config.GRPCClient `json:"client_config,omitempty"` - Rules []*config.BenchmarkJobRule `json:"rules,omitempty"` - RPS int `json:"rps,omitempty"` - ConcurrencyLimit int `json:"concurrency_limit,omitempty"` - TTLSecondsAfterFinished int `json:"ttl_seconds_after_finished,omitempty"` + *config.GlobalConfig `json:",omitempty" yaml:""` + ServerConfig *config.Servers `json:"server_config,omitempty" yaml:"server_config"` + Target *BenchmarkTarget `json:"target,omitempty" yaml:"target"` + Dataset *BenchmarkDataset `json:"dataset,omitempty" yaml:"dataset"` + Dimension int `json:"dimension,omitempty" yaml:"dimension"` + Replica int `json:"replica,omitempty" yaml:"replica"` + Repetition int `json:"repetition,omitempty" yaml:"repetition"` + JobType string `json:"job_type,omitempty" yaml:"job_type"` + InsertConfig *config.InsertConfig `json:"insert_config,omitempty" yaml:"insert_config"` + UpdateConfig *config.UpdateConfig `json:"update_config,omitempty" yaml:"update_config"` + UpsertConfig *config.UpsertConfig `json:"upsert_config,omitempty" yaml:"upsert_config"` + SearchConfig *config.SearchConfig `json:"search_config,omitempty" yaml:"search_config"` + RemoveConfig *config.RemoveConfig `json:"remove_config,omitempty" yaml:"remove_config"` + ObjectConfig *config.ObjectConfig `json:"object_config,omitempty" yaml:"object_config"` + ClientConfig *config.GRPCClient `json:"client_config,omitempty" yaml:"client_config"` + Rules []*config.BenchmarkJobRule `json:"rules,omitempty" yaml:"rules"` + RPS int `json:"rps,omitempty" yaml:"rps"` + ConcurrencyLimit int `json:"concurrency_limit,omitempty" yaml:"concurrency_limit"` + TTLSecondsAfterFinished int `json:"ttl_seconds_after_finished,omitempty" yaml:"ttl_seconds_after_finished"` } type BenchmarkJobStatus string diff --git a/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml b/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml index d85ea0c12b..f5669958ee 100644 --- a/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml +++ b/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml @@ -280,6 +280,34 @@ spec: dimension: type: integer minimum: 1 + global_config: + type: object + properties: + logging: + type: object + properties: + format: + type: string + enum: + - raw + - json + level: + type: string + enum: + - debug + - info + - warn + - error + - fatal + logger: + type: string + enum: + - glg + - zap + time_zone: + type: string + version: + type: string insert_config: type: object properties: @@ -349,6 +377,416 @@ spec: type: number timeout: type: string + server_config: + type: object + properties: + healths: + type: object + properties: + liveness: + type: object + properties: + enabled: + type: boolean + host: + type: string + livenessProbe: + type: object + properties: + failureThreshold: + type: integer + httpGet: + type: object + properties: + path: + type: string + port: + type: string + scheme: + type: string + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + port: + type: integer + maximum: 65535 + minimum: 0 + server: + type: object + properties: + http: + type: object + properties: + handler_timeout: + type: string + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + write_timeout: + type: string + mode: + type: string + network: + type: string + enum: + - tcp + - tcp4 + - tcp6 + - udp + - udp4 + - udp6 + - unix + - unixgram + - unixpacket + probe_wait_time: + type: string + socket_option: + type: object + properties: + ip_recover_destination_addr: + type: boolean + ip_transparent: + type: boolean + reuse_addr: + type: boolean + reuse_port: + type: boolean + tcp_cork: + type: boolean + tcp_defer_accept: + type: boolean + tcp_fast_open: + type: boolean + tcp_no_delay: + type: boolean + tcp_quick_ack: + type: boolean + socket_path: + type: string + servicePort: + type: integer + maximum: 65535 + minimum: 0 + readiness: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: integer + maximum: 65535 + minimum: 0 + readinessProbe: + type: object + properties: + failureThreshold: + type: integer + httpGet: + type: object + properties: + path: + type: string + port: + type: string + scheme: + type: string + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + server: + type: object + properties: + http: + type: object + properties: + handler_timeout: + type: string + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + write_timeout: + type: string + mode: + type: string + network: + type: string + enum: + - tcp + - tcp4 + - tcp6 + - udp + - udp4 + - udp6 + - unix + - unixgram + - unixpacket + probe_wait_time: + type: string + socket_option: + type: object + properties: + ip_recover_destination_addr: + type: boolean + ip_transparent: + type: boolean + reuse_addr: + type: boolean + reuse_port: + type: boolean + tcp_cork: + type: boolean + tcp_defer_accept: + type: boolean + tcp_fast_open: + type: boolean + tcp_no_delay: + type: boolean + tcp_quick_ack: + type: boolean + socket_path: + type: string + servicePort: + type: integer + maximum: 65535 + minimum: 0 + startup: + type: object + properties: + enabled: + type: boolean + port: + type: integer + maximum: 65535 + minimum: 0 + startupProbe: + type: object + properties: + failureThreshold: + type: integer + httpGet: + type: object + properties: + path: + type: string + port: + type: string + scheme: + type: string + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + successThreshold: + type: integer + timeoutSeconds: + type: integer + servers: + type: object + properties: + grpc: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: integer + maximum: 65535 + minimum: 0 + server: + type: object + properties: + grpc: + type: object + properties: + bidirectional_stream_concurrency: + type: integer + connection_timeout: + type: string + enable_reflection: + type: boolean + header_table_size: + type: integer + initial_conn_window_size: + type: integer + initial_window_size: + type: integer + interceptors: + type: array + items: + type: string + enum: + - RecoverInterceptor + - AccessLogInterceptor + - TraceInterceptor + - MetricInterceptor + keepalive: + type: object + properties: + max_conn_age: + type: string + max_conn_age_grace: + type: string + max_conn_idle: + type: string + min_time: + type: string + permit_without_stream: + type: boolean + time: + type: string + timeout: + type: string + max_header_list_size: + type: integer + max_receive_message_size: + type: integer + max_send_message_size: + type: integer + read_buffer_size: + type: integer + write_buffer_size: + type: integer + mode: + type: string + network: + type: string + enum: + - tcp + - tcp4 + - tcp6 + - udp + - udp4 + - udp6 + - unix + - unixgram + - unixpacket + probe_wait_time: + type: string + restart: + type: boolean + socket_option: + type: object + properties: + ip_recover_destination_addr: + type: boolean + ip_transparent: + type: boolean + reuse_addr: + type: boolean + reuse_port: + type: boolean + tcp_cork: + type: boolean + tcp_defer_accept: + type: boolean + tcp_fast_open: + type: boolean + tcp_no_delay: + type: boolean + tcp_quick_ack: + type: boolean + socket_path: + type: string + servicePort: + type: integer + maximum: 65535 + minimum: 0 + rest: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: integer + maximum: 65535 + minimum: 0 + server: + type: object + properties: + http: + type: object + properties: + handler_timeout: + type: string + idle_timeout: + type: string + read_header_timeout: + type: string + read_timeout: + type: string + shutdown_duration: + type: string + write_timeout: + type: string + mode: + type: string + network: + type: string + enum: + - tcp + - tcp4 + - tcp6 + - udp + - udp4 + - udp6 + - unix + - unixgram + - unixpacket + probe_wait_time: + type: string + socket_option: + type: object + properties: + ip_recover_destination_addr: + type: boolean + ip_transparent: + type: boolean + reuse_addr: + type: boolean + reuse_port: + type: boolean + tcp_cork: + type: boolean + tcp_defer_accept: + type: boolean + tcp_fast_open: + type: boolean + tcp_no_delay: + type: boolean + tcp_quick_ack: + type: boolean + socket_path: + type: string + servicePort: + type: integer + maximum: 65535 + minimum: 0 target: type: object properties: diff --git a/pkg/tools/benchmark/job/config/config.go b/pkg/tools/benchmark/job/config/config.go index 3b1492e25d..afab9a9392 100644 --- a/pkg/tools/benchmark/job/config/config.go +++ b/pkg/tools/benchmark/job/config/config.go @@ -19,6 +19,7 @@ package config import ( "context" + "encoding/json" "os" "github.com/vdaas/vald/internal/config" @@ -45,7 +46,7 @@ type Config struct { Job *config.BenchmarkJob `json:"job" yaml:"job"` // K8sClient represents kubernetes clients - K8sClient client.Client + K8sClient client.Client `json:"k8s_client" yaml:"k8s_client"` } var ( @@ -61,7 +62,6 @@ func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { if err != nil { return nil, err } - if cfg != nil { cfg.Bind() } @@ -102,93 +102,184 @@ func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { if err != nil { log.Warn(err.Error()) } else { - cfg.Job.Target = (*config.BenchmarkTarget)(jobResource.Spec.Target) - cfg.Job.Dataset = (*config.BenchmarkDataset)(jobResource.Spec.Dataset) - cfg.Job.Dimension = jobResource.Spec.Dimension - cfg.Job.Replica = jobResource.Spec.Replica - cfg.Job.Repetition = jobResource.Spec.Repetition - cfg.Job.JobType = jobResource.Spec.JobType - cfg.Job.Rules = jobResource.Spec.Rules - cfg.Job.InsertConfig = jobResource.Spec.InsertConfig - cfg.Job.UpdateConfig = jobResource.Spec.UpdateConfig - cfg.Job.UpsertConfig = jobResource.Spec.UpsertConfig - cfg.Job.SearchConfig = jobResource.Spec.SearchConfig - cfg.Job.RemoveConfig = jobResource.Spec.RemoveConfig - cfg.Job.ObjectConfig = jobResource.Spec.ObjectConfig - cfg.Job.ClientConfig = jobResource.Spec.ClientConfig - cfg.Job.RPS = jobResource.Spec.RPS - cfg.Job.ConcurrencyLimit = jobResource.Spec.ConcurrencyLimit + // create override Config + overrideCfg := new(Config) + if jobResource.Spec.GlobalConfig != nil { + overrideCfg.GlobalConfig = *jobResource.Spec.Bind() + } + if jobResource.Spec.ServerConfig != nil { + overrideCfg.Server = (*jobResource.Spec.ServerConfig).Bind() + } + // jobResource.Spec has another field comparering Config.Job, so json.Marshal and Unmarshal are used for embedding field value of Config.Job from jobResource.Spec + var overrideJobCfg config.BenchmarkJob + b, err := json.Marshal(*jobResource.Spec.DeepCopy()) + if err == nil { + err = json.Unmarshal([]byte(b), &overrideJobCfg) + if err != nil { + log.Warn(err.Error()) + } + overrideCfg.Job = overrideJobCfg.Bind() + } if annotations := jobResource.GetAnnotations(); annotations != nil { - cfg.Job.BeforeJobName = annotations[JOBNAME_ANNOTATION] - cfg.Job.BeforeJobNamespace = annotations[JOBNAMESPACE_ANNOTATION] + overrideCfg.Job.BeforeJobName = annotations[JOBNAME_ANNOTATION] + overrideCfg.Job.BeforeJobNamespace = annotations[JOBNAMESPACE_ANNOTATION] } + return config.Merge(cfg, overrideCfg) } - return cfg, nil } // func FakeData() { // d := Config{ -// Version: "v0.0.1", +// GlobalConfig: config.GlobalConfig{ +// Version: "v0.0.1", +// TZ: "JST", +// Logging: &config.Logging{ +// Format: "raw", +// Level: "debug", +// Logger: "glg", +// }, +// }, // Server: &config.Servers{ // Servers: []*config.Server{ // { -// Name: "agent-rest", -// Host: "127.0.0.1", -// Port: 8080, -// Mode: "REST", +// Name: "grpc", +// Host: "0.0.0.0", +// Port: 8081, +// Mode: "GRPC", // ProbeWaitTime: "3s", +// SocketPath: "", +// GRPC: &config.GRPC{ +// BidirectionalStreamConcurrency: 20, +// MaxReceiveMessageSize: 0, +// MaxSendMessageSize: 0, +// InitialWindowSize: 1048576, +// InitialConnWindowSize: 2097152, +// Keepalive: &config.GRPCKeepalive{ +// MaxConnIdle: "", +// MaxConnAge: "", +// MaxConnAgeGrace: "", +// Time: "3h", +// Timeout: "60s", +// MinTime: "10m", +// PermitWithoutStream: true, +// }, +// WriteBufferSize: 0, +// ReadBufferSize: 0, +// ConnectionTimeout: "", +// MaxHeaderListSize: 0, +// HeaderTableSize: 0, +// Interceptors: []string{ +// "RecoverInterceptor", +// }, +// EnableReflection: true, +// }, +// SocketOption: &config.SocketOption{ +// ReusePort: true, +// ReuseAddr: true, +// TCPFastOpen: false, +// TCPNoDelay: false, +// TCPCork: false, +// TCPQuickAck: false, +// TCPDeferAccept: false, +// IPTransparent: false, +// IPRecoverDestinationAddr: false, +// }, +// Restart: true, +// }, +// }, +// HealthCheckServers: []*config.Server{ +// { +// Name: "livenesss", +// Host: "0.0.0.0", +// Port: 3000, +// Mode: "", +// Network: "tcp", +// ProbeWaitTime: "3s", +// SocketPath: "", // HTTP: &config.HTTP{ +// HandlerTimeout: "", +// IdleTimeout: "", +// ReadHeaderTimeout: "", +// ReadTimeout: "", // ShutdownDuration: "5s", -// HandlerTimeout: "5s", -// IdleTimeout: "2s", -// ReadHeaderTimeout: "1s", -// ReadTimeout: "1s", -// WriteTimeout: "1s", +// WriteTimeout: "", +// }, +// SocketOption: &config.SocketOption{ +// ReusePort: true, +// ReuseAddr: true, +// TCPFastOpen: true, +// TCPNoDelay: true, +// TCPCork: false, +// TCPQuickAck: true, +// TCPDeferAccept: false, +// IPTransparent: false, +// IPRecoverDestinationAddr: false, // }, // }, // { -// Name: "agent-grpc", -// Host: "127.0.0.1", -// Port: 8082, -// Mode: "GRPC", +// Name: "readiness", +// Host: "0.0.0.0", +// Port: 3001, +// Mode: "", +// Network: "tcp", +// ProbeWaitTime: "3s", +// SocketPath: "", +// HTTP: &config.HTTP{ +// HandlerTimeout: "", +// IdleTimeout: "", +// ReadHeaderTimeout: "", +// ReadTimeout: "", +// ShutdownDuration: "0s", +// WriteTimeout: "", +// }, +// SocketOption: &config.SocketOption{ +// ReusePort: true, +// ReuseAddr: true, +// TCPFastOpen: true, +// TCPNoDelay: true, +// TCPCork: false, +// TCPQuickAck: true, +// TCPDeferAccept: false, +// IPTransparent: false, +// IPRecoverDestinationAddr: false, +// }, // }, // }, // MetricsServers: []*config.Server{ // { // Name: "pprof", -// Host: "127.0.0.1", +// Host: "0.0.0.0", // Port: 6060, // Mode: "REST", +// Network: "tcp", // ProbeWaitTime: "3s", +// SocketPath: "", // HTTP: &config.HTTP{ -// ShutdownDuration: "5s", // HandlerTimeout: "5s", // IdleTimeout: "2s", // ReadHeaderTimeout: "1s", // ReadTimeout: "1s", -// WriteTimeout: "1s", +// ShutdownDuration: "5s", +// WriteTimeout: "1m", +// }, +// SocketOption: &config.SocketOption{ +// ReusePort: true, +// ReuseAddr: true, +// TCPFastOpen: false, +// TCPNoDelay: false, +// TCPCork: false, +// TCPQuickAck: false, +// TCPDeferAccept: false, +// IPTransparent: false, +// IPRecoverDestinationAddr: false, // }, -// }, -// }, -// HealthCheckServers: []*config.Server{ -// { -// Name: "livenesss", -// Host: "127.0.0.1", -// Port: 3000, -// }, -// { -// Name: "readiness", -// Host: "127.0.0.1", -// Port: 3001, // }, // }, // StartUpStrategy: []string{ // "livenesss", -// "pprof", -// "agent-grpc", -// "agent-rest", // "readiness", +// "pprof", // }, // ShutdownStrategy: []string{ // "readiness", @@ -205,6 +296,42 @@ func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { // CA: "/path/to/ca", // }, // }, +// Observability: &config.Observability{ +// Enabled: true, +// OTLP: &config.OTLP{ +// CollectorEndpoint: "", +// Attribute: &config.OTLPAttribute{ +// Namespace: NAMESPACE, +// PodName: NAME, +// NodeName: "", +// ServiceName: "vald", +// }, +// TraceBatchTimeout: "1s", +// TraceExportTimeout: "1m", +// TraceMaxExportBatchSize: 1024, +// TraceMaxQueueSize: 256, +// MetricsExportInterval: "1s", +// MetricsExportTimeout: "1m", +// }, +// Metrics: &config.Metrics{ +// EnableVersionInfo: true, +// EnableMemory: true, +// EnableGoroutine: true, +// EnableCGO: true, +// VersionInfoLabels: []string{ +// "vald_version", +// "server_name", +// "git_commit", +// "build_time", +// "go_version", +// "go_arch", +// "ngt_version", +// }, +// }, +// Trace: &config.Trace{ +// Enabled: true, +// }, +// }, // Job: &config.BenchmarkJob{ // Target: &config.BenchmarkTarget{ // Host: "vald-lb-gateway.svc.local", @@ -262,15 +389,15 @@ func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { // DialOption: &config.DialOption{ // WriteBufferSize: 0, // ReadBufferSize: 0, -// InitialWindowSize: 0, -// InitialConnectionWindowSize: 0, +// InitialWindowSize: 1048576, +// InitialConnectionWindowSize: 2097152, // MaxMsgSize: 0, // BackoffMaxDelay: "120s", // BackoffBaseDelay: "1s", // BackoffJitter: 0.2, // BackoffMultiplier: 1.6, // MinimumConnectionTimeout: "20s", -// EnableBackoff: true, +// EnableBackoff: false, // Insecure: true, // Timeout: "", // Interceptors: []string{}, @@ -296,11 +423,11 @@ func NewConfig(ctx context.Context, path string) (cfg *Config, err error) { // SocketOption: &config.SocketOption{ // ReusePort: true, // ReuseAddr: true, -// TCPFastOpen: true, -// TCPNoDelay: true, -// TCPQuickAck: true, +// TCPFastOpen: false, +// TCPNoDelay: false, +// TCPQuickAck: false, // TCPCork: false, -// TCPDeferAccept: true, +// TCPDeferAccept: false, // IPTransparent: false, // IPRecoverDestinationAddr: false, // }, diff --git a/pkg/tools/benchmark/job/handler/grpc/handler.go b/pkg/tools/benchmark/job/handler/grpc/handler.go index 7febc72d11..e3ec51c25d 100644 --- a/pkg/tools/benchmark/job/handler/grpc/handler.go +++ b/pkg/tools/benchmark/job/handler/grpc/handler.go @@ -21,7 +21,6 @@ import ( "context" "github.com/vdaas/vald/apis/grpc/v1/benchmark" - "github.com/vdaas/vald/internal/singleflight" "github.com/vdaas/vald/pkg/tools/benchmark/job/service" ) @@ -33,8 +32,7 @@ type Benchmark interface { type server struct { benchmark.UnimplementedJobServer - job service.Job - group singleflight.Group + job service.Job } func New(opts ...Option) (bm Benchmark, err error) { @@ -47,8 +45,6 @@ func New(opts ...Option) (bm Benchmark, err error) { } } - b.group = singleflight.New() - return b, nil } diff --git a/pkg/tools/benchmark/job/service/insert.go b/pkg/tools/benchmark/job/service/insert.go index 69d16ecc27..00ba632e80 100644 --- a/pkg/tools/benchmark/job/service/insert.go +++ b/pkg/tools/benchmark/job/service/insert.go @@ -23,9 +23,9 @@ import ( "strconv" "github.com/vdaas/vald/apis/grpc/v1/payload" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" + "golang.org/x/sync/errgroup" ) func (j *job) insert(ctx context.Context, ech chan error) error { @@ -38,8 +38,10 @@ func (j *job) insert(ctx context.Context, ech chan error) error { if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - eg, egctx := errgroup.New(ctx) - eg.Limitation(j.concurrencyLimit) + eg, egctx := errgroup.WithContext(ctx) + eg.SetLimit(j.concurrencyLimit) + // eg, egctx := errgroup.New(ctx) + // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { iter := i eg.Go(func() error { diff --git a/pkg/tools/benchmark/job/service/job.go b/pkg/tools/benchmark/job/service/job.go index 2f115ea157..168046e2b7 100644 --- a/pkg/tools/benchmark/job/service/job.go +++ b/pkg/tools/benchmark/job/service/job.go @@ -33,6 +33,7 @@ import ( "github.com/vdaas/vald/internal/k8s/client" v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/rand" "github.com/vdaas/vald/internal/safety" "github.com/vdaas/vald/internal/test/data/hdf5" "github.com/vdaas/vald/internal/timeutil/rate" @@ -245,34 +246,6 @@ func (j *job) PreStart(ctx context.Context) error { return err } } - // Wait for beforeJob completed if exists - if len(j.beforeJobName) != 0 { - var jobResource v1.ValdBenchmarkJob - log.Info("[benchmark job] check before benchjob is completed or not...") - j.eg.Go(safety.RecoverFunc(func() error { - dt := time.NewTicker(j.beforeJobDur) - defer dt.Stop() - for { - select { - case <-ctx.Done(): - return nil - case <-dt.C: - err := j.k8sClient.Get(ctx, j.beforeJobName, j.beforeJobNamespace, &jobResource) - if err != nil { - return err - } - if jobResource.Status == v1.BenchmarkJobCompleted { - log.Infof("[benchmark job ] before job (%s) is completed, job service will start soon.", j.beforeJobName) - return nil - } - log.Infof("[benchmark job] before job (%s) is not completed...", j.beforeJobName) - } - } - })) - if err := j.eg.Wait(); err != nil { - return err - } - } return nil } @@ -345,3 +318,12 @@ func calcRecall(linearRes, searchRes *payload.Search_Response) (recall float64) } return recall / float64(len(lres)) } + +// TODO: apply many object type +func addNoiseToVec(oVec []float32) []float32 { + noise := rand.Float32() + vec := oVec + idx := rand.LimitedUint32(uint64(len(oVec) - 1)) + vec[idx] += noise + return vec +} diff --git a/pkg/tools/benchmark/job/service/object.go b/pkg/tools/benchmark/job/service/object.go index eec7ea773f..af3971798f 100644 --- a/pkg/tools/benchmark/job/service/object.go +++ b/pkg/tools/benchmark/job/service/object.go @@ -22,15 +22,17 @@ import ( "strconv" "github.com/vdaas/vald/apis/grpc/v1/payload" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" + "golang.org/x/sync/errgroup" ) func (j *job) exists(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking exists") - eg, egctx := errgroup.New(ctx) - eg.Limitation(j.concurrencyLimit) + eg, egctx := errgroup.WithContext(ctx) + eg.SetLimit(j.concurrencyLimit) + // eg, egctx := errgroup.New(ctx) + // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { idx := i eg.Go(func() error { @@ -78,8 +80,10 @@ func (j *job) exists(ctx context.Context, ech chan error) error { func (j *job) getObject(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking getObject") - eg, egctx := errgroup.New(ctx) - eg.Limitation(j.concurrencyLimit) + eg, egctx := errgroup.WithContext(ctx) + eg.SetLimit(j.concurrencyLimit) + // eg, egctx := errgroup.New(ctx) + // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { log.Infof("[benchmark job] Start get object: iter = %d", i) ft := []*payload.Filter_Target{} diff --git a/pkg/tools/benchmark/job/service/remove.go b/pkg/tools/benchmark/job/service/remove.go index 66c604a6dd..be3c91c3fa 100644 --- a/pkg/tools/benchmark/job/service/remove.go +++ b/pkg/tools/benchmark/job/service/remove.go @@ -22,9 +22,9 @@ import ( "strconv" "github.com/vdaas/vald/apis/grpc/v1/payload" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" + "golang.org/x/sync/errgroup" ) func (j *job) remove(ctx context.Context, ech chan error) error { @@ -35,8 +35,10 @@ func (j *job) remove(ctx context.Context, ech chan error) error { if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - eg, egctx := errgroup.New(ctx) - eg.Limitation(j.concurrencyLimit) + eg, egctx := errgroup.WithContext(ctx) + eg.SetLimit(j.concurrencyLimit) + // eg, egctx := errgroup.New(ctx) + // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { idx := i eg.Go(func() error { diff --git a/pkg/tools/benchmark/job/service/search.go b/pkg/tools/benchmark/job/service/search.go index d8fb0d25f9..c7d5493705 100644 --- a/pkg/tools/benchmark/job/service/search.go +++ b/pkg/tools/benchmark/job/service/search.go @@ -22,9 +22,9 @@ import ( "math" "github.com/vdaas/vald/apis/grpc/v1/payload" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" + "golang.org/x/sync/errgroup" ) func (j *job) search(ctx context.Context, ech chan error) error { @@ -47,8 +47,10 @@ func (j *job) search(ctx context.Context, ech chan error) error { }(), } sres := make([]*payload.Search_Response, j.dataset.Indexes) - eg, egctx := errgroup.New(ctx) - eg.Limitation(j.concurrencyLimit) + eg, egctx := errgroup.WithContext(ctx) + eg.SetLimit(j.concurrencyLimit) + // eg, egctx := errgroup.New(ctx) + // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { iter := i eg.Go(func() error { @@ -92,6 +94,9 @@ func (j *job) search(ctx context.Context, ech chan error) error { } if res != nil && j.searchConfig.EnableLinearSearch { sres[iter-j.dataset.Range.Start] = res + log.Debugf("[benchmark job] Finish search: iter = %d, len = %d", iter, len(res.Results)) + } else { + log.Debugf("[benchmark job] Finish search: iter = %d, res = %v", iter, res) } log.Debugf("[benchmark job] Finish search: iter = %d, len = %d", iter, len(res.Results)) return nil diff --git a/pkg/tools/benchmark/job/service/update.go b/pkg/tools/benchmark/job/service/update.go index 2c7ab12c49..51a10a9ba2 100644 --- a/pkg/tools/benchmark/job/service/update.go +++ b/pkg/tools/benchmark/job/service/update.go @@ -23,9 +23,9 @@ import ( "strconv" "github.com/vdaas/vald/apis/grpc/v1/payload" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" + "golang.org/x/sync/errgroup" ) func (j *job) update(ctx context.Context, ech chan error) error { @@ -39,8 +39,10 @@ func (j *job) update(ctx context.Context, ech chan error) error { if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - eg, egctx := errgroup.New(ctx) - eg.Limitation(j.concurrencyLimit) + eg, egctx := errgroup.WithContext(ctx) + eg.SetLimit(j.concurrencyLimit) + // eg, egctx := errgroup.New(ctx) + // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { iter := i eg.Go(func() error { @@ -63,7 +65,7 @@ func (j *job) update(ctx context.Context, ech chan error) error { res, err := j.client.Update(egctx, &payload.Update_Request{ Vector: &payload.Object_Vector{ Id: strconv.Itoa(iter), - Vector: vecs[idx], + Vector: addNoiseToVec(vecs[idx]), }, Config: cfg, }) diff --git a/pkg/tools/benchmark/job/service/upsert.go b/pkg/tools/benchmark/job/service/upsert.go index a31f337972..016c296382 100644 --- a/pkg/tools/benchmark/job/service/upsert.go +++ b/pkg/tools/benchmark/job/service/upsert.go @@ -23,9 +23,9 @@ import ( "strconv" "github.com/vdaas/vald/apis/grpc/v1/payload" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" + "golang.org/x/sync/errgroup" ) func (j *job) upsert(ctx context.Context, ech chan error) error { @@ -39,8 +39,10 @@ func (j *job) upsert(ctx context.Context, ech chan error) error { if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - eg, egctx := errgroup.New(ctx) - eg.Limitation(j.concurrencyLimit) + eg, egctx := errgroup.WithContext(ctx) + eg.SetLimit(j.concurrencyLimit) + // eg, egctx := errgroup.New(ctx) + // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { iter := i eg.Go(func() error { @@ -62,7 +64,7 @@ func (j *job) upsert(ctx context.Context, ech chan error) error { res, err := j.client.Upsert(egctx, &payload.Upsert_Request{ Vector: &payload.Object_Vector{ Id: strconv.Itoa(iter), - Vector: vecs[idx], + Vector: addNoiseToVec(vecs[idx]), }, Config: cfg, }) diff --git a/pkg/tools/benchmark/job/usecase/benchmarkd.go b/pkg/tools/benchmark/job/usecase/benchmarkd.go index 400c6d580a..038f048c38 100644 --- a/pkg/tools/benchmark/job/usecase/benchmarkd.go +++ b/pkg/tools/benchmark/job/usecase/benchmarkd.go @@ -143,39 +143,6 @@ func New(cfg *config.Config) (r runner.Runner, err error) { } } - if len(cfg.Server.MetricsServers) == 0 { - cfg.Server.MetricsServers = []*iconf.Server{ - { - Name: "pprof", - Host: "0.0.0.0", - Port: uint16(6060), - HTTP: &iconf.HTTP{ - HandlerTimeout: "5s", - IdleTimeout: "2s", - ReadHeaderTimeout: "1s", - ReadTimeout: "1s", - ShutdownDuration: "5s", - WriteTimeout: "1m", - }, - Mode: "REST", - Network: "tcp", - ProbeWaitTime: "3s", - SocketOption: &iconf.SocketOption{ - IPRecoverDestinationAddr: false, - IPTransparent: false, - ReuseAddr: true, - ReusePort: true, - TCPCork: false, - TCPDeferAccept: true, - TCPFastOpen: true, - TCPNoDelay: true, - TCPQuickAck: true, - }, - SocketPath: "", - }, - } - } - srv, err := starter.New( starter.WithConfig(cfg.Server), starter.WithREST(func(sc *iconf.Server) []server.Option { diff --git a/pkg/tools/benchmark/operator/handler/grpc/handler.go b/pkg/tools/benchmark/operator/handler/grpc/handler.go index a11cf4133f..7809bc394c 100644 --- a/pkg/tools/benchmark/operator/handler/grpc/handler.go +++ b/pkg/tools/benchmark/operator/handler/grpc/handler.go @@ -21,7 +21,6 @@ import ( "context" "github.com/vdaas/vald/apis/grpc/v1/benchmark" - "github.com/vdaas/vald/internal/singleflight" "github.com/vdaas/vald/pkg/tools/benchmark/operator/service" ) @@ -34,7 +33,6 @@ type server struct { benchmark.UnimplementedJobServer operator service.Operator - group singleflight.Group } func New(opts ...Option) (bm Benchmark, err error) { @@ -47,8 +45,6 @@ func New(opts ...Option) (bm Benchmark, err error) { } } - b.group = singleflight.New() - return b, nil } From f5babfab2e2ea107041c5aed6061ac077c0ccfce Mon Sep 17 00:00:00 2001 From: Kiichiro YUKAWA Date: Mon, 11 Sep 2023 19:49:18 +0900 Subject: [PATCH 08/11] :recycle: Apply new internal/sync for continous benchmark (#2175) Signed-off-by: vankichi --- .../workflows/dockers-benchmark-job-image.yml | 111 +++++------------- .../dockers-benchmark-operator-image.yaml | 110 +++++------------ .../k8s/vald/benchmark/api/v1/job_types.go | 2 +- pkg/tools/benchmark/job/router/option.go | 2 +- pkg/tools/benchmark/job/router/router.go | 2 +- pkg/tools/benchmark/job/service/insert.go | 10 +- pkg/tools/benchmark/job/service/job.go | 2 +- pkg/tools/benchmark/job/service/object.go | 20 +--- pkg/tools/benchmark/job/service/option.go | 2 +- pkg/tools/benchmark/job/service/remove.go | 10 +- pkg/tools/benchmark/job/service/search.go | 19 +-- pkg/tools/benchmark/job/service/update.go | 10 +- pkg/tools/benchmark/job/service/upsert.go | 9 +- pkg/tools/benchmark/job/usecase/benchmarkd.go | 2 +- pkg/tools/benchmark/operator/router/option.go | 2 +- pkg/tools/benchmark/operator/router/router.go | 2 +- .../benchmark/operator/service/operator.go | 2 +- .../benchmark/operator/service/option.go | 2 +- .../benchmark/operator/usecase/benchmarkd.go | 2 +- 19 files changed, 84 insertions(+), 237 deletions(-) diff --git a/.github/workflows/dockers-benchmark-job-image.yml b/.github/workflows/dockers-benchmark-job-image.yml index f38149a7c3..1b2142e6a6 100644 --- a/.github/workflows/dockers-benchmark-job-image.yml +++ b/.github/workflows/dockers-benchmark-job-image.yml @@ -17,7 +17,7 @@ name: "Build docker image: benchmark-job" on: push: branches: - - master + - main tags: - "*.*.*" - "v*.*.*" @@ -25,109 +25,58 @@ on: - "v*.*.*-*" paths: - ".github/actions/docker-build/actions.yaml" + - ".github/workflows/_docker-image.yaml" - ".github/workflows/dockers-benchmak-job-image.yml" - "go.mod" - "go.sum" - "internal/**" - "!internal/**/*_test.go" - "!internal/db/**" - - "!internal/k8s/**" - "apis/grpc/**" + - "pkg/benchmark/operator/**" + - "cmd/benchmark/operator/**" - "pkg/benchmark/job/**" - "cmd/benchmark/job/**" - - "dockers/benchmark/job/Dockerfile" + - "dockers/tools/benchmark/job/Dockerfile" - "versions/GO_VERSION" - - "versions/NGT_VERSION" pull_request: paths: - ".github/actions/docker-build/actions.yaml" + - ".github/workflows/_docker-image.yaml" - ".github/workflows/dockers-benchmak-job-image.yml" - "go.mod" - "go.sum" - "internal/**" - "!internal/**/*_test.go" - "!internal/db/**" - - "!internal/k8s/**" - "apis/grpc/**" + - "pkg/benchmark/operator/**" + - "cmd/benchmark/operator/**" - "pkg/benchmark/job/**" - "cmd/benchmark/job/**" - - "dockers/benchmark/job/Dockerfile" + - "dockers/tools/benchmark/job/Dockerfile" + - "versions/GO_VERSION" + pull_request_target: + paths: + - ".github/actions/docker-build/actions.yaml" + - ".github/workflows/_docker-image.yaml" + - ".github/workflows/dockers-benchmak-job-image.yml" + - "go.mod" + - "go.sum" + - "internal/**" + - "!internal/**/*_test.go" + - "!internal/db/**" + - "apis/grpc/**" + - "pkg/benchmark/operator/**" + - "cmd/benchmark/operator/**" + - "pkg/benchmark/job/**" + - "cmd/benchmark/job/**" + - "dockers/tools/benchmark/job/Dockerfile" - "versions/GO_VERSION" - - "versions/NGT_VERSION" - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref != 'refs/heads/main' && github.ref || github.sha }}-${{ github.event_name }} - cancel-in-progress: true jobs: build: - strategy: - max-parallel: 4 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup QEMU - uses: docker/setup-qemu-action@v1 - with: - platforms: all - - name: Setup Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v1 - with: - buildkitd-flags: "--debug" - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USER }} - password: ${{ secrets.DOCKERHUB_PASS }} - - name: Login to GitHub Container Registry - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ secrets.PACKAGE_USER }} - password: ${{ secrets.PACKAGE_TOKEN }} - - name: Build and Publish - id: build_and_publish - uses: ./.github/actions/docker-build - with: - target: benchmark-job - builder: ${{ steps.buildx.outputs.name }} - - name: Initialize CodeQL - if: startsWith( github.ref, 'refs/tags/') - uses: github/codeql-action/init@v2 - - name: Run vulnerability scanner (table) - if: startsWith( github.ref, 'refs/tags/') - uses: aquasecurity/trivy-action@master - with: - image-ref: "${{ steps.build_and_publish.outputs.IMAGE_NAME }}:${{ steps.build_and_publish.outputs.PRIMARY_TAG }}" - format: "table" - - name: Run vulnerability scanner (sarif) - if: startsWith( github.ref, 'refs/tags/') - uses: aquasecurity/trivy-action@master - with: - image-ref: "${{ steps.build_and_publish.outputs.IMAGE_NAME }}:${{ steps.build_and_publish.outputs.PRIMARY_TAG }}" - format: "template" - template: "@/contrib/sarif.tpl" - output: "trivy-results.sarif" - - name: Upload Trivy scan results to Security tab - if: startsWith( github.ref, 'refs/tags/') - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: "trivy-results.sarif" - slack: - name: Slack notification - needs: build - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/tags/') - steps: - - uses: technote-space/workflow-conclusion-action@v2 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: 8398a7/action-slack@v3 - with: - author_name: benchmark-job image build - status: ${{ env.WORKFLOW_CONCLUSION }} - only_mention_fail: channel - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_NOTIFY_WEBHOOK_URL }} + uses: ./.github/workflows/_docker-image.yaml + with: + target: benchmark-job + secrets: inherit diff --git a/.github/workflows/dockers-benchmark-operator-image.yaml b/.github/workflows/dockers-benchmark-operator-image.yaml index ada420e0be..279d559cf3 100644 --- a/.github/workflows/dockers-benchmark-operator-image.yaml +++ b/.github/workflows/dockers-benchmark-operator-image.yaml @@ -17,7 +17,7 @@ name: "Build docker image: benchmark-operator" on: push: branches: - - master + - main tags: - "*.*.*" - "v*.*.*" @@ -25,22 +25,24 @@ on: - "v*.*.*-*" paths: - ".github/actions/docker-build/actions.yaml" + - ".github/workflows/_docker-image.yaml" - ".github/workflows/dockers-benchmak-operator-image.yml" - "go.mod" - "go.sum" - "internal/**" - "!internal/**/*_test.go" - "!internal/db/**" - - "!internal/k8s/**" - "apis/grpc/**" - "pkg/benchmark/operator/**" - "cmd/benchmark/operator/**" - - "dockers/benchmark/operator/Dockerfile" + - "pkg/benchmark/job/**" + - "cmd/benchmark/job/**" + - "dockers/tools/benchmark/operator/Dockerfile" - "versions/GO_VERSION" - - "versions/NGT_VERSION" pull_request: paths: - ".github/actions/docker-build/actions.yaml" + - ".github/workflows/_docker-image.yaml" - ".github/workflows/dockers-benchmak-operator-image.yml" - "go.mod" - "go.sum" @@ -50,83 +52,31 @@ on: - "apis/grpc/**" - "pkg/benchmark/operator/**" - "cmd/benchmark/operator/**" - - "dockers/benchmark/operator/Dockerfile" + - "pkg/benchmark/job/**" + - "cmd/benchmark/job/**" + - "dockers/tools/benchmark/operator/Dockerfile" + - "versions/GO_VERSION" + pull_request_target: + paths: + - ".github/actions/docker-build/actions.yaml" + - ".github/workflows/_docker-image.yaml" + - ".github/workflows/dockers-benchmak-operator-image.yml" + - "go.mod" + - "go.sum" + - "internal/**" + - "!internal/**/*_test.go" + - "!internal/db/**" + - "apis/grpc/**" + - "pkg/benchmark/operator/**" + - "cmd/benchmark/operator/**" + - "pkg/benchmark/job/**" + - "cmd/benchmark/job/**" + - "dockers/tools/benchmark/operator/Dockerfile" - "versions/GO_VERSION" - - "versions/NGT_VERSION" - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref != 'refs/heads/main' && github.ref || github.sha }}-${{ github.event_name }} - cancel-in-progress: true jobs: build: - strategy: - max-parallel: 4 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup QEMU - uses: docker/setup-qemu-action@v1 - with: - platforms: all - - name: Setup Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v1 - with: - buildkitd-flags: "--debug" - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USER }} - password: ${{ secrets.DOCKERHUB_PASS }} - - name: Login to GitHub Container Registry - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ secrets.PACKAGE_USER }} - password: ${{ secrets.PACKAGE_TOKEN }} - - name: Build and Publish - id: build_and_publish - uses: ./.github/actions/docker-build - with: - target: benchmark-operator - builder: ${{ steps.buildx.outputs.name }} - - name: Initialize CodeQL - if: startsWith( github.ref, 'refs/tags/') - uses: github/codeql-action/init@v2 - - name: Run vulnerability scanner (table) - if: startsWith( github.ref, 'refs/tags/') - uses: aquasecurity/trivy-action@master - with: - image-ref: "${{ steps.build_and_publish.outputs.IMAGE_NAME }}:${{ steps.build_and_publish.outputs.PRIMARY_TAG }}" - format: "table" - - name: Run vulnerability scanner (sarif) - if: startsWith( github.ref, 'refs/tags/') - uses: aquasecurity/trivy-action@master - with: - image-ref: "${{ steps.build_and_publish.outputs.IMAGE_NAME }}:${{ steps.build_and_publish.outputs.PRIMARY_TAG }}" - format: "template" - template: "@/contrib/sarif.tpl" - output: "trivy-results.sarif" - - name: Upload Trivy scan results to Security tab - if: startsWith( github.ref, 'refs/tags/') - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: "trivy-results.sarif" - slack: - name: Slack notification - needs: build - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/tags/') - steps: - - uses: technote-space/workflow-conclusion-action@v2 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: 8398a7/action-slack@v3 - with: - author_name: benchmark-operator image build - status: ${{ env.WORKFLOW_CONCLUSION }} - only_mention_fail: channel - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_NOTIFY_WEBHOOK_URL }} + uses: ./.github/workflows/_docker-image.yaml + with: + target: benchmark-operator + secrets: inherit diff --git a/internal/k8s/vald/benchmark/api/v1/job_types.go b/internal/k8s/vald/benchmark/api/v1/job_types.go index 9b8bbf1f0b..7d40109380 100644 --- a/internal/k8s/vald/benchmark/api/v1/job_types.go +++ b/internal/k8s/vald/benchmark/api/v1/job_types.go @@ -23,7 +23,7 @@ import ( ) type BenchmarkJobSpec struct { - *config.GlobalConfig `json:",omitempty" yaml:""` + *config.GlobalConfig `json:",omitempty" yaml:""` ServerConfig *config.Servers `json:"server_config,omitempty" yaml:"server_config"` Target *BenchmarkTarget `json:"target,omitempty" yaml:"target"` Dataset *BenchmarkDataset `json:"dataset,omitempty" yaml:"dataset"` diff --git a/pkg/tools/benchmark/job/router/option.go b/pkg/tools/benchmark/job/router/option.go index 7053ee57ea..e3cc0633f7 100644 --- a/pkg/tools/benchmark/job/router/option.go +++ b/pkg/tools/benchmark/job/router/option.go @@ -18,7 +18,7 @@ package router import ( - "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/internal/sync/errgroup" "github.com/vdaas/vald/pkg/tools/benchmark/job/handler/rest" ) diff --git a/pkg/tools/benchmark/job/router/router.go b/pkg/tools/benchmark/job/router/router.go index 6b45c72184..3020c9f506 100644 --- a/pkg/tools/benchmark/job/router/router.go +++ b/pkg/tools/benchmark/job/router/router.go @@ -20,9 +20,9 @@ package router import ( "net/http" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/net/http/middleware" "github.com/vdaas/vald/internal/net/http/routing" + "github.com/vdaas/vald/internal/sync/errgroup" "github.com/vdaas/vald/pkg/tools/benchmark/job/handler/rest" ) diff --git a/pkg/tools/benchmark/job/service/insert.go b/pkg/tools/benchmark/job/service/insert.go index 00ba632e80..d4074f292a 100644 --- a/pkg/tools/benchmark/job/service/insert.go +++ b/pkg/tools/benchmark/job/service/insert.go @@ -25,7 +25,7 @@ import ( "github.com/vdaas/vald/apis/grpc/v1/payload" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "golang.org/x/sync/errgroup" + "github.com/vdaas/vald/internal/sync/errgroup" ) func (j *job) insert(ctx context.Context, ech chan error) error { @@ -38,10 +38,8 @@ func (j *job) insert(ctx context.Context, ech chan error) error { if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - eg, egctx := errgroup.WithContext(ctx) + eg, egctx := errgroup.New(ctx) eg.SetLimit(j.concurrencyLimit) - // eg, egctx := errgroup.New(ctx) - // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { iter := i eg.Go(func() error { @@ -50,7 +48,6 @@ func (j *job) insert(ctx context.Context, ech chan error) error { if err != nil { log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { - // return errors.Join(err, context.Canceled) return nil } select { @@ -74,9 +71,6 @@ func (j *job) insert(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: - // if st, ok := status.FromError(err); ok { - // log.Warnf("[benchmark job] insert error is detected: code = %d, msg = %s", st.Code(), err.Error()) - // } } } // TODO: send metrics to the Prometeus diff --git a/pkg/tools/benchmark/job/service/job.go b/pkg/tools/benchmark/job/service/job.go index 168046e2b7..3a39977737 100644 --- a/pkg/tools/benchmark/job/service/job.go +++ b/pkg/tools/benchmark/job/service/job.go @@ -28,13 +28,13 @@ import ( "github.com/vdaas/vald/apis/grpc/v1/payload" "github.com/vdaas/vald/internal/client/v1/client/vald" "github.com/vdaas/vald/internal/config" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/k8s/client" v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" "github.com/vdaas/vald/internal/log" "github.com/vdaas/vald/internal/rand" "github.com/vdaas/vald/internal/safety" + "github.com/vdaas/vald/internal/sync/errgroup" "github.com/vdaas/vald/internal/test/data/hdf5" "github.com/vdaas/vald/internal/timeutil/rate" ) diff --git a/pkg/tools/benchmark/job/service/object.go b/pkg/tools/benchmark/job/service/object.go index af3971798f..542213de0e 100644 --- a/pkg/tools/benchmark/job/service/object.go +++ b/pkg/tools/benchmark/job/service/object.go @@ -24,15 +24,13 @@ import ( "github.com/vdaas/vald/apis/grpc/v1/payload" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "golang.org/x/sync/errgroup" + "github.com/vdaas/vald/internal/sync/errgroup" ) func (j *job) exists(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking exists") - eg, egctx := errgroup.WithContext(ctx) + eg, egctx := errgroup.New(ctx) eg.SetLimit(j.concurrencyLimit) - // eg, egctx := errgroup.New(ctx) - // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { idx := i eg.Go(func() error { @@ -42,7 +40,6 @@ func (j *job) exists(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { return nil - // return errors.Join(err, context.Canceled) } select { case <-egctx.Done(): @@ -58,11 +55,7 @@ func (j *job) exists(ctx context.Context, ech chan error) error { case <-egctx.Done(): log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return nil - // return errors.Join(err, egctx.Err()) default: - // if st, ok := status.FromError(err); ok { - // log.Warnf("[benchmark job] exists error is detected: code = %d, msg = %s", st.Code(), err.Error()) - // } } } log.Debugf("[benchmark job] Finish exists: iter= %d \n%v\n", idx, res) @@ -80,10 +73,8 @@ func (j *job) exists(ctx context.Context, ech chan error) error { func (j *job) getObject(ctx context.Context, ech chan error) error { log.Info("[benchmark job] Start benchmarking getObject") - eg, egctx := errgroup.WithContext(ctx) + eg, egctx := errgroup.New(ctx) eg.SetLimit(j.concurrencyLimit) - // eg, egctx := errgroup.New(ctx) - // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { log.Infof("[benchmark job] Start get object: iter = %d", i) ft := []*payload.Filter_Target{} @@ -102,7 +93,6 @@ func (j *job) getObject(ctx context.Context, ech chan error) error { if err != nil { log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { - // return errors.Join(err, context.Canceled) return nil } select { @@ -123,12 +113,8 @@ func (j *job) getObject(ctx context.Context, ech chan error) error { select { case <-egctx.Done(): log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) - // return errors.Join(err, egctx.Err()) return nil default: - // if st, ok := status.FromError(err); ok { - // log.Warnf("[benchmark job] object error is detected: code = %d, msg = %s", st.Code(), err.Error()) - // } } } if res != nil { diff --git a/pkg/tools/benchmark/job/service/option.go b/pkg/tools/benchmark/job/service/option.go index 5b2d470fae..e1d443d416 100644 --- a/pkg/tools/benchmark/job/service/option.go +++ b/pkg/tools/benchmark/job/service/option.go @@ -22,9 +22,9 @@ import ( "github.com/vdaas/vald/internal/client/v1/client/vald" "github.com/vdaas/vald/internal/config" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/k8s/client" + "github.com/vdaas/vald/internal/sync/errgroup" "github.com/vdaas/vald/internal/test/data/hdf5" "github.com/vdaas/vald/internal/timeutil" ) diff --git a/pkg/tools/benchmark/job/service/remove.go b/pkg/tools/benchmark/job/service/remove.go index be3c91c3fa..79f465e411 100644 --- a/pkg/tools/benchmark/job/service/remove.go +++ b/pkg/tools/benchmark/job/service/remove.go @@ -24,7 +24,7 @@ import ( "github.com/vdaas/vald/apis/grpc/v1/payload" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "golang.org/x/sync/errgroup" + "github.com/vdaas/vald/internal/sync/errgroup" ) func (j *job) remove(ctx context.Context, ech chan error) error { @@ -35,10 +35,8 @@ func (j *job) remove(ctx context.Context, ech chan error) error { if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - eg, egctx := errgroup.WithContext(ctx) + eg, egctx := errgroup.New(ctx) eg.SetLimit(j.concurrencyLimit) - // eg, egctx := errgroup.New(ctx) - // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { idx := i eg.Go(func() error { @@ -48,7 +46,6 @@ func (j *job) remove(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { return nil - // return errors.Join(err, context.Canceled) } select { case <-egctx.Done(): @@ -68,9 +65,6 @@ func (j *job) remove(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: - // if st, ok := status.FromError(err); ok { - // log.Warnf("[benchmark job] remove error is detected: code = %d, msg = %s", st.Code(), err.Error()) - // } } } log.Debugf("[benchmark job] Finish remove: iter= %d \n%v", idx, res) diff --git a/pkg/tools/benchmark/job/service/search.go b/pkg/tools/benchmark/job/service/search.go index c7d5493705..c59356ae86 100644 --- a/pkg/tools/benchmark/job/service/search.go +++ b/pkg/tools/benchmark/job/service/search.go @@ -24,7 +24,7 @@ import ( "github.com/vdaas/vald/apis/grpc/v1/payload" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "golang.org/x/sync/errgroup" + "github.com/vdaas/vald/internal/sync/errgroup" ) func (j *job) search(ctx context.Context, ech chan error) error { @@ -47,10 +47,8 @@ func (j *job) search(ctx context.Context, ech chan error) error { }(), } sres := make([]*payload.Search_Response, j.dataset.Indexes) - eg, egctx := errgroup.WithContext(ctx) + eg, egctx := errgroup.New(ctx) eg.SetLimit(j.concurrencyLimit) - // eg, egctx := errgroup.New(ctx) - // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { iter := i eg.Go(func() error { @@ -60,7 +58,6 @@ func (j *job) search(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { return nil - // return errors.Join(err, context.Canceled) } select { case <-egctx.Done(): @@ -83,13 +80,7 @@ func (j *job) search(ctx context.Context, ech chan error) error { case <-egctx.Done(): log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return nil - // return errors.Join(err, egctx.Err()) default: - // if st, ok := status.FromError(err); ok { - // if st.Code() != codes.NotFound { - // log.Warnf("[benchmark job] search error is detected: code = %d, msg = %s", st.Code(), err.Error()) - // } - // } } } if res != nil && j.searchConfig.EnableLinearSearch { @@ -116,7 +107,6 @@ func (j *job) search(ctx context.Context, ech chan error) error { if err != nil { log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { - // return errors.Join(err, context.Canceled) return nil } select { @@ -142,11 +132,6 @@ func (j *job) search(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: - // if st, ok := status.FromError(err); ok { - // if st.Code() != codes.NotFound { - // log.Warnf("[benchmark job] linear search error is detected: code = %d, msg = %s", st.Code(), err.Error()) - // } - // } } } if res != nil { diff --git a/pkg/tools/benchmark/job/service/update.go b/pkg/tools/benchmark/job/service/update.go index 51a10a9ba2..14848fcd65 100644 --- a/pkg/tools/benchmark/job/service/update.go +++ b/pkg/tools/benchmark/job/service/update.go @@ -25,7 +25,7 @@ import ( "github.com/vdaas/vald/apis/grpc/v1/payload" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "golang.org/x/sync/errgroup" + "github.com/vdaas/vald/internal/sync/errgroup" ) func (j *job) update(ctx context.Context, ech chan error) error { @@ -39,10 +39,8 @@ func (j *job) update(ctx context.Context, ech chan error) error { if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - eg, egctx := errgroup.WithContext(ctx) + eg, egctx := errgroup.New(ctx) eg.SetLimit(j.concurrencyLimit) - // eg, egctx := errgroup.New(ctx) - // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { iter := i eg.Go(func() error { @@ -51,7 +49,6 @@ func (j *job) update(ctx context.Context, ech chan error) error { if err != nil { log.Errorf("[benchmark job] limiter error is detected: %s", err.Error()) if errors.Is(err, context.Canceled) { - // return errors.Join(err, context.Canceled) return nil } select { @@ -75,9 +72,6 @@ func (j *job) update(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: - // if st, ok := status.FromError(err); ok { - // log.Warnf("[benchmark job] update error is detected: code = %d, msg = %s\n", st.Code(), err.Error()) - // } } } if res != nil { diff --git a/pkg/tools/benchmark/job/service/upsert.go b/pkg/tools/benchmark/job/service/upsert.go index 016c296382..1aaf4c3d44 100644 --- a/pkg/tools/benchmark/job/service/upsert.go +++ b/pkg/tools/benchmark/job/service/upsert.go @@ -25,7 +25,7 @@ import ( "github.com/vdaas/vald/apis/grpc/v1/payload" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" - "golang.org/x/sync/errgroup" + "github.com/vdaas/vald/internal/sync/errgroup" ) func (j *job) upsert(ctx context.Context, ech chan error) error { @@ -39,10 +39,8 @@ func (j *job) upsert(ctx context.Context, ech chan error) error { if j.timestamp > int64(0) { cfg.Timestamp = j.timestamp } - eg, egctx := errgroup.WithContext(ctx) + eg, egctx := errgroup.New(ctx) eg.SetLimit(j.concurrencyLimit) - // eg, egctx := errgroup.New(ctx) - // eg.Limitation(j.concurrencyLimit) for i := j.dataset.Range.Start; i <= j.dataset.Range.End; i++ { iter := i eg.Go(func() error { @@ -74,9 +72,6 @@ func (j *job) upsert(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: - // if st, ok := status.FromError(err); ok { - // log.Warnf("[benchmark job] upsert error is detected: code = %d, msg = %s", st.Code(), err.Error()) - // } } } if res != nil { diff --git a/pkg/tools/benchmark/job/usecase/benchmarkd.go b/pkg/tools/benchmark/job/usecase/benchmarkd.go index 038f048c38..c7e23e277e 100644 --- a/pkg/tools/benchmark/job/usecase/benchmarkd.go +++ b/pkg/tools/benchmark/job/usecase/benchmarkd.go @@ -23,7 +23,6 @@ import ( "github.com/vdaas/vald/internal/client/v1/client/vald" iconf "github.com/vdaas/vald/internal/config" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" "github.com/vdaas/vald/internal/net/grpc" @@ -34,6 +33,7 @@ import ( "github.com/vdaas/vald/internal/safety" "github.com/vdaas/vald/internal/servers/server" "github.com/vdaas/vald/internal/servers/starter" + "github.com/vdaas/vald/internal/sync/errgroup" "github.com/vdaas/vald/internal/test/data/hdf5" "github.com/vdaas/vald/pkg/tools/benchmark/job/config" handler "github.com/vdaas/vald/pkg/tools/benchmark/job/handler/grpc" diff --git a/pkg/tools/benchmark/operator/router/option.go b/pkg/tools/benchmark/operator/router/option.go index 681ce4bef7..875d6f8241 100644 --- a/pkg/tools/benchmark/operator/router/option.go +++ b/pkg/tools/benchmark/operator/router/option.go @@ -18,7 +18,7 @@ package router import ( - "github.com/vdaas/vald/internal/errgroup" + "github.com/vdaas/vald/internal/sync/errgroup" "github.com/vdaas/vald/pkg/tools/benchmark/operator/handler/rest" ) diff --git a/pkg/tools/benchmark/operator/router/router.go b/pkg/tools/benchmark/operator/router/router.go index 1303eeec8d..505df5dce3 100644 --- a/pkg/tools/benchmark/operator/router/router.go +++ b/pkg/tools/benchmark/operator/router/router.go @@ -20,9 +20,9 @@ package router import ( "net/http" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/net/http/middleware" "github.com/vdaas/vald/internal/net/http/routing" + "github.com/vdaas/vald/internal/sync/errgroup" "github.com/vdaas/vald/pkg/tools/benchmark/operator/handler/rest" ) diff --git a/pkg/tools/benchmark/operator/service/operator.go b/pkg/tools/benchmark/operator/service/operator.go index c3c6872859..4c41078537 100644 --- a/pkg/tools/benchmark/operator/service/operator.go +++ b/pkg/tools/benchmark/operator/service/operator.go @@ -24,7 +24,6 @@ import ( "sync/atomic" "time" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/k8s" "github.com/vdaas/vald/internal/k8s/client" @@ -33,6 +32,7 @@ import ( benchjob "github.com/vdaas/vald/internal/k8s/vald/benchmark/job" benchscenario "github.com/vdaas/vald/internal/k8s/vald/benchmark/scenario" "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/sync/errgroup" ) type Operator interface { diff --git a/pkg/tools/benchmark/operator/service/option.go b/pkg/tools/benchmark/operator/service/option.go index f38c10f38e..e1a426e480 100644 --- a/pkg/tools/benchmark/operator/service/option.go +++ b/pkg/tools/benchmark/operator/service/option.go @@ -20,8 +20,8 @@ package service import ( "time" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/sync/errgroup" ) // Option represents the functional option for scenario struct. diff --git a/pkg/tools/benchmark/operator/usecase/benchmarkd.go b/pkg/tools/benchmark/operator/usecase/benchmarkd.go index e867473bc5..6a8f2148de 100644 --- a/pkg/tools/benchmark/operator/usecase/benchmarkd.go +++ b/pkg/tools/benchmark/operator/usecase/benchmarkd.go @@ -22,7 +22,6 @@ import ( "os" iconf "github.com/vdaas/vald/internal/config" - "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" "github.com/vdaas/vald/internal/net/grpc" @@ -33,6 +32,7 @@ import ( "github.com/vdaas/vald/internal/safety" "github.com/vdaas/vald/internal/servers/server" "github.com/vdaas/vald/internal/servers/starter" + "github.com/vdaas/vald/internal/sync/errgroup" "github.com/vdaas/vald/pkg/tools/benchmark/operator/config" handler "github.com/vdaas/vald/pkg/tools/benchmark/operator/handler/grpc" "github.com/vdaas/vald/pkg/tools/benchmark/operator/handler/rest" From fcc72dd87d7fc73f68fa30443321e48c65b06e8a Mon Sep 17 00:00:00 2001 From: vankichi Date: Mon, 25 Sep 2023 17:41:00 +0900 Subject: [PATCH 09/11] :whale: fix Dockerfile Signed-off-by: vankichi --- dockers/tools/benchmark/job/Dockerfile | 25 +++++++++------------ dockers/tools/benchmark/operator/Dockerfile | 19 ++++++++-------- go.mod | 1 + go.sum | 4 ++++ 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/dockers/tools/benchmark/job/Dockerfile b/dockers/tools/benchmark/job/Dockerfile index a6251e3075..205be55e82 100644 --- a/dockers/tools/benchmark/job/Dockerfile +++ b/dockers/tools/benchmark/job/Dockerfile @@ -13,12 +13,15 @@ # See the License for the specific language governing permissions and # limitations under the License. # + ARG GO_VERSION=latest ARG DISTROLESS_IMAGE=gcr.io/distroless/static ARG DISTROLESS_IMAGE_TAG=nonroot ARG UPX_OPTIONS=-9 ARG MAINTAINER="vdaas.org vald team " +FROM golang:${GO_VERSION} AS golang + FROM ubuntu:devel AS builder ARG UPX_OPTIONS @@ -47,17 +50,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -RUN mkdir -p ${GOPATH}/src - COPY --from=golang /usr/local/go $GOROOT RUN mkdir -p "$GOPATH/src" -WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/Makefile.d +COPY Makefile.d . +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} +COPY Makefile . +COPY .git . COPY go.mod . COPY go.sum . -RUN go mod download +RUN make go/download WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/internal COPY internal . @@ -74,22 +79,15 @@ COPY cmd/${PKG} . WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/versions COPY versions . -WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/Makefile.d -COPY Makefile.d . - WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} -COPY Makefile . - -COPY .git . - RUN make REPO=${ORG} NAME=${REPO} cmd/${PKG}/${APP_NAME} \ - && upx ${UPX_OPTIONS} -o "/usr/bin/${APP_NAME}" "cmd/${PKG}/${APP_NAME}" + && mv "cmd/${PKG}/${APP_NAME}" "/usr/bin/${APP_NAME}" WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/cmd/${PKG} RUN cp sample.yaml /tmp/config.yaml FROM ${DISTROLESS_IMAGE}:${DISTROLESS_IMAGE_TAG} -LABEL maintainer "Vald team " +LABEL maintainer="${MAINTAINER}" ENV APP_NAME job COPY --from=builder /usr/lib/x86_64-linux-gnu/libaec* /usr/lib/x86_64-linux-gnu/ @@ -131,7 +129,6 @@ COPY --from=builder /lib/x86_64-linux-gnu/libtasn1.so.6 /lib/x86_64-linux-gnu/ COPY --from=builder /lib/x86_64-linux-gnu/libkeyutils.so.1 /lib/x86_64-linux-gnu/ COPY --from=builder /lib/x86_64-linux-gnu/libresolv.so.2 /lib/x86_64-linux-gnu/ COPY --from=builder /lib/x86_64-linux-gnu/libffi.so.8 /lib/x86_64-linux-gnu/ -# COPY --from=builder /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/ COPY --from=builder /lib64/ld-linux-x86-64.so.2 /lib64/ COPY --from=builder /usr/bin/${APP_NAME} /go/bin/${APP_NAME} diff --git a/dockers/tools/benchmark/operator/Dockerfile b/dockers/tools/benchmark/operator/Dockerfile index b930f92d04..9095b40a0e 100644 --- a/dockers/tools/benchmark/operator/Dockerfile +++ b/dockers/tools/benchmark/operator/Dockerfile @@ -20,6 +20,8 @@ ARG DISTROLESS_IMAGE_TAG=nonroot ARG UPX_OPTIONS=-9 ARG MAINTAINER="vdaas.org vald team " +FROM golang:${GO_VERSION} AS golang + FROM ubuntu:devel AS builder ENV GO111MODULE on @@ -50,8 +52,12 @@ RUN mkdir -p ${GOPATH}/src COPY --from=golang /usr/local/go $GOROOT RUN mkdir -p "$GOPATH/src" -WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/Makefile.d +COPY Makefile.d . +WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} +COPY Makefile . +COPY .git . COPY go.mod . COPY go.sum . @@ -72,22 +78,15 @@ COPY cmd/${PKG} . WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/versions COPY versions . -WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/Makefile.d -COPY Makefile.d . - WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO} -COPY Makefile . - -COPY .git . - RUN make REPO=${ORG} NAME=${REPO} cmd/${PKG}/${APP_NAME} \ - && upx ${UPX_OPTIONS} -o "/usr/bin/${APP_NAME}" "cmd/${PKG}/${APP_NAME}" + && mv "cmd/${PKG}/${APP_NAME}" "/usr/bin/${APP_NAME}" WORKDIR ${GOPATH}/src/github.com/${ORG}/${REPO}/cmd/${PKG} RUN cp sample.yaml /tmp/config.yaml FROM ${DISTROLESS_IMAGE}:${DISTROLESS_IMAGE_TAG} -LABEL maintainer "Vald team " +LABEL maintainer="${MAINTAINER}" ENV APP_NAME operator COPY --from=builder /usr/bin/${APP_NAME} /go/bin/${APP_NAME} diff --git a/go.mod b/go.mod index 47547ee8b1..7345580581 100755 --- a/go.mod +++ b/go.mod @@ -396,6 +396,7 @@ require ( golang.org/x/text v0.13.0 golang.org/x/tools v0.14.0 golang.org/x/time v0.3.0 + golang.org/x/tools v0.13.0 gonum.org/v1/hdf5 v0.0.0-00010101000000-000000000000 gonum.org/v1/plot v0.10.1 google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a diff --git a/go.sum b/go.sum index 5bf82041d1..daa65b4519 100644 --- a/go.sum +++ b/go.sum @@ -605,12 +605,16 @@ go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lI go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.starlark.net v0.0.0-20231016134836-22325403fcb3 h1:CKbpFNZNfaNyEWd6C+F1vLZ0WJjukoU45zDErBmRKPs= go.starlark.net v0.0.0-20231016134836-22325403fcb3/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= gocloud.dev v0.34.0 h1:LzlQY+4l2cMtuNfwT2ht4+fiXwWf/NmPTnXUlLmGif4= From 8052e647a9ba82baae2de9bc19e9bff739552108 Mon Sep 17 00:00:00 2001 From: vankichi Date: Wed, 11 Oct 2023 12:18:43 +0900 Subject: [PATCH 10/11] :recycle: Fix license and format Signed-off-by: vankichi --- .github/workflows/dockers-benchmark-job-image.yml | 2 +- .github/workflows/dockers-benchmark-operator-image.yaml | 2 +- apis/grpc/v1/benchmark/benchmark.pb.go | 2 +- apis/grpc/v1/benchmark/benchmark_vtproto.pb.go | 2 +- apis/proto/v1/benchmark/benchmark.proto | 8 ++++---- charts/vald-benchmark-operator/Chart.yaml | 2 +- charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml | 2 +- .../crds/valdbenchmarkoperatorrelease.yaml | 2 +- .../crds/valdbenchmarkscenario.yaml | 2 +- charts/vald-benchmark-operator/schemas/job-values.yaml | 2 +- .../vald-benchmark-operator/schemas/scenario-values.yaml | 2 +- charts/vald-benchmark-operator/templates/clusterrole.yaml | 2 +- .../templates/clusterrolebinding.yaml | 2 +- charts/vald-benchmark-operator/templates/configmap.yaml | 2 +- charts/vald-benchmark-operator/templates/deployment.yaml | 2 +- charts/vald-benchmark-operator/templates/service.yaml | 2 +- .../vald-benchmark-operator/templates/serviceaccount.yaml | 2 +- charts/vald-benchmark-operator/values.yaml | 2 +- charts/vald-benchmark-operator/values/benchmark-job.yaml | 2 +- .../values/benchmark-scenario.yaml | 2 +- cmd/tools/benchmark/job/main.go | 2 +- cmd/tools/benchmark/job/sample.yaml | 2 +- cmd/tools/benchmark/operator/main.go | 2 +- dockers/tools/benchmark/job/Dockerfile | 2 +- dockers/tools/benchmark/operator/Dockerfile | 2 +- internal/config/benchmark.go | 2 +- internal/k8s/client/client.go | 2 +- internal/k8s/client/option.go | 2 +- internal/k8s/job/job.go | 2 +- internal/k8s/job/option.go | 2 +- internal/k8s/vald/benchmark/api/v1/info.go | 2 +- internal/k8s/vald/benchmark/api/v1/job_types.go | 2 +- internal/k8s/vald/benchmark/api/v1/scenario_types.go | 2 +- internal/k8s/vald/benchmark/job/doc.go | 2 +- internal/k8s/vald/benchmark/job/job.go | 2 +- internal/k8s/vald/benchmark/job/job_template.go | 2 +- internal/k8s/vald/benchmark/job/job_template_option.go | 2 +- internal/k8s/vald/benchmark/job/option.go | 2 +- internal/k8s/vald/benchmark/scenario/doc.go | 2 +- internal/k8s/vald/benchmark/scenario/option.go | 2 +- internal/k8s/vald/benchmark/scenario/scenario.go | 2 +- internal/test/data/hdf5/doc.go | 2 +- internal/test/data/hdf5/hdf5.go | 2 +- internal/test/data/hdf5/hdf5_test.go | 2 +- internal/test/data/hdf5/option.go | 2 +- internal/test/data/hdf5/option_test.go | 2 +- internal/timeutil/rate/rate.go | 2 +- k8s/tools/benchmark/job/clusterrole.yaml | 2 +- k8s/tools/benchmark/job/clusterrolebinding.yaml | 2 +- k8s/tools/benchmark/job/serviceaccount.yaml | 2 +- k8s/tools/benchmark/job/svc.yaml | 2 +- k8s/tools/benchmark/operator/clusterrole.yaml | 2 +- k8s/tools/benchmark/operator/clusterrolebinding.yaml | 2 +- k8s/tools/benchmark/operator/configmap.yaml | 2 +- k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml | 2 +- .../operator/crds/valdbenchmarkoperatorrelease.yaml | 2 +- .../benchmark/operator/crds/valdbenchmarkscenario.yaml | 2 +- k8s/tools/benchmark/operator/deployment.yaml | 2 +- k8s/tools/benchmark/operator/service.yaml | 2 +- k8s/tools/benchmark/operator/serviceaccount.yaml | 2 +- pkg/tools/benchmark/job/config/config.go | 2 +- pkg/tools/benchmark/job/config/config_test.go | 2 +- pkg/tools/benchmark/job/config/doc.go | 2 +- pkg/tools/benchmark/job/handler/doc.go | 2 +- pkg/tools/benchmark/job/handler/grpc/handler.go | 2 +- pkg/tools/benchmark/job/handler/grpc/option.go | 2 +- pkg/tools/benchmark/job/handler/rest/handler.go | 2 +- pkg/tools/benchmark/job/handler/rest/option.go | 2 +- pkg/tools/benchmark/job/router/doc.go | 2 +- pkg/tools/benchmark/job/router/option.go | 2 +- pkg/tools/benchmark/job/router/router.go | 2 +- pkg/tools/benchmark/job/service/doc.go | 2 +- pkg/tools/benchmark/job/service/insert.go | 2 +- pkg/tools/benchmark/job/service/job.go | 2 +- pkg/tools/benchmark/job/service/object.go | 2 +- pkg/tools/benchmark/job/service/option.go | 2 +- pkg/tools/benchmark/job/service/remove.go | 2 +- pkg/tools/benchmark/job/service/search.go | 2 +- pkg/tools/benchmark/job/service/update.go | 2 +- pkg/tools/benchmark/job/service/upsert.go | 2 +- pkg/tools/benchmark/job/usecase/benchmarkd.go | 2 +- pkg/tools/benchmark/operator/config/config.go | 2 +- pkg/tools/benchmark/operator/config/doc.go | 2 +- pkg/tools/benchmark/operator/handler/doc.go | 2 +- pkg/tools/benchmark/operator/handler/grpc/handler.go | 2 +- pkg/tools/benchmark/operator/handler/grpc/option.go | 2 +- pkg/tools/benchmark/operator/handler/rest/handler.go | 2 +- pkg/tools/benchmark/operator/handler/rest/option.go | 2 +- pkg/tools/benchmark/operator/router/doc.go | 2 +- pkg/tools/benchmark/operator/router/option.go | 2 +- pkg/tools/benchmark/operator/router/router.go | 2 +- pkg/tools/benchmark/operator/service/doc.go | 2 +- pkg/tools/benchmark/operator/service/operator.go | 2 +- pkg/tools/benchmark/operator/service/option.go | 2 +- pkg/tools/benchmark/operator/usecase/benchmarkd.go | 2 +- 95 files changed, 98 insertions(+), 98 deletions(-) diff --git a/.github/workflows/dockers-benchmark-job-image.yml b/.github/workflows/dockers-benchmark-job-image.yml index 1b2142e6a6..c4b9d63c0c 100644 --- a/.github/workflows/dockers-benchmark-job-image.yml +++ b/.github/workflows/dockers-benchmark-job-image.yml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/.github/workflows/dockers-benchmark-operator-image.yaml b/.github/workflows/dockers-benchmark-operator-image.yaml index 279d559cf3..f1d290229a 100644 --- a/.github/workflows/dockers-benchmark-operator-image.yaml +++ b/.github/workflows/dockers-benchmark-operator-image.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/apis/grpc/v1/benchmark/benchmark.pb.go b/apis/grpc/v1/benchmark/benchmark.pb.go index 69a3a42d39..d09ca96802 100644 --- a/apis/grpc/v1/benchmark/benchmark.pb.go +++ b/apis/grpc/v1/benchmark/benchmark.pb.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/apis/grpc/v1/benchmark/benchmark_vtproto.pb.go b/apis/grpc/v1/benchmark/benchmark_vtproto.pb.go index d0c6147ab2..12e3feb0a6 100644 --- a/apis/grpc/v1/benchmark/benchmark_vtproto.pb.go +++ b/apis/grpc/v1/benchmark/benchmark_vtproto.pb.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/apis/proto/v1/benchmark/benchmark.proto b/apis/proto/v1/benchmark/benchmark.proto index cc1bbc9596..47c78dac9c 100644 --- a/apis/proto/v1/benchmark/benchmark.proto +++ b/apis/proto/v1/benchmark/benchmark.proto @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 @@ -20,13 +20,13 @@ package benchmark.v1; option go_package = "github.com/vdaas/vald/apis/grpc/v1/benchmark"; option java_multiple_files = true; -option java_package = "org.vdaas.vald.api.v1.benchmark"; option java_outer_classname = "Benchmark"; +option java_package = "org.vdaas.vald.api.v1.benchmark"; service Controller { - // TODO define API spec here + // TODO define API spec here } service Job { - // TODO define API spec here + // TODO define API spec here } diff --git a/charts/vald-benchmark-operator/Chart.yaml b/charts/vald-benchmark-operator/Chart.yaml index b0726effed..52822a3706 100644 --- a/charts/vald-benchmark-operator/Chart.yaml +++ b/charts/vald-benchmark-operator/Chart.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml b/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml index f5669958ee..6b730312dd 100644 --- a/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml +++ b/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml b/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml index 28ab299701..58ead1bcaa 100644 --- a/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml +++ b/charts/vald-benchmark-operator/crds/valdbenchmarkoperatorrelease.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml b/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml index 871f59b390..a45074f8db 100644 --- a/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml +++ b/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/schemas/job-values.yaml b/charts/vald-benchmark-operator/schemas/job-values.yaml index a795d321b6..75119aad2e 100644 --- a/charts/vald-benchmark-operator/schemas/job-values.yaml +++ b/charts/vald-benchmark-operator/schemas/job-values.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/schemas/scenario-values.yaml b/charts/vald-benchmark-operator/schemas/scenario-values.yaml index e33b76f5f5..f675e26b49 100644 --- a/charts/vald-benchmark-operator/schemas/scenario-values.yaml +++ b/charts/vald-benchmark-operator/schemas/scenario-values.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/templates/clusterrole.yaml b/charts/vald-benchmark-operator/templates/clusterrole.yaml index cd41992a5a..3402df59c1 100644 --- a/charts/vald-benchmark-operator/templates/clusterrole.yaml +++ b/charts/vald-benchmark-operator/templates/clusterrole.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/templates/clusterrolebinding.yaml b/charts/vald-benchmark-operator/templates/clusterrolebinding.yaml index 5130f14292..84eeb22014 100644 --- a/charts/vald-benchmark-operator/templates/clusterrolebinding.yaml +++ b/charts/vald-benchmark-operator/templates/clusterrolebinding.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/templates/configmap.yaml b/charts/vald-benchmark-operator/templates/configmap.yaml index f00359380f..37daaa8199 100644 --- a/charts/vald-benchmark-operator/templates/configmap.yaml +++ b/charts/vald-benchmark-operator/templates/configmap.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/templates/deployment.yaml b/charts/vald-benchmark-operator/templates/deployment.yaml index 5be94d8829..0f06f7b45a 100644 --- a/charts/vald-benchmark-operator/templates/deployment.yaml +++ b/charts/vald-benchmark-operator/templates/deployment.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/templates/service.yaml b/charts/vald-benchmark-operator/templates/service.yaml index 0be6a1a882..8e09cf0031 100644 --- a/charts/vald-benchmark-operator/templates/service.yaml +++ b/charts/vald-benchmark-operator/templates/service.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/templates/serviceaccount.yaml b/charts/vald-benchmark-operator/templates/serviceaccount.yaml index f07cdf57b9..c0498fd8e2 100644 --- a/charts/vald-benchmark-operator/templates/serviceaccount.yaml +++ b/charts/vald-benchmark-operator/templates/serviceaccount.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/values.yaml b/charts/vald-benchmark-operator/values.yaml index a6c2b16bef..a5e00c5a60 100644 --- a/charts/vald-benchmark-operator/values.yaml +++ b/charts/vald-benchmark-operator/values.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/values/benchmark-job.yaml b/charts/vald-benchmark-operator/values/benchmark-job.yaml index 424845cba6..6c626d9887 100644 --- a/charts/vald-benchmark-operator/values/benchmark-job.yaml +++ b/charts/vald-benchmark-operator/values/benchmark-job.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/charts/vald-benchmark-operator/values/benchmark-scenario.yaml b/charts/vald-benchmark-operator/values/benchmark-scenario.yaml index 0460e1a151..7d37c016e4 100644 --- a/charts/vald-benchmark-operator/values/benchmark-scenario.yaml +++ b/charts/vald-benchmark-operator/values/benchmark-scenario.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/cmd/tools/benchmark/job/main.go b/cmd/tools/benchmark/job/main.go index c7e31931de..51420f3bf0 100644 --- a/cmd/tools/benchmark/job/main.go +++ b/cmd/tools/benchmark/job/main.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/cmd/tools/benchmark/job/sample.yaml b/cmd/tools/benchmark/job/sample.yaml index d49500d6b1..a48697ed45 100644 --- a/cmd/tools/benchmark/job/sample.yaml +++ b/cmd/tools/benchmark/job/sample.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/cmd/tools/benchmark/operator/main.go b/cmd/tools/benchmark/operator/main.go index a6799e2922..3961abc9dd 100644 --- a/cmd/tools/benchmark/operator/main.go +++ b/cmd/tools/benchmark/operator/main.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/dockers/tools/benchmark/job/Dockerfile b/dockers/tools/benchmark/job/Dockerfile index 205be55e82..a806553afb 100644 --- a/dockers/tools/benchmark/job/Dockerfile +++ b/dockers/tools/benchmark/job/Dockerfile @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/dockers/tools/benchmark/operator/Dockerfile b/dockers/tools/benchmark/operator/Dockerfile index 9095b40a0e..f9301cafb6 100644 --- a/dockers/tools/benchmark/operator/Dockerfile +++ b/dockers/tools/benchmark/operator/Dockerfile @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/config/benchmark.go b/internal/config/benchmark.go index 84ec55b58a..e859ca4fd7 100644 --- a/internal/config/benchmark.go +++ b/internal/config/benchmark.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/client/client.go b/internal/k8s/client/client.go index 62ee649f03..1ebb013426 100644 --- a/internal/k8s/client/client.go +++ b/internal/k8s/client/client.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/client/option.go b/internal/k8s/client/option.go index 6d2d9a624b..38f89ecec1 100644 --- a/internal/k8s/client/option.go +++ b/internal/k8s/client/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/job/job.go b/internal/k8s/job/job.go index 97ec3de0eb..76fda87522 100644 --- a/internal/k8s/job/job.go +++ b/internal/k8s/job/job.go @@ -1,7 +1,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/job/option.go b/internal/k8s/job/option.go index 7752092de4..26776f7ddb 100644 --- a/internal/k8s/job/option.go +++ b/internal/k8s/job/option.go @@ -1,7 +1,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/vald/benchmark/api/v1/info.go b/internal/k8s/vald/benchmark/api/v1/info.go index 5fd71cf8cc..a67b4b9850 100644 --- a/internal/k8s/vald/benchmark/api/v1/info.go +++ b/internal/k8s/vald/benchmark/api/v1/info.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/vald/benchmark/api/v1/job_types.go b/internal/k8s/vald/benchmark/api/v1/job_types.go index 7d40109380..4de559b154 100644 --- a/internal/k8s/vald/benchmark/api/v1/job_types.go +++ b/internal/k8s/vald/benchmark/api/v1/job_types.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/vald/benchmark/api/v1/scenario_types.go b/internal/k8s/vald/benchmark/api/v1/scenario_types.go index ddedb49371..a0d1ebc821 100644 --- a/internal/k8s/vald/benchmark/api/v1/scenario_types.go +++ b/internal/k8s/vald/benchmark/api/v1/scenario_types.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/vald/benchmark/job/doc.go b/internal/k8s/vald/benchmark/job/doc.go index 0567d4f35b..ff3b63f0d2 100644 --- a/internal/k8s/vald/benchmark/job/doc.go +++ b/internal/k8s/vald/benchmark/job/doc.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/vald/benchmark/job/job.go b/internal/k8s/vald/benchmark/job/job.go index 6f81ee01fa..466d27107b 100644 --- a/internal/k8s/vald/benchmark/job/job.go +++ b/internal/k8s/vald/benchmark/job/job.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/vald/benchmark/job/job_template.go b/internal/k8s/vald/benchmark/job/job_template.go index 6b1926f0dc..54f2c4a542 100644 --- a/internal/k8s/vald/benchmark/job/job_template.go +++ b/internal/k8s/vald/benchmark/job/job_template.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/vald/benchmark/job/job_template_option.go b/internal/k8s/vald/benchmark/job/job_template_option.go index 4ec09250d3..e401c78a5b 100644 --- a/internal/k8s/vald/benchmark/job/job_template_option.go +++ b/internal/k8s/vald/benchmark/job/job_template_option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/vald/benchmark/job/option.go b/internal/k8s/vald/benchmark/job/option.go index aece8a869d..828597673f 100644 --- a/internal/k8s/vald/benchmark/job/option.go +++ b/internal/k8s/vald/benchmark/job/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/vald/benchmark/scenario/doc.go b/internal/k8s/vald/benchmark/scenario/doc.go index b93a52b69b..96f31f66c7 100644 --- a/internal/k8s/vald/benchmark/scenario/doc.go +++ b/internal/k8s/vald/benchmark/scenario/doc.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/vald/benchmark/scenario/option.go b/internal/k8s/vald/benchmark/scenario/option.go index adda6adfee..f04078c81b 100644 --- a/internal/k8s/vald/benchmark/scenario/option.go +++ b/internal/k8s/vald/benchmark/scenario/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/k8s/vald/benchmark/scenario/scenario.go b/internal/k8s/vald/benchmark/scenario/scenario.go index d03d1e37da..0ef74d4b6f 100644 --- a/internal/k8s/vald/benchmark/scenario/scenario.go +++ b/internal/k8s/vald/benchmark/scenario/scenario.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/test/data/hdf5/doc.go b/internal/test/data/hdf5/doc.go index 0db75d556e..6149232713 100644 --- a/internal/test/data/hdf5/doc.go +++ b/internal/test/data/hdf5/doc.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/test/data/hdf5/hdf5.go b/internal/test/data/hdf5/hdf5.go index b308687cf0..fb24401efa 100644 --- a/internal/test/data/hdf5/hdf5.go +++ b/internal/test/data/hdf5/hdf5.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/test/data/hdf5/hdf5_test.go b/internal/test/data/hdf5/hdf5_test.go index 940a76021b..e839acec5e 100644 --- a/internal/test/data/hdf5/hdf5_test.go +++ b/internal/test/data/hdf5/hdf5_test.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/test/data/hdf5/option.go b/internal/test/data/hdf5/option.go index 4d02b281f2..c8ffae2988 100644 --- a/internal/test/data/hdf5/option.go +++ b/internal/test/data/hdf5/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/test/data/hdf5/option_test.go b/internal/test/data/hdf5/option_test.go index e239c3361d..d9702fde89 100644 --- a/internal/test/data/hdf5/option_test.go +++ b/internal/test/data/hdf5/option_test.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/internal/timeutil/rate/rate.go b/internal/timeutil/rate/rate.go index 045b999a9a..4065021e2d 100644 --- a/internal/timeutil/rate/rate.go +++ b/internal/timeutil/rate/rate.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/job/clusterrole.yaml b/k8s/tools/benchmark/job/clusterrole.yaml index 3fe77f9445..81adc0d54f 100644 --- a/k8s/tools/benchmark/job/clusterrole.yaml +++ b/k8s/tools/benchmark/job/clusterrole.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/job/clusterrolebinding.yaml b/k8s/tools/benchmark/job/clusterrolebinding.yaml index 46b5b27c63..41e8ffbc65 100644 --- a/k8s/tools/benchmark/job/clusterrolebinding.yaml +++ b/k8s/tools/benchmark/job/clusterrolebinding.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/job/serviceaccount.yaml b/k8s/tools/benchmark/job/serviceaccount.yaml index 419a7f8f37..f1536745b7 100644 --- a/k8s/tools/benchmark/job/serviceaccount.yaml +++ b/k8s/tools/benchmark/job/serviceaccount.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/job/svc.yaml b/k8s/tools/benchmark/job/svc.yaml index fd78cebd0e..ec9dd15066 100644 --- a/k8s/tools/benchmark/job/svc.yaml +++ b/k8s/tools/benchmark/job/svc.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/operator/clusterrole.yaml b/k8s/tools/benchmark/operator/clusterrole.yaml index 4d3fc54948..7675f44e8e 100644 --- a/k8s/tools/benchmark/operator/clusterrole.yaml +++ b/k8s/tools/benchmark/operator/clusterrole.yaml @@ -3,7 +3,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/operator/clusterrolebinding.yaml b/k8s/tools/benchmark/operator/clusterrolebinding.yaml index ded68d5204..9543ed59d3 100644 --- a/k8s/tools/benchmark/operator/clusterrolebinding.yaml +++ b/k8s/tools/benchmark/operator/clusterrolebinding.yaml @@ -3,7 +3,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/operator/configmap.yaml b/k8s/tools/benchmark/operator/configmap.yaml index 7f6f7a492a..095bc14316 100644 --- a/k8s/tools/benchmark/operator/configmap.yaml +++ b/k8s/tools/benchmark/operator/configmap.yaml @@ -3,7 +3,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml b/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml index f5669958ee..6b730312dd 100644 --- a/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml +++ b/k8s/tools/benchmark/operator/crds/valdbenchmarkjob.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml b/k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml index 28ab299701..58ead1bcaa 100644 --- a/k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml +++ b/k8s/tools/benchmark/operator/crds/valdbenchmarkoperatorrelease.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml b/k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml index 871f59b390..a45074f8db 100644 --- a/k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml +++ b/k8s/tools/benchmark/operator/crds/valdbenchmarkscenario.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/operator/deployment.yaml b/k8s/tools/benchmark/operator/deployment.yaml index 520609518b..86318ae4cb 100644 --- a/k8s/tools/benchmark/operator/deployment.yaml +++ b/k8s/tools/benchmark/operator/deployment.yaml @@ -3,7 +3,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/operator/service.yaml b/k8s/tools/benchmark/operator/service.yaml index 0cb8ba3770..6cc3d6a4a1 100644 --- a/k8s/tools/benchmark/operator/service.yaml +++ b/k8s/tools/benchmark/operator/service.yaml @@ -3,7 +3,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/k8s/tools/benchmark/operator/serviceaccount.yaml b/k8s/tools/benchmark/operator/serviceaccount.yaml index cf7ac3908d..817cc7b5d4 100644 --- a/k8s/tools/benchmark/operator/serviceaccount.yaml +++ b/k8s/tools/benchmark/operator/serviceaccount.yaml @@ -3,7 +3,7 @@ # Copyright (C) 2019-2023 vdaas.org vald team # # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. +# You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/config/config.go b/pkg/tools/benchmark/job/config/config.go index afab9a9392..22e3ab6e8b 100644 --- a/pkg/tools/benchmark/job/config/config.go +++ b/pkg/tools/benchmark/job/config/config.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/config/config_test.go b/pkg/tools/benchmark/job/config/config_test.go index 57dcee5efd..0f46ac0c44 100644 --- a/pkg/tools/benchmark/job/config/config_test.go +++ b/pkg/tools/benchmark/job/config/config_test.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/config/doc.go b/pkg/tools/benchmark/job/config/doc.go index 04fa0ebe9b..c183b6b985 100644 --- a/pkg/tools/benchmark/job/config/doc.go +++ b/pkg/tools/benchmark/job/config/doc.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/handler/doc.go b/pkg/tools/benchmark/job/handler/doc.go index 825bcc7f61..179b99456b 100644 --- a/pkg/tools/benchmark/job/handler/doc.go +++ b/pkg/tools/benchmark/job/handler/doc.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/handler/grpc/handler.go b/pkg/tools/benchmark/job/handler/grpc/handler.go index e3ec51c25d..e4fd0944d8 100644 --- a/pkg/tools/benchmark/job/handler/grpc/handler.go +++ b/pkg/tools/benchmark/job/handler/grpc/handler.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/handler/grpc/option.go b/pkg/tools/benchmark/job/handler/grpc/option.go index d5d9c32d1e..89f01bdccf 100644 --- a/pkg/tools/benchmark/job/handler/grpc/option.go +++ b/pkg/tools/benchmark/job/handler/grpc/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/handler/rest/handler.go b/pkg/tools/benchmark/job/handler/rest/handler.go index 1b9494c3d5..e1e6867192 100644 --- a/pkg/tools/benchmark/job/handler/rest/handler.go +++ b/pkg/tools/benchmark/job/handler/rest/handler.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/handler/rest/option.go b/pkg/tools/benchmark/job/handler/rest/option.go index 054b120b94..9f90068e67 100644 --- a/pkg/tools/benchmark/job/handler/rest/option.go +++ b/pkg/tools/benchmark/job/handler/rest/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/router/doc.go b/pkg/tools/benchmark/job/router/doc.go index ffa1ce388f..3572a233f4 100644 --- a/pkg/tools/benchmark/job/router/doc.go +++ b/pkg/tools/benchmark/job/router/doc.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/router/option.go b/pkg/tools/benchmark/job/router/option.go index e3cc0633f7..1591eec107 100644 --- a/pkg/tools/benchmark/job/router/option.go +++ b/pkg/tools/benchmark/job/router/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/router/router.go b/pkg/tools/benchmark/job/router/router.go index 3020c9f506..5cdfdf1f93 100644 --- a/pkg/tools/benchmark/job/router/router.go +++ b/pkg/tools/benchmark/job/router/router.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/service/doc.go b/pkg/tools/benchmark/job/service/doc.go index 4d4cfe1548..8f39410597 100644 --- a/pkg/tools/benchmark/job/service/doc.go +++ b/pkg/tools/benchmark/job/service/doc.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/service/insert.go b/pkg/tools/benchmark/job/service/insert.go index d4074f292a..b38c3622b2 100644 --- a/pkg/tools/benchmark/job/service/insert.go +++ b/pkg/tools/benchmark/job/service/insert.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/service/job.go b/pkg/tools/benchmark/job/service/job.go index 3a39977737..b047051ea1 100644 --- a/pkg/tools/benchmark/job/service/job.go +++ b/pkg/tools/benchmark/job/service/job.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/service/object.go b/pkg/tools/benchmark/job/service/object.go index 542213de0e..9284b50f95 100644 --- a/pkg/tools/benchmark/job/service/object.go +++ b/pkg/tools/benchmark/job/service/object.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/service/option.go b/pkg/tools/benchmark/job/service/option.go index e1d443d416..79506647e8 100644 --- a/pkg/tools/benchmark/job/service/option.go +++ b/pkg/tools/benchmark/job/service/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/service/remove.go b/pkg/tools/benchmark/job/service/remove.go index 79f465e411..c8a6650bc2 100644 --- a/pkg/tools/benchmark/job/service/remove.go +++ b/pkg/tools/benchmark/job/service/remove.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/service/search.go b/pkg/tools/benchmark/job/service/search.go index c59356ae86..715b8f5356 100644 --- a/pkg/tools/benchmark/job/service/search.go +++ b/pkg/tools/benchmark/job/service/search.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/service/update.go b/pkg/tools/benchmark/job/service/update.go index 14848fcd65..f641f84f57 100644 --- a/pkg/tools/benchmark/job/service/update.go +++ b/pkg/tools/benchmark/job/service/update.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/service/upsert.go b/pkg/tools/benchmark/job/service/upsert.go index 1aaf4c3d44..71a88cfda7 100644 --- a/pkg/tools/benchmark/job/service/upsert.go +++ b/pkg/tools/benchmark/job/service/upsert.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/job/usecase/benchmarkd.go b/pkg/tools/benchmark/job/usecase/benchmarkd.go index c7e23e277e..6b3501b34e 100644 --- a/pkg/tools/benchmark/job/usecase/benchmarkd.go +++ b/pkg/tools/benchmark/job/usecase/benchmarkd.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/config/config.go b/pkg/tools/benchmark/operator/config/config.go index cf9acbdfd1..2a2bc33d56 100644 --- a/pkg/tools/benchmark/operator/config/config.go +++ b/pkg/tools/benchmark/operator/config/config.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/config/doc.go b/pkg/tools/benchmark/operator/config/doc.go index 04fa0ebe9b..c183b6b985 100644 --- a/pkg/tools/benchmark/operator/config/doc.go +++ b/pkg/tools/benchmark/operator/config/doc.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/handler/doc.go b/pkg/tools/benchmark/operator/handler/doc.go index 825bcc7f61..179b99456b 100644 --- a/pkg/tools/benchmark/operator/handler/doc.go +++ b/pkg/tools/benchmark/operator/handler/doc.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/handler/grpc/handler.go b/pkg/tools/benchmark/operator/handler/grpc/handler.go index 7809bc394c..06fce336f5 100644 --- a/pkg/tools/benchmark/operator/handler/grpc/handler.go +++ b/pkg/tools/benchmark/operator/handler/grpc/handler.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/handler/grpc/option.go b/pkg/tools/benchmark/operator/handler/grpc/option.go index d5d9c32d1e..89f01bdccf 100644 --- a/pkg/tools/benchmark/operator/handler/grpc/option.go +++ b/pkg/tools/benchmark/operator/handler/grpc/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/handler/rest/handler.go b/pkg/tools/benchmark/operator/handler/rest/handler.go index 1b9494c3d5..e1e6867192 100644 --- a/pkg/tools/benchmark/operator/handler/rest/handler.go +++ b/pkg/tools/benchmark/operator/handler/rest/handler.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/handler/rest/option.go b/pkg/tools/benchmark/operator/handler/rest/option.go index 054b120b94..9f90068e67 100644 --- a/pkg/tools/benchmark/operator/handler/rest/option.go +++ b/pkg/tools/benchmark/operator/handler/rest/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/router/doc.go b/pkg/tools/benchmark/operator/router/doc.go index ffa1ce388f..3572a233f4 100644 --- a/pkg/tools/benchmark/operator/router/doc.go +++ b/pkg/tools/benchmark/operator/router/doc.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/router/option.go b/pkg/tools/benchmark/operator/router/option.go index 875d6f8241..71ac80c4f0 100644 --- a/pkg/tools/benchmark/operator/router/option.go +++ b/pkg/tools/benchmark/operator/router/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/router/router.go b/pkg/tools/benchmark/operator/router/router.go index 505df5dce3..7fa9d582d0 100644 --- a/pkg/tools/benchmark/operator/router/router.go +++ b/pkg/tools/benchmark/operator/router/router.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/service/doc.go b/pkg/tools/benchmark/operator/service/doc.go index 4d4cfe1548..8f39410597 100644 --- a/pkg/tools/benchmark/operator/service/doc.go +++ b/pkg/tools/benchmark/operator/service/doc.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/service/operator.go b/pkg/tools/benchmark/operator/service/operator.go index 4c41078537..ef377b6227 100644 --- a/pkg/tools/benchmark/operator/service/operator.go +++ b/pkg/tools/benchmark/operator/service/operator.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/service/option.go b/pkg/tools/benchmark/operator/service/option.go index e1a426e480..6a4629ee6c 100644 --- a/pkg/tools/benchmark/operator/service/option.go +++ b/pkg/tools/benchmark/operator/service/option.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 diff --git a/pkg/tools/benchmark/operator/usecase/benchmarkd.go b/pkg/tools/benchmark/operator/usecase/benchmarkd.go index 6a8f2148de..918dc8ea9e 100644 --- a/pkg/tools/benchmark/operator/usecase/benchmarkd.go +++ b/pkg/tools/benchmark/operator/usecase/benchmarkd.go @@ -2,7 +2,7 @@ // Copyright (C) 2019-2023 vdaas.org vald team // // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. +// You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 From a00d5a977052af23f518e0314148f252b0aa4bb5 Mon Sep 17 00:00:00 2001 From: vankichi Date: Thu, 19 Oct 2023 11:41:01 +0900 Subject: [PATCH 11/11] :white_check_mark: add benchmark operator reconcile test Signed-off-by: vankichi --- Makefile.d/build.mk | 4 + go.mod | 22 +- go.sum | 26 +- internal/k8s/job/job.go | 3 + internal/test/mock/controller_runtime.go | 67 + pkg/tools/benchmark/job/service/insert.go | 7 + pkg/tools/benchmark/job/service/job.go | 2 + pkg/tools/benchmark/job/service/object.go | 6 + pkg/tools/benchmark/job/service/remove.go | 3 + pkg/tools/benchmark/job/service/search.go | 14 + pkg/tools/benchmark/job/service/update.go | 7 + pkg/tools/benchmark/job/service/upsert.go | 7 + .../benchmark/operator/service/operator.go | 25 +- .../operator/service/operator_test.go | 4509 +++++++++++++++++ 14 files changed, 4671 insertions(+), 31 deletions(-) create mode 100644 internal/test/mock/controller_runtime.go create mode 100644 pkg/tools/benchmark/operator/service/operator_test.go diff --git a/Makefile.d/build.mk b/Makefile.d/build.mk index 6489344d86..bbfa26a152 100644 --- a/Makefile.d/build.mk +++ b/Makefile.d/build.mk @@ -282,6 +282,10 @@ cmd/tools/benchmark/operator/operator: \ CGO_CXXFLAGS="-g -Ofast -march=native" \ CGO_FFLAGS="-g -Ofast -march=native" \ CGO_LDFLAGS="-g -Ofast -march=native" \ + GO111MODULE=on \ + GOPRIVATE=$(GOPRIVATE) \ + go build \ + --ldflags "-s -w \ -X '$(GOPKG)/internal/info.CGOEnabled=$${CGO_ENABLED}' \ -X '$(GOPKG)/internal/info.NGTVersion=$(NGT_VERSION)' \ -X '$(GOPKG)/internal/info.BuildCPUInfoFlags=$(CPU_INFO_FLAGS)' \ diff --git a/go.mod b/go.mod index 7345580581..ae78eb8263 100755 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ replace ( cloud.google.com/go/secretmanager => cloud.google.com/go/secretmanager v1.11.2 cloud.google.com/go/storage => cloud.google.com/go/storage v1.33.0 cloud.google.com/go/trace => cloud.google.com/go/trace v1.10.2 - code.cloudfoundry.org/bytefmt => code.cloudfoundry.org/bytefmt v0.0.0-20230612151507-41ef4d1f67a4 + code.cloudfoundry.org/bytefmt => code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b contrib.go.opencensus.io/exporter/aws => contrib.go.opencensus.io/exporter/aws v0.0.0-20230502192102-15967c811cec contrib.go.opencensus.io/exporter/prometheus => contrib.go.opencensus.io/exporter/prometheus v0.4.2 contrib.go.opencensus.io/integrations/ocsql => contrib.go.opencensus.io/integrations/ocsql v0.1.7 @@ -24,7 +24,7 @@ replace ( github.com/Azure/azure-sdk-for-go => github.com/Azure/azure-sdk-for-go v68.0.0+incompatible github.com/Azure/azure-sdk-for-go/sdk/azcore => github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity => github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 - github.com/Azure/azure-sdk-for-go/sdk/internal => github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 + github.com/Azure/azure-sdk-for-go/sdk/internal => github.com/Azure/azure-sdk-for-go/sdk/internal v1.4.0 github.com/Azure/go-amqp => github.com/Azure/go-amqp v1.0.2 github.com/Azure/go-autorest => github.com/Azure/go-autorest v14.2.1-0.20230905222633-df94ce56f001+incompatible github.com/Azure/go-autorest/autorest => github.com/Azure/go-autorest/autorest v0.11.30-0.20230905222633-df94ce56f001 @@ -36,14 +36,14 @@ replace ( github.com/Azure/go-autorest/tracing => github.com/Azure/go-autorest/tracing v0.6.1-0.20230905222633-df94ce56f001 github.com/BurntSushi/toml => github.com/BurntSushi/toml v1.3.2 github.com/DATA-DOG/go-sqlmock => github.com/DATA-DOG/go-sqlmock v1.5.0 - github.com/GoogleCloudPlatform/cloudsql-proxy => github.com/GoogleCloudPlatform/cloudsql-proxy v1.33.11 + github.com/GoogleCloudPlatform/cloudsql-proxy => github.com/GoogleCloudPlatform/cloudsql-proxy v1.33.12 github.com/Masterminds/semver/v3 => github.com/Masterminds/semver/v3 v3.2.1 github.com/ajstarks/deck => github.com/ajstarks/deck v0.0.0-20231012031509-f833e437b68a github.com/ajstarks/deck/generate => github.com/ajstarks/deck/generate v0.0.0-20231012031509-f833e437b68a github.com/ajstarks/svgo => github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b github.com/antihax/optional => github.com/antihax/optional v1.0.0 github.com/armon/go-socks5 => github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 - github.com/aws/aws-sdk-go => github.com/aws/aws-sdk-go v1.45.26 + github.com/aws/aws-sdk-go => github.com/aws/aws-sdk-go v1.45.28 github.com/aws/aws-sdk-go-v2 => github.com/aws/aws-sdk-go-v2 v1.21.2 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream => github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.14 github.com/aws/aws-sdk-go-v2/config => github.com/aws/aws-sdk-go-v2/config v1.19.0 @@ -207,7 +207,7 @@ replace ( github.com/lucasb-eyer/go-colorful => github.com/lucasb-eyer/go-colorful v1.2.0 github.com/mailru/easyjson => github.com/mailru/easyjson v0.7.7 github.com/mattn/go-colorable => github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty => github.com/mattn/go-isatty v0.0.19 + github.com/mattn/go-isatty => github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.14.17 github.com/matttproud/golang_protobuf_extensions => github.com/matttproud/golang_protobuf_extensions v1.0.4 github.com/mitchellh/colorstring => github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db @@ -236,7 +236,7 @@ replace ( github.com/prashantv/gostub => github.com/prashantv/gostub v1.1.0 github.com/prometheus/client_golang => github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_model => github.com/prometheus/client_model v0.5.0 - github.com/prometheus/common => github.com/prometheus/common v0.44.0 + github.com/prometheus/common => github.com/prometheus/common v0.45.0 github.com/prometheus/procfs => github.com/prometheus/procfs v0.12.0 github.com/prometheus/prometheus => github.com/prometheus/prometheus v1.99.0 github.com/quasilyte/go-ruleguard => github.com/quasilyte/go-ruleguard v0.4.0 @@ -316,7 +316,7 @@ replace ( google.golang.org/genproto => google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b google.golang.org/genproto/googleapis/api => google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b google.golang.org/genproto/googleapis/rpc => google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b - google.golang.org/grpc => google.golang.org/grpc v1.58.3 + google.golang.org/grpc => google.golang.org/grpc v1.59.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc => google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 google.golang.org/protobuf => google.golang.org/protobuf v1.31.0 gopkg.in/check.v1 => gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c @@ -394,9 +394,8 @@ require ( golang.org/x/sync v0.4.0 golang.org/x/sys v0.13.0 golang.org/x/text v0.13.0 - golang.org/x/tools v0.14.0 golang.org/x/time v0.3.0 - golang.org/x/tools v0.13.0 + golang.org/x/tools v0.14.0 gonum.org/v1/hdf5 v0.0.0-00010101000000-000000000000 gonum.org/v1/plot v0.10.1 google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a @@ -469,7 +468,7 @@ require ( github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -478,7 +477,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.15.1 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.1 // indirect @@ -500,7 +499,6 @@ require ( golang.org/x/image v0.13.0 // indirect golang.org/x/mod v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect - golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect google.golang.org/api v0.139.0 // indirect diff --git a/go.sum b/go.sum index daa65b4519..53ea96d659 100644 --- a/go.sum +++ b/go.sum @@ -133,8 +133,8 @@ cloud.google.com/go/vpcaccess v1.7.2/go.mod h1:mmg/MnRHv+3e8FJUjeSibVFvQF1cCy2Ms cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd/5uQIPBc= cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas= cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM= -code.cloudfoundry.org/bytefmt v0.0.0-20230612151507-41ef4d1f67a4 h1:9G5F8zgma5v0GdDvNz6iZwwJp3RS/z0SY/aHGfVwvTo= -code.cloudfoundry.org/bytefmt v0.0.0-20230612151507-41ef4d1f67a4/go.mod h1:wYHCXH/gI19ujoFVuMkY48qPpPCoHLKBKXPkn67h/Yc= +code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b h1:/2OEIBwZAaJ8n8iTXrM4v/+bdyLDTLwcW6RZtkO4+r0= +code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b/go.mod h1:CKNYSQxmKcMCNIKoRG5rRR4AIgJMIoK65ya+Z5xHnk4= dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20221208032759-85de2813cf6b/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA= @@ -177,8 +177,8 @@ github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/P github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go v1.45.26 h1:PJ2NJNY5N/yeobLYe1Y+xLdavBi67ZI8gvph6ftwVCg= -github.com/aws/aws-sdk-go v1.45.26/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.45.28 h1:p2ATcaK6ffSw4yZ2UAGzgRyRXwKyOJY6ZCiKqj5miJE= +github.com/aws/aws-sdk-go v1.45.28/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.14 h1:Sc82v7tDQ/vdU1WtuSyzZ1I7y/68j//HJ6uozND1IDs= @@ -217,6 +217,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 h1:0BkLfgeDjfZnZ+MhB3ONb01u9pwF github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= @@ -464,11 +466,11 @@ github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLO github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mandolyte/mdtopdf v1.3.2/go.mod h1:c28Ldk+tVc/y7QQcEcILStS/OFlerdXGGdBUzJQBgEo= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= @@ -517,8 +519,8 @@ github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1 github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quasilyte/go-ruleguard v0.4.0 h1:DyM6r+TKL+xbKB4Nm7Afd1IQh9kEUKQs2pboWGKtvQo= @@ -623,8 +625,6 @@ golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= -go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8= golang.org/x/exp/shiny v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= @@ -673,8 +673,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go. google.golang.org/genproto/googleapis/bytestream v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:itlFWGBbEyD32PUeJsTG8h8Wz7iJXfVK4gt1EJ+pAG0= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= diff --git a/internal/k8s/job/job.go b/internal/k8s/job/job.go index 76fda87522..5c00db89a0 100644 --- a/internal/k8s/job/job.go +++ b/internal/k8s/job/job.go @@ -48,6 +48,9 @@ type reconciler struct { // Job is a type alias for the k8s job definition. type Job = batchv1.Job +// JobStatus is a type alias for the k8s job status definition. +type JobStatus = batchv1.JobStatus + // New returns the JobWatcher that implements reconciliation loop, or any errors occurred. func New(opts ...Option) (JobWatcher, error) { r := &reconciler{ diff --git a/internal/test/mock/controller_runtime.go b/internal/test/mock/controller_runtime.go new file mode 100644 index 0000000000..30a3835416 --- /dev/null +++ b/internal/test/mock/controller_runtime.go @@ -0,0 +1,67 @@ +// Copyright (C) 2019-2023 vdaas.org vald team +// +// 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 +// +// https://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 mock + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type MockSubResourceWriter struct { + client.SubResourceWriter +} + +func (s *MockSubResourceWriter) Update(context.Context, client.Object, ...client.SubResourceUpdateOption) error { + return nil +} + +type MockClient struct { + client.Client +} + +func (*MockClient) Status() client.SubResourceWriter { + s := MockSubResourceWriter{ + SubResourceWriter: &MockSubResourceWriter{}, + } + return s.SubResourceWriter +} + +func (*MockClient) Get(context.Context, client.ObjectKey, client.Object, ...client.GetOption) error { + return nil +} + +func (*MockClient) Create(context.Context, client.Object, ...client.CreateOption) error { + return nil +} + +func (*MockClient) Delete(context.Context, client.Object, ...client.DeleteOption) error { + return nil +} + +func (*MockClient) DeleteAllOf(context.Context, client.Object, ...client.DeleteAllOfOption) error { + return nil +} + +type MockManager struct { + manager.Manager +} + +func (m *MockManager) GetClient() client.Client { + c := &MockClient{ + Client: &MockClient{}, + } + return c.Client +} diff --git a/pkg/tools/benchmark/job/service/insert.go b/pkg/tools/benchmark/job/service/insert.go index b38c3622b2..d654ecc1a4 100644 --- a/pkg/tools/benchmark/job/service/insert.go +++ b/pkg/tools/benchmark/job/service/insert.go @@ -56,6 +56,10 @@ func (j *job) insert(ctx context.Context, ech chan error) error { case ech <- err: } } + // loopCnt represents the quotient of iter divided by the len(vecs). + // This is to account for when iter exceeds len(vecs). + // It is used to calculate idx to determine which index of vecs to access. + // idx takes between <0, len(vecs)-1>. loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) idx := iter - 1 - (len(vecs) * int(loopCnt)) res, err := j.client.Insert(egctx, &payload.Insert_Request{ @@ -71,6 +75,9 @@ func (j *job) insert(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } // TODO: send metrics to the Prometeus diff --git a/pkg/tools/benchmark/job/service/job.go b/pkg/tools/benchmark/job/service/job.go index b047051ea1..299b114172 100644 --- a/pkg/tools/benchmark/job/service/job.go +++ b/pkg/tools/benchmark/job/service/job.go @@ -254,6 +254,7 @@ func (j *job) Start(ctx context.Context) (<-chan error, error) { cech, err := j.client.Start(ctx) if err != nil { log.Error("[benchmark job] failed to start connection monitor") + close(ech) return nil, err } j.eg.Go(func() error { @@ -280,6 +281,7 @@ func (j *job) Start(ctx context.Context) (<-chan error, error) { case ech <- err: } } + close(ech) if err := p.Signal(syscall.SIGTERM); err != nil { log.Error(err) } diff --git a/pkg/tools/benchmark/job/service/object.go b/pkg/tools/benchmark/job/service/object.go index 9284b50f95..91732f78c2 100644 --- a/pkg/tools/benchmark/job/service/object.go +++ b/pkg/tools/benchmark/job/service/object.go @@ -56,6 +56,9 @@ func (j *job) exists(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return nil default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } log.Debugf("[benchmark job] Finish exists: iter= %d \n%v\n", idx, res) @@ -115,6 +118,9 @@ func (j *job) getObject(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return nil default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } if res != nil { diff --git a/pkg/tools/benchmark/job/service/remove.go b/pkg/tools/benchmark/job/service/remove.go index c8a6650bc2..dbf58664aa 100644 --- a/pkg/tools/benchmark/job/service/remove.go +++ b/pkg/tools/benchmark/job/service/remove.go @@ -65,6 +65,9 @@ func (j *job) remove(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } log.Debugf("[benchmark job] Finish remove: iter= %d \n%v", idx, res) diff --git a/pkg/tools/benchmark/job/service/search.go b/pkg/tools/benchmark/job/service/search.go index 715b8f5356..fe5f81f9f4 100644 --- a/pkg/tools/benchmark/job/service/search.go +++ b/pkg/tools/benchmark/job/service/search.go @@ -65,6 +65,10 @@ func (j *job) search(ctx context.Context, ech chan error) error { case ech <- err: } } + // loopCnt represents the quotient of iter divided by the len(vecs). + // This is to account for when iter exceeds len(vecs). + // It is used to calculate idx to determine which index of vecs to access. + // idx takes between <0, len(vecs)-1>. loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) idx := iter - 1 - (len(vecs) * int(loopCnt)) if len(vecs[idx]) != j.dimension { @@ -81,6 +85,9 @@ func (j *job) search(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return nil default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } if res != nil && j.searchConfig.EnableLinearSearch { @@ -116,6 +123,10 @@ func (j *job) search(ctx context.Context, ech chan error) error { } } log.Debugf("[benchmark job] Start linear search: iter = %d", iter) + // loopCnt represents the quotient of iter divided by the len(vecs). + // This is to account for when iter exceeds len(vecs). + // It is used to calculate idx to determine which index of vecs to access. + // idx takes between <0, len(vecs)-1>. loopCnt := math.Floor(float64(i-1) / float64(len(vecs))) idx := iter - 1 - (len(vecs) * int(loopCnt)) if len(vecs[idx]) != j.dimension { @@ -132,6 +143,9 @@ func (j *job) search(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } if res != nil { diff --git a/pkg/tools/benchmark/job/service/update.go b/pkg/tools/benchmark/job/service/update.go index f641f84f57..06a95ebaa3 100644 --- a/pkg/tools/benchmark/job/service/update.go +++ b/pkg/tools/benchmark/job/service/update.go @@ -57,6 +57,10 @@ func (j *job) update(ctx context.Context, ech chan error) error { case ech <- err: } } + // loopCnt represents the quotient of iter divided by the len(vecs). + // This is to account for when iter exceeds len(vecs). + // It is used to calculate idx to determine which index of vecs to access. + // idx takes between <0, len(vecs)-1>. loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) idx := iter - 1 - (len(vecs) * int(loopCnt)) res, err := j.client.Update(egctx, &payload.Update_Request{ @@ -72,6 +76,9 @@ func (j *job) update(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } if res != nil { diff --git a/pkg/tools/benchmark/job/service/upsert.go b/pkg/tools/benchmark/job/service/upsert.go index 71a88cfda7..cf77479a79 100644 --- a/pkg/tools/benchmark/job/service/upsert.go +++ b/pkg/tools/benchmark/job/service/upsert.go @@ -57,6 +57,10 @@ func (j *job) upsert(ctx context.Context, ech chan error) error { case ech <- err: } } + // loopCnt represents the quotient of iter divided by the len(vecs). + // This is to account for when iter exceeds len(vecs). + // It is used to calculate idx to determine which index of vecs to access. + // idx takes between <0, len(vecs)-1>. loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) idx := iter - 1 - (len(vecs) * int(loopCnt)) res, err := j.client.Upsert(egctx, &payload.Upsert_Request{ @@ -72,6 +76,9 @@ func (j *job) upsert(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } if res != nil { diff --git a/pkg/tools/benchmark/operator/service/operator.go b/pkg/tools/benchmark/operator/service/operator.go index ef377b6227..867541fe21 100644 --- a/pkg/tools/benchmark/operator/service/operator.go +++ b/pkg/tools/benchmark/operator/service/operator.go @@ -57,9 +57,9 @@ type operator struct { jobNamespace string jobImage string jobImagePullPolicy string - scenarios atomic.Pointer[map[string]*scenario] - benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] - jobs atomic.Pointer[map[string]string] + scenarios *atomic.Pointer[map[string]*scenario] + benchjobs *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + jobs *atomic.Pointer[map[string]string] rcd time.Duration // reconcile check duration eg errgroup.Group ctrl k8s.Controller @@ -137,6 +137,10 @@ func (o *operator) initCtrl() error { } func (o *operator) getAtomicScenario() map[string]*scenario { + if o.scenarios == nil { + o.scenarios = &atomic.Pointer[map[string]*scenario]{} + return nil + } if v := o.scenarios.Load(); v != nil { return *(v) } @@ -144,6 +148,10 @@ func (o *operator) getAtomicScenario() map[string]*scenario { } func (o *operator) getAtomicBenchJob() map[string]*v1.ValdBenchmarkJob { + if o.benchjobs == nil { + o.benchjobs = &atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + return nil + } if v := o.benchjobs.Load(); v != nil { return *(v) } @@ -151,6 +159,10 @@ func (o *operator) getAtomicBenchJob() map[string]*v1.ValdBenchmarkJob { } func (o *operator) getAtomicJob() map[string]string { + if o.jobs == nil { + o.jobs = &atomic.Pointer[map[string]string]{} + return nil + } if v := o.jobs.Load(); v != nil { return *(v) } @@ -171,7 +183,7 @@ func (o *operator) jobReconcile(ctx context.Context, jobList map[string][]job.Jo if cjobs == nil { cjobs = map[string]string{} } - // jobStatus is used for update benchmark job resource status + // benchmarkJobStatus is used for update benchmark job resource status benchmarkJobStatus := make(map[string]v1.BenchmarkJobStatus) // jobNames is used for check whether cjobs has delted job. // If cjobs has the delted job, it will be remove the end of jobReconcile function. @@ -215,7 +227,7 @@ func (o *operator) jobReconcile(ctx context.Context, jobList map[string][]job.Jo log.Debug("[reconcile job] finish") } -// benchmarkJobReconcile gets the vald benchmark job resource list and create Job for running benchmark job. +// benchJobReconcile gets the vald benchmark job resource list and create Job for running benchmark job. func (o *operator) benchJobReconcile(ctx context.Context, benchJobList map[string]v1.ValdBenchmarkJob) { log.Debugf("[reconcile benchmark job resource] job list: %#v", benchJobList) if len(benchJobList) == 0 { @@ -228,6 +240,7 @@ func (o *operator) benchJobReconcile(ctx context.Context, benchJobList map[strin if cbjl == nil { cbjl = make(map[string]*v1.ValdBenchmarkJob, 0) } + // jobStatus is used for update benchmarkJob CR status if updating is needed. jobStatus := make(map[string]v1.BenchmarkJobStatus) for k := range benchJobList { // update scenario status @@ -475,7 +488,7 @@ func (o *operator) createJob(ctx context.Context, bjr v1.ValdBenchmarkJob) error benchjob.WithTTLSecondsAfterFinished(int32(bjr.Spec.TTLSecondsAfterFinished)), ) if err != nil { - return nil + return err } // create job c := o.ctrl.GetManager().GetClient() diff --git a/pkg/tools/benchmark/operator/service/operator_test.go b/pkg/tools/benchmark/operator/service/operator_test.go new file mode 100644 index 0000000000..f7b49866d4 --- /dev/null +++ b/pkg/tools/benchmark/operator/service/operator_test.go @@ -0,0 +1,4509 @@ +// Copyright (C) 2019-2023 vdaas.org vald team +// +// 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 +// +// https://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 service + +import ( + "context" + "reflect" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/vdaas/vald/internal/config" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/k8s" + "github.com/vdaas/vald/internal/k8s/job" + v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" + "github.com/vdaas/vald/internal/test/goleak" + "github.com/vdaas/vald/internal/test/mock" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// mockCtrl is used for mock the request to the Kubernetes API. +type mockCtrl struct { + StartFunc func(ctx context.Context) (<-chan error, error) + GetManagerFunc func() k8s.Manager +} + +func (m *mockCtrl) Start(ctx context.Context) (<-chan error, error) { + return m.StartFunc(ctx) +} + +func (m *mockCtrl) GetManager() k8s.Manager { + return m.GetManagerFunc() +} + +func Test_operator_getAtomicScenario(t *testing.T) { + type fields struct { + scenarios *atomic.Pointer[map[string]*scenario] + } + type want struct { + want map[string]*scenario + } + type test struct { + name string + fields fields + want want + checkFunc func(want, map[string]*scenario) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got map[string]*scenario) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + { + name: "get nil when atomic has no resource", + fields: fields{}, + want: want{ + want: nil, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + { + name: "get scenarios when scenario list is stored", + fields: fields{ + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scneario-insert": v1.BenchmarkJobAvailable, + "scneario-search": v1.BenchmarkJobAvailable, + }, + }, + }) + return &ap + }(), + }, + want: want{ + want: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scneario-insert": v1.BenchmarkJobAvailable, + "scneario-search": v1.BenchmarkJobAvailable, + }, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + scenarios: test.fields.scenarios, + } + + got := o.getAtomicScenario() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_getAtomicBenchJob(t *testing.T) { + type fields struct { + benchjobs *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + } + type want struct { + want map[string]*v1.ValdBenchmarkJob + } + type test struct { + name string + fields fields + want want + checkFunc func(want, map[string]*v1.ValdBenchmarkJob) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got map[string]*v1.ValdBenchmarkJob) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + { + name: "get nil when atomic has no resource", + fields: fields{}, + want: want{ + want: nil, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + { + name: "get benchjobs when job list is stored", + fields: fields{ + benchjobs: func() *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + "scenario-search": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + } + ap.Store(&m) + return &ap + }(), + }, + want: want{ + want: map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + "scenario-search": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + benchjobs: test.fields.benchjobs, + } + + got := o.getAtomicBenchJob() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_getAtomicJob(t *testing.T) { + type fields struct { + jobs *atomic.Pointer[map[string]string] + } + type want struct { + want map[string]string + } + type test struct { + name string + fields fields + want want + checkFunc func(want, map[string]string) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got map[string]string) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + { + name: "get nil when atomic has no resource", + fields: fields{}, + want: want{ + want: nil, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + { + name: "get jobs when jobs has resource", + fields: fields{ + jobs: func() *atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + m := map[string]string{ + "scenario-insert": "default", + "scenario-search": "default", + } + ap.Store(&m) + return &ap + }(), + }, + want: want{ + want: map[string]string{ + "scenario-insert": "default", + "scenario-search": "default", + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + jobs: test.fields.jobs, + } + + got := o.getAtomicJob() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_jobReconcile(t *testing.T) { + type args struct { + ctx context.Context + jobList map[string][]job.Job + } + type fields struct { + jobNamespace string + jobImage string + jobImagePullPolicy string + scenarios *atomic.Pointer[map[string]*scenario] + benchjobs *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + jobs *atomic.Pointer[map[string]string] + ctrl k8s.Controller + } + type want struct { + want map[string]string + } + type test struct { + name string + args args + fields fields + want want + checkFunc func(want, map[string]string) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, got map[string]string) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when the length of jobList is 0.", + args: args{ + ctx: ctx, + jobList: map[string][]job.Job{}, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: &atomic.Pointer[map[string]*scenario]{}, + benchjobs: &atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{}, + jobs: &atomic.Pointer[map[string]string]{}, + }, + want: want{ + want: map[string]string{}, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with new job whose namespace is same as jobNamespace and deleted job by etcd", + args: args{ + ctx: ctx, + jobList: map[string][]job.Job{ + "scenario-insert": { + { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + }, + Status: job.JobStatus{ + Active: 1, + }, + }, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: &atomic.Pointer[map[string]*scenario]{}, + jobs: func() *atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + m := map[string]string{ + "scenario-completed-insert": "default", + } + ap.Store(&m) + return &ap + }(), + benchjobs: func() *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + } + ap.Store(&m) + return &ap + }(), + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]string{ + "scenario-insert": "default", + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with completed job whose namespace is same as jobNamespace", + args: args{ + ctx: ctx, + jobList: map[string][]job.Job{ + "scenario-insert": { + { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-completed-insert", + Namespace: "default", + }, + Status: job.JobStatus{ + Active: 0, + Succeeded: 1, + CompletionTime: func() *metav1.Time { + t := &metav1.Time{ + Time: time.Now(), + } + return t + }(), + }, + }, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: &atomic.Pointer[map[string]*scenario]{}, + jobs: func() *atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + m := map[string]string{ + "scenario-completed-insert": "default", + } + ap.Store(&m) + return &ap + }(), + benchjobs: func() *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + } + ap.Store(&m) + return &ap + }(), + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]string{ + "scenario-completed-insert": "default", + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with job whose namespace is not same as jobNamespace", + args: args{ + ctx: ctx, + jobList: map[string][]job.Job{ + "scenario-insert": { + { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "benchmark", + }, + Status: job.JobStatus{ + Active: 1, + }, + }, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: &atomic.Pointer[map[string]*scenario]{}, + benchjobs: &atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{}, + jobs: &atomic.Pointer[map[string]string]{}, + ctrl: nil, + }, + want: want{ + want: map[string]string{}, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + jobNamespace: test.fields.jobNamespace, + jobImage: test.fields.jobImage, + jobImagePullPolicy: test.fields.jobImagePullPolicy, + benchjobs: test.fields.benchjobs, + jobs: test.fields.jobs, + ctrl: test.fields.ctrl, + } + + o.jobReconcile(test.args.ctx, test.args.jobList) + got := o.getAtomicJob() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_benchJobReconcile(t *testing.T) { + type args struct { + ctx context.Context + benchJobList map[string]v1.ValdBenchmarkJob + } + type fields struct { + jobNamespace string + jobImage string + jobImagePullPolicy string + scenarios *atomic.Pointer[map[string]*scenario] + benchjobs *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + jobs *atomic.Pointer[map[string]string] + ctrl k8s.Controller + } + type want struct { + scenarios map[string]*scenario + benchjobs map[string]*v1.ValdBenchmarkJob + } + type test struct { + name string + args args + fields fields + want want + checkFunc func(want, map[string]*scenario, map[string]*v1.ValdBenchmarkJob) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, gotS map[string]*scenario, gotJ map[string]*v1.ValdBenchmarkJob) error { + if !reflect.DeepEqual(w.scenarios, gotS) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotS, w.scenarios) + } + if !reflect.DeepEqual(w.benchjobs, gotJ) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotJ, w.benchjobs) + } + return nil + } + tests := []test{ + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when benchJobList is empty", + args: args{ + ctx: ctx, + benchJobList: map[string]v1.ValdBenchmarkJob{}, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: &atomic.Pointer[map[string]*scenario]{}, + benchjobs: &atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{}, + jobs: &atomic.Pointer[map[string]string]{}, + ctrl: nil, + }, + want: want{ + benchjobs: map[string]*v1.ValdBenchmarkJob{}, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when benchJobList has new benchmark Job with owner reference (reconcile after submitted scenario)", + args: args{ + ctx: ctx, + benchJobList: map[string]v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + }, + } + ap.Store(&m) + return &ap + }(), + benchjobs: &atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{}, + jobs: &atomic.Pointer[map[string]string]{}, + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + scenarios: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": "", + }, + }, + }, + benchjobs: map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when benchJobList has updated benchmark Job with owner reference (reconcile after updated scenario)", + args: args{ + ctx: ctx, + benchJobList: map[string]v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 2, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 2, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + }, + }, + } + ap.Store(&m) + return &ap + }(), + benchjobs: func() *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + } + ap.Store(&m) + return &ap + }(), + jobs: &atomic.Pointer[map[string]string]{}, + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + scenarios: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 2, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + }, + }, + }, + benchjobs: map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 2, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when benchJobList has updated benchmark Job with owner reference (reconcile after updated job status)", + args: args{ + ctx: ctx, + benchJobList: map[string]v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": "", + }, + }, + } + ap.Store(&m) + return &ap + }(), + benchjobs: func() *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: "", + }, + } + ap.Store(&m) + return &ap + }(), + jobs: &atomic.Pointer[map[string]string]{}, + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + scenarios: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + }, + }, + }, + benchjobs: map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when benchJobList has new benchmark Job with owner reference and benchJob has deleted job", + args: args{ + ctx: ctx, + benchJobList: map[string]v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": "", + }, + }, + } + ap.Store(&m) + return &ap + }(), + benchjobs: func() *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: "", + }, + "scenario-deleted-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-deleted-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario-deleted", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobCompleted, + }, + } + ap.Store(&m) + return &ap + }(), + jobs: &atomic.Pointer[map[string]string]{}, + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + scenarios: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + }, + }, + }, + benchjobs: map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + jobNamespace: test.fields.jobNamespace, + jobImage: test.fields.jobImage, + jobImagePullPolicy: test.fields.jobImagePullPolicy, + scenarios: test.fields.scenarios, + benchjobs: test.fields.benchjobs, + jobs: test.fields.jobs, + ctrl: test.fields.ctrl, + } + + o.benchJobReconcile(test.args.ctx, test.args.benchJobList) + gotS := o.getAtomicScenario() + gotJ := o.getAtomicBenchJob() + if err := checkFunc(test.want, gotS, gotJ); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_benchScenarioReconcile(t *testing.T) { + type args struct { + ctx context.Context + scenarioList map[string]v1.ValdBenchmarkScenario + } + type fields struct { + jobNamespace string + jobImage string + jobImagePullPolicy string + scenarios *atomic.Pointer[map[string]*scenario] + benchjobs *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + jobs *atomic.Pointer[map[string]string] + ctrl k8s.Controller + } + type want struct { + want map[string]*scenario + } + type test struct { + name string + args args + fields fields + want want + checkFunc func(want, map[string]*scenario) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, got map[string]*scenario) error { + if len(w.want) != len(got) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + for k, ws := range w.want { + gs, ok := got[k] + if !ok { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + // check CRD + if !reflect.DeepEqual(ws.Crd, gs.Crd) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + // check benchJobStatus + if len(ws.BenchJobStatus) != len(gs.BenchJobStatus) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + for k, v := range gs.BenchJobStatus { + sk := strings.Split(k, "-") + wk := strings.Join(sk[:len(sk)-1], "-") + if v != ws.BenchJobStatus[wk] { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + } + } + return nil + } + tests := []test{ + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with scenarioList is empty", + args: args{ + ctx: ctx, + scenarioList: map[string]v1.ValdBenchmarkScenario{}, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: &atomic.Pointer[map[string]*scenario]{}, + benchjobs: &atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{}, + jobs: &atomic.Pointer[map[string]string]{}, + ctrl: nil, + }, + want: want{ + want: map[string]*scenario{}, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with scenarioList has new scenario with no scenario has been applied yet.", + args: args{ + ctx: ctx, + scenarioList: map[string]v1.ValdBenchmarkScenario{ + "scenario": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: &atomic.Pointer[map[string]*scenario]{}, + benchjobs: &atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{}, + jobs: &atomic.Pointer[map[string]string]{}, + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobNotReady, + "scenario-search": v1.BenchmarkJobNotReady, + }, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with scenarioList has only status updated scenario.", + args: args{ + ctx: ctx, + scenarioList: map[string]v1.ValdBenchmarkScenario{ + "scenario": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert-1234567890": v1.BenchmarkJobNotReady, + "scenario-search-1234567891": v1.BenchmarkJobNotReady, + }, + }, + } + ap.Store(&m) + return &ap + }(), + benchjobs: &atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{}, + jobs: &atomic.Pointer[map[string]string]{}, + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobNotReady, + "scenario-search": v1.BenchmarkJobNotReady, + }, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with scenarioList has updated scenario when job is already running", + args: args{ + ctx: ctx, + scenarioList: map[string]v1.ValdBenchmarkScenario{ + "scenario": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 2, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + "scenario-search": v1.BenchmarkJobAvailable, + }, + }, + } + ap.Store(&m) + return &ap + }(), + benchjobs: &atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{}, + jobs: &atomic.Pointer[map[string]string]{}, + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 2, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobNotReady, + "scenario-search": v1.BenchmarkJobNotReady, + }, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with scenarioList has another scenario when scenario is already running", + args: args{ + ctx: ctx, + scenarioList: map[string]v1.ValdBenchmarkScenario{ + "scenario-v2": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-v2", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + "scenario-search": v1.BenchmarkJobAvailable, + }, + }, + } + ap.Store(&m) + return &ap + }(), + benchjobs: &atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{}, + jobs: &atomic.Pointer[map[string]string]{}, + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]*scenario{ + "scenario-v2": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-v2", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-v2-insert": v1.BenchmarkJobNotReady, + "scenario-v2-search": v1.BenchmarkJobNotReady, + }, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + jobNamespace: test.fields.jobNamespace, + jobImage: test.fields.jobImage, + jobImagePullPolicy: test.fields.jobImagePullPolicy, + scenarios: test.fields.scenarios, + benchjobs: test.fields.benchjobs, + jobs: test.fields.jobs, + ctrl: test.fields.ctrl, + } + + o.benchScenarioReconcile(test.args.ctx, test.args.scenarioList) + got := o.getAtomicScenario() + t.Log(got["scenario"]) + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_checkAtomics(t *testing.T) { + type fields struct { + scenarios *atomic.Pointer[map[string]*scenario] + benchjobs *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + jobs *atomic.Pointer[map[string]string] + } + type want struct { + err error + } + type test struct { + name string + fields fields + want want + checkFunc func(want, error) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + return nil + } + defaultScenarioMap := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + { + JobType: "update", + UpdateConfig: &config.UpdateConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobCompleted, + "scenario-search": v1.BenchmarkJobAvailable, + "scenario-update": v1.BenchmarkJobAvailable, + }, + }, + } + defaultBenchJobMap := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Kind: ScenarioKind, + Name: "scenario", + }, + }, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobCompleted, + }, + "scenario-search": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-search", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Kind: ScenarioKind, + Name: "scenario", + }, + }, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + "scenario-update": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-update", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Kind: ScenarioKind, + Name: "scenario", + }, + }, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "update", + UpdateConfig: &config.UpdateConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + } + defaultJobMap := map[string]string{ + "scenario-insert": "default", + "scenario-search": "default", + "scenario-update": "default", + } + tests := []test{ + func() test { + return test{ + name: "return nil with no mismatch atmoics", + fields: fields{ + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&defaultScenarioMap) + return &ap + }(), + benchjobs: func() *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + ap.Store(&defaultBenchJobMap) + return &ap + }(), + jobs: func() *atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + ap.Store(&defaultJobMap) + return &ap + }(), + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + } + }(), + func() test { + return test{ + name: "return mismatch error when scneario and job has atomic and benchJob has no atomic", + fields: fields{ + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&defaultScenarioMap) + return &ap + }(), + jobs: func() *atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + ap.Store(&defaultJobMap) + return &ap + }(), + }, + want: want{ + err: errors.ErrMismatchBenchmarkAtomics(defaultJobMap, map[string]*v1.ValdBenchmarkJob{}, defaultScenarioMap), + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + } + }(), + func() test { + benchJobMap := map[string]*v1.ValdBenchmarkJob{} + for k, v := range defaultBenchJobMap { + val := v1.ValdBenchmarkJob{} + val = *v + benchJobMap[k] = &val + } + benchJobMap["scenario-search"].SetNamespace("benchmark") + return test{ + name: "return mismatch error when benchJob with different namespace", + fields: fields{ + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&defaultScenarioMap) + return &ap + }(), + benchjobs: func() *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + ap.Store(&benchJobMap) + return &ap + }(), + jobs: func() *atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + ap.Store(&defaultJobMap) + return &ap + }(), + }, + want: want{ + err: errors.ErrMismatchBenchmarkAtomics(defaultJobMap, benchJobMap, defaultScenarioMap), + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + } + }(), + func() test { + benchJobMap := map[string]*v1.ValdBenchmarkJob{} + for k, v := range defaultBenchJobMap { + val := v1.ValdBenchmarkJob{} + val = *v + benchJobMap[k] = &val + } + benchJobMap["scenario-search"].Status = v1.BenchmarkJobNotReady + return test{ + name: "return mismatch error when status is not same between benchJob and scenario.BenchJobStatus", + fields: fields{ + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&defaultScenarioMap) + return &ap + }(), + benchjobs: func() *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + ap.Store(&benchJobMap) + return &ap + }(), + jobs: func() *atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + ap.Store(&defaultJobMap) + return &ap + }(), + }, + want: want{ + err: errors.ErrMismatchBenchmarkAtomics(defaultJobMap, benchJobMap, defaultScenarioMap), + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + } + }(), + func() test { + benchJobMap := map[string]*v1.ValdBenchmarkJob{} + for k, v := range defaultBenchJobMap { + val := v1.ValdBenchmarkJob{} + val = *v + benchJobMap[k] = &val + } + ors := []metav1.OwnerReference{} + for _, v := range benchJobMap["scenario-search"].OwnerReferences { + or := v.DeepCopy() + or.Name = "incorrectName" + ors = append(ors, *or) + } + benchJobMap["scenario-search"].OwnerReferences = ors + return test{ + name: "return mismatch error when scenario does not have key of bench job owners scenario", + fields: fields{ + scenarios: func() *atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&defaultScenarioMap) + return &ap + }(), + benchjobs: func() *atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + ap.Store(&benchJobMap) + return &ap + }(), + jobs: func() *atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + ap.Store(&defaultJobMap) + return &ap + }(), + }, + want: want{ + err: errors.ErrMismatchBenchmarkAtomics(defaultJobMap, benchJobMap, defaultScenarioMap), + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + } + }(), + } + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + scenarios: test.fields.scenarios, + benchjobs: test.fields.benchjobs, + jobs: test.fields.jobs, + } + + err := o.checkAtomics() + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +// NOT IMPLEMENTED BELOW +// func TestNew(t *testing.T) { +// type args struct { +// opts []Option +// } +// type want struct { +// want Operator +// err error +// } +// type test struct { +// name string +// args args +// want want +// checkFunc func(want, Operator, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, got Operator, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// if !reflect.DeepEqual(got, w.want) { +// return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// // { +// // name: "test_case_1", +// // args: args{ +// // opts: nil, +// // }, +// // want: want{ +// // want: func() Operator { +// // o := &operator{ +// // jobNamespace: "default", +// // jobImage: "vdaas/vald-benchmark-job", +// // jobImagePullPolicy: "Always", +// // rcd: 10 * time.Second, +// // } +// // return o +// // }(), +// // }, +// // checkFunc: defaultCheckFunc, +// // beforeFunc: func(t *testing.T, args args) { +// // t.Helper() +// // }, +// // afterFunc: func(t *testing.T, args args) { +// // t.Helper() +// // }, +// // }, +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// opts:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// +// got, err := New(test.args.opts...) +// if err := checkFunc(test.want, got, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } + +// +// func Test_operator_PreStart(t *testing.T) { +// type args struct { +// ctx context.Context +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.PreStart(test.args.ctx) +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// +// }) +// } +// } +// +// func Test_operator_Start(t *testing.T) { +// type args struct { +// ctx context.Context +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// want <-chan error +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, <-chan error, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, got <-chan error, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// if !reflect.DeepEqual(got, w.want) { +// return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// got, err := o.Start(test.args.ctx) +// if err := checkFunc(test.want, got, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// +// }) +// } +// } + +// func Test_operator_initCtrl(t *testing.T) { +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T) +// afterFunc func(*testing.T) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// +// tests := []test{ +// // { +// // name: "test_case_1", +// // fields: fields{ +// // jobNamespace: "default", +// // jobImage: "vdaas/vald-benchmark-job", +// // jobImagePullPolicy: "Always", +// // // scenarios:nil, +// // // benchjobs:nil, +// // // jobs:nil, +// // // rcd:nil, +// // eg: nil, +// // ctrl: nil, +// // }, +// // want: want{ +// // err: errors.New("hoge"), +// // }, +// // checkFunc: defaultCheckFunc, +// // beforeFunc: func(t *testing.T) { +// // t.Helper() +// // }, +// // afterFunc: func(t *testing.T) { +// // t.Helper() +// // }, +// // }, +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T,) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T,) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.initCtrl() +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } + +// func Test_operator_deleteBenchmarkJob(t *testing.T) { +// type args struct { +// ctx context.Context +// name string +// generation int64 +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// name:"", +// generation:0, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// name:"", +// generation:0, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.deleteBenchmarkJob(test.args.ctx, test.args.name, test.args.generation) +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_deleteJob(t *testing.T) { +// type args struct { +// ctx context.Context +// name string +// generation int64 +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// name:"", +// generation:0, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// name:"", +// generation:0, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.deleteJob(test.args.ctx, test.args.name, test.args.generation) +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_createBenchmarkJob(t *testing.T) { +// type args struct { +// ctx context.Context +// scenario v1.ValdBenchmarkScenario +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// want []string +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, []string, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, got []string, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// if !reflect.DeepEqual(got, w.want) { +// return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// scenario:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// scenario:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// got, err := o.createBenchmarkJob(test.args.ctx, test.args.scenario) +// if err := checkFunc(test.want, got, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_createJob(t *testing.T) { +// type args struct { +// ctx context.Context +// bjr v1.ValdBenchmarkJob +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// bjr:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// bjr:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.createJob(test.args.ctx, test.args.bjr) +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_updateBenchmarkScenarioStatus(t *testing.T) { +// type args struct { +// ctx context.Context +// ss map[string]v1.ValdBenchmarkScenarioStatus +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// want []string +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, []string, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, got []string, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// if !reflect.DeepEqual(got, w.want) { +// return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// ss:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// ss:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// got, err := o.updateBenchmarkScenarioStatus(test.args.ctx, test.args.ss) +// if err := checkFunc(test.want, got, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_updateBenchmarkJobStatus(t *testing.T) { +// type args struct { +// ctx context.Context +// js map[string]v1.BenchmarkJobStatus +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// want []string +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, []string, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, got []string, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// if !reflect.DeepEqual(got, w.want) { +// return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// js:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// js:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// got, err := o.updateBenchmarkJobStatus(test.args.ctx, test.args.js) +// if err := checkFunc(test.want, got, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_checkJobsStatus(t *testing.T) { +// type args struct { +// ctx context.Context +// jobs map[string]string +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// tests := []test{ +// // func() test { +// // return test{ +// // name: "test_case_2", +// // args: args{ +// // ctx: nil, +// // jobs: nil, +// // }, +// // fields: fields{ +// // jobNamespace: "", +// // jobImage: "", +// // jobImagePullPolicy: "", +// // scenarios: nil, +// // benchjobs: nil, +// // jobs: nil, +// // rcd: nil, +// // eg: nil, +// // ctrl: nil, +// // }, +// // want: want{}, +// // checkFunc: defaultCheckFunc, +// // beforeFunc: func(t *testing.T, args args) { +// // t.Helper() +// // }, +// // afterFunc: func(t *testing.T, args args) { +// // t.Helper() +// // }, +// // } +// // }(), +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.checkJobsStatus(test.args.ctx, test.args.jobs) +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// }