Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd: add --clients and --content-type flags to runner command #16

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ bin/
# vendor/

# Go workspace file
go.work
go.work
20 changes: 20 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM golang:1.20 AS build-stage

WORKDIR /gomod
COPY go.mod go.sum ./
RUN go mod download

RUN mkdir -p /output

WORKDIR /kperf-build
RUN --mount=source=./,target=/kperf-build,rw make build && PREFIX=/output make install

FROM gcr.io/distroless/static-debian12:nonroot AS release-stage

WORKDIR /

COPY --from=build-stage /output/bin/kperf /kperf

USER nonroot:nonroot

ENTRYPOINT ["/kperf"]
24 changes: 23 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
COMMANDS=kperf

# PREFIX is base path to install.
PREFIX ?= /usr/local

GO_BUILDTAGS = -tags "osusergo netgo static_build"

# IMAGE_REPO is default repo for image-build recipe.
IMAGE_REPO ?= localhost:5000
IMAGE_TAG ?= latest
IMAGE_NAME = $(IMAGE_REPO)/kperf:$(IMAGE_TAG)

BINARIES=$(addprefix bin/,$(COMMANDS))

# default recipe is build
Expand All @@ -9,11 +19,23 @@ BINARIES=$(addprefix bin/,$(COMMANDS))
ALWAYS:

bin/%: cmd/% ALWAYS
@go build -o $@ ./$<
@GO_ENABLED=0 go build -o $@ ${GO_BUILDTAGS} ./$<

build: $(BINARIES) ## build binaries
@echo "$@"

install: ## install binaries
@install -d $(PREFIX)/bin
@install $(BINARIES) $(PREFIX)/bin

image-build: ## build image
@echo building ${IMAGE_NAME}
@docker build . -t ${IMAGE_NAME}

image-push: image-build ## push image
@echo pushing ${IMAGE_NAME}
@docker push ${IMAGE_NAME}

test: ## run test
@go test -v ./...

Expand Down
137 changes: 125 additions & 12 deletions api/types/load_traffic.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package types

import "fmt"

