-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Wei Fu <weifu@microsoft.com>
- Loading branch information
Showing
12 changed files
with
464 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
linters: | ||
enable: | ||
- gofmt | ||
- goimports | ||
- gosec | ||
- ineffassign | ||
- misspell | ||
- nolintlint | ||
- revive | ||
- staticcheck | ||
- unconvert | ||
- unused | ||
- vet | ||
- errcheck |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
COMMANDS=kperf | ||
|
||
BINARIES=$(addprefix bin/,$(COMMANDS)) | ||
|
||
# default recipe is build | ||
.DEFAULT_GOAL := build | ||
|
||
# Always build | ||
ALWAYS: | ||
|
||
bin/%: cmd/% ALWAYS | ||
@go build -o $@ ./$< | ||
|
||
build: $(BINARIES) ## build binaries | ||
@echo "$@" | ||
|
||
test: ## run test | ||
@go test -v ./... | ||
|
||
lint: ## run lint | ||
@golangci-lint run --config .golangci.yml | ||
|
||
.PHONY: clean | ||
clean: ## clean up binaries | ||
@rm -f $(BINARIES) | ||
|
||
.PHONY: help | ||
help: ## this help | ||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-36s\033[0m%s\n", $$1, $$2}' $(MAKEFILE_LIST) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package types | ||
|
||
// LoadProfile defines how to create load traffic from one host to kube-apiserver. | ||
type LoadProfile struct { | ||
// Version defines the version of this object. | ||
Version int `json:"version" yaml:"version"` | ||
// Description is a string value to describe this object. | ||
Description string `json:"description,omitempty" yaml:"description"` | ||
// Spec defines behavior of load profile. | ||
Spec LoadProfileSpec `json:"spec" yaml:"spec"` | ||
} | ||
|
||
// LoadProfileSpec defines the load traffic for traget resource. | ||
type LoadProfileSpec struct { | ||
// Rate defines the maximum requests per second (zero is no limit). | ||
Rate int `json:"rate" yaml:"rate"` | ||
// Total defines the total number of requests. | ||
Total int `json:"total" yaml:"total"` | ||
// Conns defines total number of long connections used for traffic. | ||
Conns int `json:"conns" yaml:"conns"` | ||
// 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"` | ||
} | ||
|
||
// WeightedRequest represents request with weight. | ||
// Only one of request types may be specified. | ||
type WeightedRequest struct { | ||
// Shares defines weight in the same group. | ||
Shares int `json:"shares" yaml:"shares"` | ||
// StaleList means this list request with zero resource version. | ||
StaleList *RequestList `json:"staleList" yaml:"staleList"` | ||
// QuorumList means this list request without kube-apiserver cache. | ||
QuorumList *RequestList `json:"quorumList" yaml:"quorumList"` | ||
// StaleGet means this get request with zero resource version. | ||
StaleGet *RequestGet `json:"staleGet" yaml:"staleGet"` | ||
// QuorumGet means this get request without kube-apiserver cache. | ||
QuorumGet *RequestGet `json:"quorumGet" yaml:"quorumGet"` | ||
// Put means this is mutating request. | ||
Put *RequestPut `json:"put" yaml:"put"` | ||
} | ||
|
||
// RequestGet defines GET request for target object. | ||
type RequestGet struct { | ||
// KubeTypeMeta represents object's resource type. | ||
KubeTypeMeta `yaml:",inline"` | ||
// Namespace is object's namespace. | ||
Namespace string `json:"namespace" yaml:"namespace"` | ||
// Name is object's name. | ||
Name string `json:"name" yaml:"name"` | ||
} | ||
|
||
// RequestList defines LIST request for target objects. | ||
type RequestList struct { | ||
// KubeTypeMeta represents object's resource type. | ||
KubeTypeMeta `yaml:",inline"` | ||
// Namespace is object's namespace. | ||
Namespace string `json:"namespace" yaml:"namespace"` | ||
// Limit defines the page size. | ||
Limit int `json:"limit" yaml:"limit"` | ||
// Selector defines how to identify a set of objects. | ||
Selector string `json:"seletor" yaml:"seletor"` | ||
} | ||
|
||
// RequestPut defines PUT request for target resource type. | ||
type RequestPut struct { | ||
// KubeTypeMeta represents object's resource type. | ||
// | ||
// 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"` | ||
// Namespace is object's namespace. | ||
Namespace string `json:"namespace" yaml:"namespace"` | ||
// Name is object's prefix name. | ||
Name string `json:"name" yaml:"name"` | ||
// KeySpaceSize is used to generate random number as name's suffix. | ||
KeySpaceSize int `json:"keySpaceSize" yaml:"keySpaceSize"` | ||
// ValueSize is the object's size in bytes. | ||
ValueSize int `json:"valueSize" yaml:"valueSize"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package types | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
func TestLoadProfileUnmarshalFromYAML(t *testing.T) { | ||
in := ` | ||
version: 1 | ||
description: test | ||
spec: | ||
rate: 100 | ||
total: 10000 | ||
conns: 2 | ||
requests: | ||
- staleGet: | ||
kind: pods | ||
apiVersion: v1 | ||
namespace: default | ||
name: x1 | ||
shares: 100 | ||
- quorumGet: | ||
kind: configmap | ||
apiVersion: v1 | ||
namespace: default | ||
name: x2 | ||
shares: 150 | ||
- staleList: | ||
kind: pods | ||
apiVersion: v1 | ||
namespace: default | ||
limit: 10000 | ||
seletor: app=x2 | ||
shares: 200 | ||
- quorumList: | ||
kind: configmap | ||
apiVersion: v1 | ||
namespace: default | ||
limit: 10000 | ||
seletor: app=x3 | ||
shares: 400 | ||
- put: | ||
kind: configmap | ||
apiVersion: v1 | ||
namespace: kperf | ||
name: kperf- | ||
keySpaceSize: 1000 | ||
valueSize: 1024 | ||
shares: 1000 | ||
` | ||
|
||
target := LoadProfile{} | ||
require.NoError(t, yaml.Unmarshal([]byte(in), &target)) | ||
assert.Equal(t, 1, target.Version) | ||
assert.Equal(t, "test", target.Description) | ||
assert.Equal(t, 100, target.Spec.Rate) | ||
assert.Equal(t, 10000, target.Spec.Total) | ||
assert.Equal(t, 2, target.Spec.Conns) | ||
assert.Len(t, target.Spec.Requests, 5) | ||
|
||
assert.Equal(t, 100, target.Spec.Requests[0].Shares) | ||
assert.NotNil(t, target.Spec.Requests[0].StaleGet) | ||
assert.Equal(t, "pods", target.Spec.Requests[0].StaleGet.Kind) | ||
assert.Equal(t, "v1", target.Spec.Requests[0].StaleGet.APIVersion) | ||
assert.Equal(t, "default", target.Spec.Requests[0].StaleGet.Namespace) | ||
assert.Equal(t, "x1", target.Spec.Requests[0].StaleGet.Name) | ||
|
||
assert.NotNil(t, target.Spec.Requests[1].QuorumGet) | ||
assert.Equal(t, 150, target.Spec.Requests[1].Shares) | ||
|
||
assert.Equal(t, 200, target.Spec.Requests[2].Shares) | ||
assert.NotNil(t, target.Spec.Requests[2].StaleList) | ||
assert.Equal(t, "pods", target.Spec.Requests[2].StaleList.Kind) | ||
assert.Equal(t, "v1", target.Spec.Requests[2].StaleList.APIVersion) | ||
assert.Equal(t, "default", target.Spec.Requests[2].StaleList.Namespace) | ||
assert.Equal(t, 10000, target.Spec.Requests[2].StaleList.Limit) | ||
assert.Equal(t, "app=x2", target.Spec.Requests[2].StaleList.Selector) | ||
|
||
assert.NotNil(t, target.Spec.Requests[3].QuorumList) | ||
assert.Equal(t, 400, target.Spec.Requests[3].Shares) | ||
|
||
assert.Equal(t, 1000, target.Spec.Requests[4].Shares) | ||
assert.NotNil(t, target.Spec.Requests[4].Put) | ||
assert.Equal(t, "configmap", target.Spec.Requests[4].Put.Kind) | ||
assert.Equal(t, "v1", target.Spec.Requests[4].Put.APIVersion) | ||
assert.Equal(t, "kperf", target.Spec.Requests[4].Put.Namespace) | ||
assert.Equal(t, "kperf-", target.Spec.Requests[4].Put.Name) | ||
assert.Equal(t, 1000, target.Spec.Requests[4].Put.KeySpaceSize) | ||
assert.Equal(t, 1024, target.Spec.Requests[4].Put.ValueSize) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package multirunners | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/urfave/cli" | ||
) | ||
|
||
// Command represents multirunners sub-command. | ||
// | ||
// Subcommand multirunners is to deploy multiple runners as kubernetes jobs. | ||
// Since one runner could run out of networking bandwidth on one host, the | ||
// multirunners deploys runners on different hosts and reduces the impact of | ||
// limited networking resource. | ||
// | ||
// Command line interface: | ||
// | ||
// kperf mrunners run --help | ||
// | ||
// Options: | ||
// | ||
// --kubeconfig PATH (default: empty_string, use token if it's empty) | ||
// --namespace STRING (default: empty_string, required) | ||
// --runner-image STRING (default: empty_string, required) | ||
// --runners []STRING (default: empty, required) | ||
// --wait BOOLEAN (default: false) | ||
// | ||
// Details: | ||
// | ||
// The --runners format is defined by URI. | ||
// | ||
// - file:///abs_path?numbers=10 | ||
// - configmap:///namespace/name?numbers=2 | ||
// - ... | ||
// | ||
// The schema:://PATH is used to get runner's configuration. It can be local | ||
// path or stored as configmap in target kubernetes cluster. The query part is | ||
// to define what the job looks like. Currently, that command just requires | ||
// the number of pods in that job. At the beginning, we just need to file://. | ||
// The number of runners defines the number of jobs. | ||
// | ||
// All the jobs are referenced by one configmap (ownerReference). The configmap | ||
// name will be output to stdout. The name is progress tracker ID. By default, | ||
// there is only one progress tracker ID in one namespace. | ||
// | ||
// kperf mrunners wait --help | ||
// | ||
// Args: | ||
// | ||
// 0: namespace (STRING) | ||
// | ||
// Options: | ||
// | ||
// --kubeconfig PATH (default: empty_string, use token if it's empty) | ||
// | ||
// Wait it to wait until jobs finish. | ||
// | ||
// kperf mrunners result --help | ||
// | ||
// Args: | ||
// | ||
// 0: namespace (STRING) | ||
// | ||
// Options: | ||
// | ||
// --kubeconfig PATH (default: empty_string, use token if it's empty) | ||
// | ||
// Result retrieves the result for jobs. If jobs is still running, that command | ||
// will fail. | ||
var Command = cli.Command{ | ||
Name: "multirunners", | ||
ShortName: "mrunners", | ||
Usage: "packages runner as job and deploy runners into kubernetes", | ||
Subcommands: []cli.Command{ | ||
runCommand, | ||
waitCommand, | ||
resultCommand, | ||
}, | ||
} | ||
|
||
var runCommand = cli.Command{ | ||
Name: "run", | ||
Flags: []cli.Flag{}, | ||
Action: func(cliCtx *cli.Context) error { | ||
// 1. Parse options | ||
// 2. Deploy jobs for --runners | ||
// 3. Wait | ||
return fmt.Errorf("run - not implemented") | ||
}, | ||
} | ||
|
||
var waitCommand = cli.Command{ | ||
Name: "wait", | ||
Usage: "wait until jobs finish", | ||
Flags: []cli.Flag{}, | ||
Action: func(cliCtx *cli.Context) error { | ||
// 1. Check the progress tracker name | ||
// 2. Wait for the jobs | ||
return fmt.Errorf("wait - not implemented") | ||
}, | ||
} | ||
|
||
var resultCommand = cli.Command{ | ||
Name: "result", | ||
Usage: "show the result", | ||
Flags: []cli.Flag{}, | ||
Action: func(cliCtx *cli.Context) error { | ||
// 1. Check the progress tracker name | ||
// 2. Ensure the jobs finished | ||
// 3. Output the result | ||
return fmt.Errorf("result - not implemented") | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package commands | ||
|
||
import ( | ||
"github.com/Azure/kperf/cmd/kperf/commands/multirunners" | ||
"github.com/Azure/kperf/cmd/kperf/commands/runner" | ||
|
||
"github.com/urfave/cli" | ||
) | ||
|
||
// App returns kperf application. | ||
func App() *cli.App { | ||
return &cli.App{ | ||
Name: "kperf", | ||
// TODO: add more fields | ||
Commands: []cli.Command{ | ||
runner.Command, | ||
multirunners.Command, | ||
}, | ||
} | ||
} |
Oops, something went wrong.