// LoadProfile defines how to create load traffic from one host to kube-apiserver.
type LoadProfile struct {
// Version defines the version of this object.
Expand All @@ -18,17 +20,21 @@ type LoadProfileSpec struct {
Total int `json:"total" yaml:"total"`
// Conns defines total number of long connections used for traffic.
Conns int `json:"conns" yaml:"conns"`
// Client defines total number of HTTP clients.
Client int `json:"client" yaml:"client"`
// Requests defines the different kinds of requests with weights.
// The executor should randomly pick by weight.
Requests []*WeightedRequest
}

// KubeTypeMeta represents metadata of kubernetes object.
type KubeTypeMeta struct {
// Kind is a string value representing the REST resource the object represents.
Kind string `json:"kind" yaml:"kind"`
// APIVersion defines the versioned schema of the representation of an object.
APIVersion string `json:"apiVersion" yaml:"apiVersion"`
// KubeGroupVersionResource identifies the resource URI.
type KubeGroupVersionResource struct {
// Group is the name about a collection of related functionality.
Group string `json:"group" yaml:"group"`
// Version is a version of that group.
Version string `json:"version" yaml:"version"`
// Resource is a type in that versioned group APIs.
Resource string `json:"resource" yaml:"resource"`
}

// WeightedRequest represents request with weight.
Expand All @@ -50,8 +56,8 @@ type WeightedRequest struct {

// RequestGet defines GET request for target object.
type RequestGet struct {
// KubeTypeMeta represents object's resource type.
KubeTypeMeta `yaml:",inline"`
// KubeGroupVersionResource identifies the resource URI.
KubeGroupVersionResource `yaml:",inline"`
// Namespace is object's namespace.
Namespace string `json:"namespace" yaml:"namespace"`
// Name is object's name.
Expand All @@ -60,8 +66,8 @@ type RequestGet struct {

// RequestList defines LIST request for target objects.
type RequestList struct {
// KubeTypeMeta represents object's resource type.
KubeTypeMeta `yaml:",inline"`
// KubeGroupVersionResource identifies the resource URI.
KubeGroupVersionResource `yaml:",inline"`
// Namespace is object's namespace.
Namespace string `json:"namespace" yaml:"namespace"`
// Limit defines the page size.
Expand All @@ -72,14 +78,14 @@ type RequestList struct {

// RequestPut defines PUT request for target resource type.
type RequestPut struct {
// KubeTypeMeta represents object's resource type.
// KubeGroupVersionResource identifies the resource URI.
//
// NOTE: Currently, it should be configmap or secrets because we can
// generate random bytes as blob for it. However, for the pod resource,
// we need to ensure a lot of things are ready, for instance, volumes,
// resource capacity. It's not easy to generate it randomly. Maybe we
// can introduce pod template in the future.
KubeTypeMeta `yaml:",inline"`
KubeGroupVersionResource `yaml:",inline"`
// Namespace is object's namespace.
Namespace string `json:"namespace" yaml:"namespace"`
// Name is object's prefix name.
Expand All @@ -89,3 +95,110 @@ type RequestPut struct {
// ValueSize is the object's size in bytes.
ValueSize int `json:"valueSize" yaml:"valueSize"`
}

// Validate verifies fields of LoadProfile.
func (lp LoadProfile) Validate() error {
if lp.Version != 1 {
return fmt.Errorf("version should be 1")
}
return lp.Spec.Validate()
}

// Validate verifies fields of LoadProfileSpec.
func (spec LoadProfileSpec) Validate() error {
if spec.Conns <= 0 {
return fmt.Errorf("conns requires > 0: %v", spec.Conns)
}

if spec.Rate < 0 {
return fmt.Errorf("rate requires >= 0: %v", spec.Rate)
}

if spec.Total <= 0 {
return fmt.Errorf("total requires > 0: %v", spec.Total)
}

for idx, req := range spec.Requests {
if err := req.Validate(); err != nil {
return fmt.Errorf("idx: %v request: %v", idx, err)
}
}
return nil
}

// Validate verifies fields of WeightedRequest.
func (r WeightedRequest) Validate() error {
if r.Shares < 0 {
return fmt.Errorf("shares(%v) requires >= 0", r.Shares)
}

switch {
case r.StaleList != nil:
return r.StaleList.Validate()
case r.QuorumList != nil:
return r.QuorumList.Validate()
case r.StaleGet != nil:
return r.StaleGet.Validate()
case r.QuorumGet != nil:
return r.QuorumGet.Validate()
case r.Put != nil:
return r.Put.Validate()
default:
return fmt.Errorf("empty request value")
}
}

// RequestList validates RequestList type.
func (r *RequestList) Validate() error {
if err := r.KubeGroupVersionResource.Validate(); err != nil {
return fmt.Errorf("kube metadata: %v", err)
}

if r.Limit < 0 {
return fmt.Errorf("limit must >= 0")
}
return nil
}

// Validate validates RequestGet type.
func (r *RequestGet) Validate() error {
if err := r.KubeGroupVersionResource.Validate(); err != nil {
return fmt.Errorf("kube metadata: %v", err)
}

if r.Name == "" {
return fmt.Errorf("name is required")
}
return nil
}

// Validate validates RequestPut type.
func (r *RequestPut) Validate() error {
if err := r.KubeGroupVersionResource.Validate(); err != nil {
return fmt.Errorf("kube metadata: %v", err)
}

// TODO: check resource type
if r.Name == "" {
return fmt.Errorf("name pattern is required")
}
if r.KeySpaceSize <= 0 {
return fmt.Errorf("keySpaceSize must > 0")
}
if r.ValueSize <= 0 {
return fmt.Errorf("valueSize must > 0")
}
return nil
}

// Validate validates KubeGroupVersionResource.
func (m *KubeGroupVersionResource) Validate() error {
if m.Version == "" {
return fmt.Errorf("version is required")
}

if m.Resource == "" {
return fmt.Errorf("resource is required")
}
return nil
}
Loading