From 251ae34d2a962fcfbd21a88d37ef15e11622d3db Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Thu, 7 Dec 2023 11:19:57 +0000 Subject: [PATCH 01/22] api/types: Update LoadTraffic and add stats The client-go requires GroupVersion to generate request so that this patch is to update the KubeMetaType into KubeGroupVersionResources. And add ResponseStats as benchmark result. Signed-off-by: Wei Fu --- api/types/load_traffic.go | 135 ++++++++++++++++++++++++++++++--- api/types/load_traffic_test.go | 124 ++++++++++++++++++++++++++---- api/types/metric.go | 17 +++++ 3 files changed, 248 insertions(+), 28 deletions(-) create mode 100644 api/types/metric.go diff --git a/api/types/load_traffic.go b/api/types/load_traffic.go index a395c3a..4abf8e8 100644 --- a/api/types/load_traffic.go +++ b/api/types/load_traffic.go @@ -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. @@ -23,12 +25,14 @@ type LoadProfileSpec struct { 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. @@ -50,8 +54,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. @@ -60,8 +64,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. @@ -72,14 +76,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. @@ -89,3 +93,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 +} diff --git a/api/types/load_traffic_test.go b/api/types/load_traffic_test.go index 16ea503..05e9135 100644 --- a/api/types/load_traffic_test.go +++ b/api/types/load_traffic_test.go @@ -18,34 +18,39 @@ spec: conns: 2 requests: - staleGet: - kind: pods - apiVersion: v1 + group: core + version: v1 + resource: pods namespace: default name: x1 shares: 100 - quorumGet: - kind: configmap - apiVersion: v1 + group: core + version: v1 + resource: configmaps namespace: default name: x2 shares: 150 - staleList: - kind: pods - apiVersion: v1 + group: core + version: v1 + resource: pods namespace: default limit: 10000 seletor: app=x2 shares: 200 - quorumList: - kind: configmap - apiVersion: v1 + group: core + version: v1 + resource: configmaps namespace: default limit: 10000 seletor: app=x3 shares: 400 - put: - kind: configmap - apiVersion: v1 + group: core + version: v1 + resource: configmaps namespace: kperf name: kperf- keySpaceSize: 1000 @@ -64,8 +69,9 @@ spec: 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, "pods", target.Spec.Requests[0].StaleGet.Resource) + assert.Equal(t, "v1", target.Spec.Requests[0].StaleGet.Version) + assert.Equal(t, "core", target.Spec.Requests[0].StaleGet.Group) assert.Equal(t, "default", target.Spec.Requests[0].StaleGet.Namespace) assert.Equal(t, "x1", target.Spec.Requests[0].StaleGet.Name) @@ -74,8 +80,9 @@ spec: 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, "pods", target.Spec.Requests[2].StaleList.Resource) + assert.Equal(t, "v1", target.Spec.Requests[2].StaleList.Version) + assert.Equal(t, "core", target.Spec.Requests[0].StaleGet.Group) 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) @@ -85,10 +92,95 @@ spec: 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, "configmaps", target.Spec.Requests[4].Put.Resource) + assert.Equal(t, "v1", target.Spec.Requests[4].Put.Version) + assert.Equal(t, "core", target.Spec.Requests[0].StaleGet.Group) 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) } + +func TestWeightedRequest(t *testing.T) { + for _, r := range []struct { + name string + req *WeightedRequest + hasErr bool + }{ + { + name: "shares < 0", + req: &WeightedRequest{Shares: -1}, + hasErr: true, + }, + { + name: "no request setting", + req: &WeightedRequest{Shares: 10}, + hasErr: true, + }, + { + name: "empty version", + req: &WeightedRequest{ + Shares: 10, + StaleGet: &RequestGet{ + KubeGroupVersionResource: KubeGroupVersionResource{ + Resource: "pods", + }, + }, + }, + hasErr: true, + }, + { + name: "empty resource", + req: &WeightedRequest{ + Shares: 10, + StaleGet: &RequestGet{ + KubeGroupVersionResource: KubeGroupVersionResource{ + Group: "core", + Version: "v1", + }, + }, + }, + hasErr: true, + }, + { + name: "wrong limit", + req: &WeightedRequest{ + Shares: 10, + StaleList: &RequestList{ + KubeGroupVersionResource: KubeGroupVersionResource{ + Group: "core", + Version: "v1", + Resource: "pods", + }, + Limit: -1, + }, + }, + hasErr: true, + }, + { + name: "no error", + req: &WeightedRequest{ + Shares: 10, + StaleGet: &RequestGet{ + KubeGroupVersionResource: KubeGroupVersionResource{ + Group: "core", + Version: "v1", + Resource: "pods", + }, + Namespace: "default", + Name: "testing", + }, + }, + }, + } { + r := r + t.Run(r.name, func(t *testing.T) { + err := r.req.Validate() + if r.hasErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/api/types/metric.go b/api/types/metric.go new file mode 100644 index 0000000..2231d2c --- /dev/null +++ b/api/types/metric.go @@ -0,0 +1,17 @@ +package types + +import "time" + +// ResponseStats is the report about benchmark result. +type ResponseStats struct { + // Total represents total number of requests. + Total int + // Failures represents number of failure request. + Failures int + // Duration means the time of benchmark. + Duration time.Duration + // Latencies represents the latency distribution in seconds. + // + // NOTE: The key represents quantile. + Latencies map[float64]float64 +} From 7b45af55b134498c3caa7ea6d3fde88a567ca8d5 Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Thu, 7 Dec 2023 11:21:54 +0000 Subject: [PATCH 02/22] metrics: init new package This package is used to collect metrics and gather info into target type. In this patch, it adds ResponseMetric to collect latencies and failure information. Signed-off-by: Wei Fu --- metrics/request.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 metrics/request.go diff --git a/metrics/request.go b/metrics/request.go new file mode 100644 index 0000000..cd8840a --- /dev/null +++ b/metrics/request.go @@ -0,0 +1,64 @@ +package metrics + +import ( + "fmt" + "sync/atomic" + + "github.com/prometheus/client_golang/prometheus" +) + +// ResponseMetric is a measurement related to http response. +type ResponseMetric interface { + // ObserveLatency observes latency. + ObserveLatency(seconds float64) + // ObserveFailure observes failure response. + ObserveFailure() + // Gather returns the summary. + Gather() (latencies map[float64]float64, failure int, _ error) +} + +type responseMetricImpl struct { + latencySeconds *prometheus.SummaryVec + failureCount int64 +} + +func NewResponseMetric() ResponseMetric { + return &responseMetricImpl{ + latencySeconds: prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Namespace: "request", + Name: "request_latency_seconds", + Objectives: map[float64]float64{0: 0, 0.5: 0, 0.9: 0, 0.95: 0, 0.99: 0, 1: 0}, + }, + []string{}, + ), + } +} + +// ObserveLatency implements ResponseMetric. +func (m *responseMetricImpl) ObserveLatency(seconds float64) { + m.latencySeconds.WithLabelValues().Observe(seconds) +} + +// ObserveFailure implements ResponseMetric. +func (m *responseMetricImpl) ObserveFailure() { + atomic.AddInt64(&m.failureCount, 1) +} + +// Gather implements ResponseMetric. +func (m *responseMetricImpl) Gather() (map[float64]float64, int, error) { + reg := prometheus.NewRegistry() + reg.MustRegister(m.latencySeconds) + + metricFamilies, err := reg.Gather() + if err != nil { + return nil, 0, fmt.Errorf("failed to gather from local registry: %w", err) + } + + latencies := map[float64]float64{} + for _, q := range metricFamilies[0].GetMetric()[0].GetSummary().GetQuantile() { + latencies[q.GetQuantile()] = q.GetValue() + } + + return latencies, int(atomic.LoadInt64(&m.failureCount)), nil +} From 14a255dedc0c33102d129bc27e424ae6ae5424d8 Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Thu, 7 Dec 2023 11:23:58 +0000 Subject: [PATCH 03/22] request: init request package That package is used to schedule random request and file that request to apiserver. Signed-off-by: Wei Fu --- random.go | 6 -- request/client.go | 46 ++++++++++ request/random.go | 219 ++++++++++++++++++++++++++++++++++++++++++++ request/schedule.go | 92 +++++++++++++++++++ 4 files changed, 357 insertions(+), 6 deletions(-) delete mode 100644 random.go create mode 100644 request/client.go create mode 100644 request/random.go create mode 100644 request/schedule.go diff --git a/random.go b/random.go deleted file mode 100644 index b5807f9..0000000 --- a/random.go +++ /dev/null @@ -1,6 +0,0 @@ -package kperf - -// WeightedRandomPick returns index randomly based on weights. -func WeightedRandomPick(_ []int) (_index int) { - panic("not implemented") -} diff --git a/request/client.go b/request/client.go new file mode 100644 index 0000000..45b1c73 --- /dev/null +++ b/request/client.go @@ -0,0 +1,46 @@ +package request + +import ( + "math" + + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/kubectl/pkg/scheme" +) + +// NewClients creates N rest.Interface. +// +// FIXME(weifu): +// +// 1. Is it possible to build one http2 client with multiple connections? +// 2. How to monitor HTTP2 GOAWAY frame? +// 3. Support Protobuf as accepted content +func NewClients(kubeCfgPath string, num int, userAgent string, qps int) ([]rest.Interface, error) { + restCfg, err := clientcmd.BuildConfigFromFlags("", kubeCfgPath) + if err != nil { + return nil, err + } + + if qps == 0 { + qps = math.MaxInt32 + } + restCfg.QPS = float32(qps) + restCfg.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + restCfg.UserAgent = userAgent + if restCfg.UserAgent == "" { + restCfg.UserAgent = rest.DefaultKubernetesUserAgent() + } + + restClients := make([]rest.Interface, 0, num) + for i := 0; i < num; i++ { + cfgShallowCopy := *restCfg + + restCli, err := rest.UnversionedRESTClientFor(&cfgShallowCopy) + if err != nil { + return nil, err + } + restClients = append(restClients, restCli) + } + return restClients, nil +} diff --git a/request/random.go b/request/random.go new file mode 100644 index 0000000..7c084d5 --- /dev/null +++ b/request/random.go @@ -0,0 +1,219 @@ +package request + +import ( + "context" + "crypto/rand" + "fmt" + "math/big" + "sync" + + "github.com/Azure/kperf/api/types" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" +) + +// WeightedRandomRequests is used to generate requests based on LoadProfileSpec. +type WeightedRandomRequests struct { + once sync.Once + wg sync.WaitGroup + ctx context.Context + cancel context.CancelFunc + reqBuilderCh chan RequestBuilder + + shares []int + reqBuilders []RequestBuilder +} + +// NewWeightedRandomRequests creates new instance of WeightedRandomRequests. +func NewWeightedRandomRequests(spec *types.LoadProfileSpec) (*WeightedRandomRequests, error) { + if err := spec.Validate(); err != nil { + return nil, fmt.Errorf("invalid load profile spec: %v", err) + } + + shares := make([]int, 0, len(spec.Requests)) + reqBuilders := make([]RequestBuilder, 0, len(spec.Requests)) + for _, r := range spec.Requests { + shares = append(shares, r.Shares) + + var builder RequestBuilder + switch { + case r.StaleList != nil: + builder = newRequestListBuilder(r.StaleList, "0") + case r.QuorumList != nil: + builder = newRequestListBuilder(r.QuorumList, "") + case r.StaleGet != nil: + builder = newRequestGetBuilder(r.StaleGet, "0") + case r.QuorumGet != nil: + builder = newRequestGetBuilder(r.QuorumGet, "") + default: + return nil, fmt.Errorf("only support get/list") + } + reqBuilders = append(reqBuilders, builder) + } + + ctx, cancel := context.WithCancel(context.Background()) + return &WeightedRandomRequests{ + ctx: ctx, + cancel: cancel, + reqBuilderCh: make(chan RequestBuilder), + shares: shares, + reqBuilders: reqBuilders, + }, nil +} + +// Run starts to random pick request. +func (r *WeightedRandomRequests) Run(ctx context.Context, total int) { + defer r.wg.Done() + r.wg.Add(1) + + sum := 0 + for sum < total { + builder := r.randomPick() + select { + case r.reqBuilderCh <- builder: + sum += 1 + case <-r.ctx.Done(): + return + case <-ctx.Done(): + return + } + } +} + +// Chan returns channel to get random request. +func (r *WeightedRandomRequests) Chan() chan RequestBuilder { + return r.reqBuilderCh +} + +func (r *WeightedRandomRequests) randomPick() RequestBuilder { + sum := 0 + for _, s := range r.shares { + sum += s + } + + rndInt, err := rand.Int(rand.Reader, big.NewInt(int64(sum))) + if err != nil { + panic(err) + } + + rnd := rndInt.Int64() + for i := range r.shares { + s := int64(r.shares[i]) + if rnd < s { + return r.reqBuilders[i] + } + rnd -= s + } + panic("unreachable") +} + +// Stop stops request generator. +func (r *WeightedRandomRequests) Stop() { + r.once.Do(func() { + r.cancel() + r.wg.Wait() + close(r.reqBuilderCh) + }) +} + +// RequestBuilder is used to build rest.Request. +type RequestBuilder interface { + Build(cli rest.Interface) (method string, _ *rest.Request) +} + +type requestGetBuilder struct { + version schema.GroupVersion + resource string + namespace string + name string + resourceVersion string +} + +func newRequestGetBuilder(src *types.RequestGet, resourceVersion string) *requestGetBuilder { + return &requestGetBuilder{ + version: schema.GroupVersion{ + Group: src.Group, + Version: src.Version, + }, + resource: src.Resource, + namespace: src.Namespace, + name: src.Name, + resourceVersion: resourceVersion, + } +} + +// Build implements RequestBuilder.Build. +func (b *requestGetBuilder) Build(cli rest.Interface) (string, *rest.Request) { + // https://kubernetes.io/docs/reference/using-api/#api-groups + apiPath := "apis" + if b.version.Group == "" { + apiPath = "api" + } + + comps := make([]string, 2, 5) + comps[0], comps[1] = apiPath, b.version.Version + if b.namespace != "" { + comps = append(comps, "namespaces", b.namespace) + } + comps = append(comps, b.resource, b.name) + + return "GET", cli.Get().AbsPath(comps...). + SpecificallyVersionedParams( + &metav1.GetOptions{ResourceVersion: b.resourceVersion}, + scheme.ParameterCodec, + schema.GroupVersion{Version: "v1"}, + ) +} + +type requestListBuilder struct { + version schema.GroupVersion + resource string + namespace string + limit int64 + labelSelector string + resourceVersion string +} + +func newRequestListBuilder(src *types.RequestList, resourceVersion string) *requestListBuilder { + return &requestListBuilder{ + version: schema.GroupVersion{ + Group: src.Group, + Version: src.Version, + }, + resource: src.Resource, + namespace: src.Namespace, + limit: int64(src.Limit), + labelSelector: src.Selector, + resourceVersion: resourceVersion, + } +} + +// Build implements RequestBuilder.Build. +func (b *requestListBuilder) Build(cli rest.Interface) (string, *rest.Request) { + // https://kubernetes.io/docs/reference/using-api/#api-groups + apiPath := "apis" + if b.version.Group == "" { + apiPath = "api" + } + + comps := make([]string, 2, 5) + comps[0], comps[1] = apiPath, b.version.Version + if b.namespace != "" { + comps = append(comps, "namespaces", b.namespace) + } + comps = append(comps, b.resource) + + return "LIST", cli.Get().AbsPath(comps...). + SpecificallyVersionedParams( + &metav1.ListOptions{ + LabelSelector: b.labelSelector, + ResourceVersion: b.resourceVersion, + Limit: b.limit, + }, + scheme.ParameterCodec, + schema.GroupVersion{Version: "v1"}, + ) +} diff --git a/request/schedule.go b/request/schedule.go new file mode 100644 index 0000000..3ebf6a7 --- /dev/null +++ b/request/schedule.go @@ -0,0 +1,92 @@ +package request + +import ( + "context" + "io" + "math" + "sync" + "time" + + "github.com/Azure/kperf/api/types" + "github.com/Azure/kperf/metrics" + + "golang.org/x/time/rate" + "k8s.io/client-go/rest" +) + +const defaultTimeout = 60 * time.Second + +// Schedule files requests to apiserver based on LoadProfileSpec. +func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.Interface) (*types.ResponseStats, error) { + ctx, cancel := context.WithCancel(ctx) + + rndReqs, err := NewWeightedRandomRequests(spec) + if err != nil { + return nil, err + } + + qps := spec.Rate + if qps == 0 { + qps = math.MaxInt32 + } + limiter := rate.NewLimiter(rate.Limit(qps), 10) + + reqBuilderCh := rndReqs.Chan() + var wg sync.WaitGroup + + respMetric := metrics.NewResponseMetric() + for _, cli := range restCli { + cli := cli + wg.Add(1) + go func() { + defer wg.Done() + + for builder := range reqBuilderCh { + _, req := builder.Build(cli) + + if err := limiter.Wait(ctx); err != nil { + cancel() + return + } + + req = req.Timeout(defaultTimeout) + func() { + start := time.Now() + defer func() { + respMetric.ObserveLatency(time.Since(start).Seconds()) + }() + + respBody, err := req.Stream(context.Background()) + if err == nil { + defer respBody.Close() + // NOTE: It's to reduce memory usage because + // we don't need that unmarshal object. + _, err = io.Copy(io.Discard, respBody) + } + if err != nil { + respMetric.ObserveFailure() + } + }() + } + }() + } + + start := time.Now() + + rndReqs.Run(ctx, spec.Total) + rndReqs.Stop() + wg.Wait() + + totalDuration := time.Since(start) + + latencies, failures, err := respMetric.Gather() + if err != nil { + return nil, err + } + return &types.ResponseStats{ + Total: spec.Total, + Failures: failures, + Duration: totalDuration, + Latencies: latencies, + }, nil +} From 6bbdc5935505f4c50097889683851628243e923f Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Thu, 7 Dec 2023 11:24:32 +0000 Subject: [PATCH 04/22] *: init runner sub-command. Signed-off-by: Wei Fu --- cmd/kperf/commands/runner/runner.go | 142 ++++++++++++++++++++++------ go.mod | 34 +++++++ go.sum | 127 ++++++++++++++++++++++++- 3 files changed, 275 insertions(+), 28 deletions(-) diff --git a/cmd/kperf/commands/runner/runner.go b/cmd/kperf/commands/runner/runner.go index 72ab406..496fbbd 100644 --- a/cmd/kperf/commands/runner/runner.go +++ b/cmd/kperf/commands/runner/runner.go @@ -1,40 +1,128 @@ package runner import ( + "context" "fmt" + "os" + "sort" + + "github.com/Azure/kperf/api/types" + "github.com/Azure/kperf/request" "github.com/urfave/cli" + "gopkg.in/yaml.v2" ) -// Command represents runner sub-command. -// -// Subcommand runner is to create request load to apiserver. -// -// NOTE: It can work with subcommand multirunners. The multirunners subcommand -// will deploy subcommand runner in pod. Details in ../multirunners. -// -// Command line interface: -// -// kperf runner --help -// -// Options: -// -// --kubeconfig PATH (default: empty_string, use token if it's empty) -// --load-config PATH (default: empty_string, required, the config defined in api/types/load_traffic.go) -// --conns INT (default: 1, Total number of connections. It can override corresponding value defined by --load-config) -// --rate INT (default: 0, Maximum requests per second. It can override corresponding value defined by --load-config) -// --total INT (default: 1000, Total number of request. It can override corresponding value defined by --load-config) +// Command represents runner subcommand. var Command = cli.Command{ Name: "runner", - Usage: "run a load test to kube-apiserver", - Flags: []cli.Flag{}, + Usage: "Setup benchmark to kube-apiserver from one endpoint", + Subcommands: []cli.Command{ + runCommand, + }, +} + +var runCommand = cli.Command{ + Name: "run", + Usage: "run a benchmark test to kube-apiserver", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "kubeconfig", + Usage: "Path to the kubeconfig file", + }, + cli.StringFlag{ + Name: "config", + Usage: "Path to the configuration file", + Required: true, + }, + cli.IntFlag{ + Name: "conns", + Usage: "Total number of connections. It can override corresponding value defined by --config", + Value: 1, + }, + cli.IntFlag{ + Name: "rate", + Usage: "Maximum requests per second (Zero means no limitation). It can override corresponding value defined by --config", + }, + cli.IntFlag{ + Name: "total", + Usage: "Total number of requests. It can override corresponding value defined by --config", + Value: 1000, + }, + cli.StringFlag{ + Name: "user-agent", + Usage: "User Agent", + }, + }, Action: func(cliCtx *cli.Context) error { - // 1. Parse options - // 2. Setup producer-consumer goroutines - // 2.1 Use go limter to generate request - // 2.2 Use client-go's client to file requests - // 3. Build progress tracker to track failure number and P99/P95/P90 latencies. - // 4. Export summary in stdout. - return fmt.Errorf("runner - not implemented") + profileCfg, err := loadConfig(cliCtx) + if err != nil { + return err + } + + kubeCfgPath := cliCtx.String("kubeconfig") + userAgent := cliCtx.String("user-agent") + + conns := profileCfg.Spec.Conns + rate := profileCfg.Spec.Rate + restClis, err := request.NewClients(kubeCfgPath, conns, userAgent, rate) + if err != nil { + return err + } + + stats, err := request.Schedule(context.TODO(), &profileCfg.Spec, restClis) + if err != nil { + return err + } + printResponseStats(stats) + return nil }, } + +// loadConfig loads and validates the config. +func loadConfig(cliCtx *cli.Context) (*types.LoadProfile, error) { + var profileCfg types.LoadProfile + + cfgPath := cliCtx.String("config") + + cfgInRaw, err := os.ReadFile(cfgPath) + if err != nil { + return nil, fmt.Errorf("failed to read file %s: %w", cfgPath, err) + } + + if err := yaml.Unmarshal(cfgInRaw, &profileCfg); err != nil { + return nil, fmt.Errorf("failed to unmarshal %s from yaml format: %w", cfgPath, err) + } + + // override value by flags + // + // TODO(weifu): do not override if flag is not set + profileCfg.Spec.Rate = cliCtx.Int("rate") + profileCfg.Spec.Conns = cliCtx.Int("conns") + profileCfg.Spec.Total = cliCtx.Int("total") + + if err := profileCfg.Validate(); err != nil { + return nil, err + } + return &profileCfg, nil +} + +// printResponseStats prints ResponseStats into stdout. +func printResponseStats(stats *types.ResponseStats) { + fmt.Println("Response stat:") + fmt.Printf(" Total: %v\n", stats.Total) + fmt.Printf(" Failures: %v\n", stats.Failures) + fmt.Printf(" Duration: %v\n", stats.Duration) + fmt.Printf(" Requests/sec: %.2f\n", float64(stats.Total)/stats.Duration.Seconds()) + + fmt.Println(" Latency Distribution:") + keys := make([]float64, 0, len(stats.Latencies)) + for q := range stats.Latencies { + keys = append(keys, q) + } + sort.Float64s(keys) + + for _, q := range keys { + fmt.Printf(" [%.2f] %.3fs\n", q, stats.Latencies[q]) + } +} diff --git a/go.mod b/go.mod index 11cb7c0..0326c7d 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,49 @@ module github.com/Azure/kperf go 1.20 require ( + github.com/prometheus/client_golang v1.17.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli v1.22.14 + golang.org/x/time v0.3.0 gopkg.in/yaml.v2 v2.4.0 + k8s.io/apimachinery v0.28.4 + k8s.io/client-go v0.28.4 + k8s.io/kubectl v0.28.4 ) require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.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 github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.28.4 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 2e3b51b..2ff18d9 100644 --- a/go.sum +++ b/go.sum @@ -1,26 +1,151 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +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/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= +k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= +k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= +k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= +k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= +k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kubectl v0.28.4 h1:gWpUXW/T7aFne+rchYeHkyB8eVDl5UZce8G4X//kjUQ= +k8s.io/kubectl v0.28.4/go.mod h1:CKOccVx3l+3MmDbkXtIUtibq93nN2hkDR99XDCn7c/c= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= From 82e36b27637497b94997c102a105e4e0ff1eaa00 Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Thu, 7 Dec 2023 11:29:16 +0000 Subject: [PATCH 05/22] fix: lint issue Signed-off-by: Wei Fu --- request/random.go | 22 +++++++++++----------- request/schedule.go | 1 + 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/request/random.go b/request/random.go index 7c084d5..8bd061f 100644 --- a/request/random.go +++ b/request/random.go @@ -21,10 +21,10 @@ type WeightedRandomRequests struct { wg sync.WaitGroup ctx context.Context cancel context.CancelFunc - reqBuilderCh chan RequestBuilder + reqBuilderCh chan RESTRequestBuilder shares []int - reqBuilders []RequestBuilder + reqBuilders []RESTRequestBuilder } // NewWeightedRandomRequests creates new instance of WeightedRandomRequests. @@ -34,11 +34,11 @@ func NewWeightedRandomRequests(spec *types.LoadProfileSpec) (*WeightedRandomRequ } shares := make([]int, 0, len(spec.Requests)) - reqBuilders := make([]RequestBuilder, 0, len(spec.Requests)) + reqBuilders := make([]RESTRequestBuilder, 0, len(spec.Requests)) for _, r := range spec.Requests { shares = append(shares, r.Shares) - var builder RequestBuilder + var builder RESTRequestBuilder switch { case r.StaleList != nil: builder = newRequestListBuilder(r.StaleList, "0") @@ -49,7 +49,7 @@ func NewWeightedRandomRequests(spec *types.LoadProfileSpec) (*WeightedRandomRequ case r.QuorumGet != nil: builder = newRequestGetBuilder(r.QuorumGet, "") default: - return nil, fmt.Errorf("only support get/list") + return nil, fmt.Errorf("not implement for PUT yet") } reqBuilders = append(reqBuilders, builder) } @@ -58,7 +58,7 @@ func NewWeightedRandomRequests(spec *types.LoadProfileSpec) (*WeightedRandomRequ return &WeightedRandomRequests{ ctx: ctx, cancel: cancel, - reqBuilderCh: make(chan RequestBuilder), + reqBuilderCh: make(chan RESTRequestBuilder), shares: shares, reqBuilders: reqBuilders, }, nil @@ -74,7 +74,7 @@ func (r *WeightedRandomRequests) Run(ctx context.Context, total int) { builder := r.randomPick() select { case r.reqBuilderCh <- builder: - sum += 1 + sum++ case <-r.ctx.Done(): return case <-ctx.Done(): @@ -84,11 +84,11 @@ func (r *WeightedRandomRequests) Run(ctx context.Context, total int) { } // Chan returns channel to get random request. -func (r *WeightedRandomRequests) Chan() chan RequestBuilder { +func (r *WeightedRandomRequests) Chan() chan RESTRequestBuilder { return r.reqBuilderCh } -func (r *WeightedRandomRequests) randomPick() RequestBuilder { +func (r *WeightedRandomRequests) randomPick() RESTRequestBuilder { sum := 0 for _, s := range r.shares { sum += s @@ -119,8 +119,8 @@ func (r *WeightedRandomRequests) Stop() { }) } -// RequestBuilder is used to build rest.Request. -type RequestBuilder interface { +// RESTRequestBuilder is used to build rest.Request. +type RESTRequestBuilder interface { Build(cli rest.Interface) (method string, _ *rest.Request) } diff --git a/request/schedule.go b/request/schedule.go index 3ebf6a7..f2a216e 100644 --- a/request/schedule.go +++ b/request/schedule.go @@ -19,6 +19,7 @@ const defaultTimeout = 60 * time.Second // Schedule files requests to apiserver based on LoadProfileSpec. func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.Interface) (*types.ResponseStats, error) { ctx, cancel := context.WithCancel(ctx) + defer cancel() rndReqs, err := NewWeightedRandomRequests(spec) if err != nil { From 1d65f9bed92467df45bf8b823a2a320398d7843b Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Fri, 8 Dec 2023 02:57:01 +0000 Subject: [PATCH 06/22] *: Init recipe for image build And the binary will be built with static tag. Signed-off-by: Wei Fu --- Dockerfile | 20 ++++++++++++++++++++ Makefile | 24 +++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7ce80ae --- /dev/null +++ b/Dockerfile @@ -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"] diff --git a/Makefile b/Makefile index 8c0049b..b357424 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -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 ./... From 31280f2d809cf70e2f0cf7fc567750ba7cb97078 Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Fri, 8 Dec 2023 07:35:08 +0000 Subject: [PATCH 07/22] api/types: add TODO items to ResponseStats Signed-off-by: Wei Fu --- api/types/metric.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/types/metric.go b/api/types/metric.go index 2231d2c..69aab68 100644 --- a/api/types/metric.go +++ b/api/types/metric.go @@ -14,4 +14,8 @@ type ResponseStats struct { // // NOTE: The key represents quantile. Latencies map[float64]float64 + // TODO: + // 1. Support total read/upload bytes + // 2. Support failures partitioned by http code and verb + // 3. Support to dump all latency data } From 288acb690b9e8b4f6e0816871fdc4d82c28a3944 Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Thu, 14 Dec 2023 08:06:03 +0000 Subject: [PATCH 08/22] *: use list to track latencies Prometheus client doesn't allow us to export observed data. kperf needs to support export raw data for aggregated result. In order to avoid append, this patch uses container/list to maintain latencies. Signed-off-by: Wei Fu --- api/types/metric.go | 4 +- cmd/kperf/commands/runner/runner.go | 6 +-- go.mod | 7 --- go.sum | 16 ------- metrics/request.go | 67 ++++++++++++++++++----------- metrics/request_test.go | 48 +++++++++++++++++++++ request/schedule.go | 13 +++--- 7 files changed, 100 insertions(+), 61 deletions(-) create mode 100644 metrics/request_test.go diff --git a/api/types/metric.go b/api/types/metric.go index 69aab68..d6eb2c1 100644 --- a/api/types/metric.go +++ b/api/types/metric.go @@ -10,10 +10,10 @@ type ResponseStats struct { Failures int // Duration means the time of benchmark. Duration time.Duration - // Latencies represents the latency distribution in seconds. + // PercentileLatencies represents the latency distribution in seconds. // // NOTE: The key represents quantile. - Latencies map[float64]float64 + PercentileLatencies map[float64]float64 // TODO: // 1. Support total read/upload bytes // 2. Support failures partitioned by http code and verb diff --git a/cmd/kperf/commands/runner/runner.go b/cmd/kperf/commands/runner/runner.go index 496fbbd..c2ab3f9 100644 --- a/cmd/kperf/commands/runner/runner.go +++ b/cmd/kperf/commands/runner/runner.go @@ -116,13 +116,13 @@ func printResponseStats(stats *types.ResponseStats) { fmt.Printf(" Requests/sec: %.2f\n", float64(stats.Total)/stats.Duration.Seconds()) fmt.Println(" Latency Distribution:") - keys := make([]float64, 0, len(stats.Latencies)) - for q := range stats.Latencies { + keys := make([]float64, 0, len(stats.PercentileLatencies)) + for q := range stats.PercentileLatencies { keys = append(keys, q) } sort.Float64s(keys) for _, q := range keys { - fmt.Printf(" [%.2f] %.3fs\n", q, stats.Latencies[q]) + fmt.Printf(" [%.2f] %.3fs\n", q/100.0, stats.PercentileLatencies[q]) } } diff --git a/go.mod b/go.mod index 0326c7d..d414d58 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/Azure/kperf go 1.20 require ( - github.com/prometheus/client_golang v1.17.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli v1.22.14 golang.org/x/time v0.3.0 @@ -14,8 +13,6 @@ require ( ) require ( - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.2.4 // indirect @@ -24,13 +21,9 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.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 github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.17.0 // indirect diff --git a/go.sum b/go.sum index 2ff18d9..e21baab 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,4 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -17,7 +13,6 @@ github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2Kv github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= @@ -39,8 +34,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -49,14 +42,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -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/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -88,7 +73,6 @@ golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/metrics/request.go b/metrics/request.go index cd8840a..374c929 100644 --- a/metrics/request.go +++ b/metrics/request.go @@ -1,10 +1,11 @@ package metrics import ( - "fmt" + "container/list" + "math" + "sort" + "sync" "sync/atomic" - - "github.com/prometheus/client_golang/prometheus" ) // ResponseMetric is a measurement related to http response. @@ -14,30 +15,26 @@ type ResponseMetric interface { // ObserveFailure observes failure response. ObserveFailure() // Gather returns the summary. - Gather() (latencies map[float64]float64, failure int, _ error) + Gather() (latencies []float64, percentileLatencies map[float64]float64, failure int) } type responseMetricImpl struct { - latencySeconds *prometheus.SummaryVec - failureCount int64 + mu sync.Mutex + failureCount int64 + latencies *list.List } func NewResponseMetric() ResponseMetric { return &responseMetricImpl{ - latencySeconds: prometheus.NewSummaryVec( - prometheus.SummaryOpts{ - Namespace: "request", - Name: "request_latency_seconds", - Objectives: map[float64]float64{0: 0, 0.5: 0, 0.9: 0, 0.95: 0, 0.99: 0, 1: 0}, - }, - []string{}, - ), + latencies: list.New(), } } // ObserveLatency implements ResponseMetric. func (m *responseMetricImpl) ObserveLatency(seconds float64) { - m.latencySeconds.WithLabelValues().Observe(seconds) + m.mu.Lock() + defer m.mu.Unlock() + m.latencies.PushBack(seconds) } // ObserveFailure implements ResponseMetric. @@ -46,19 +43,39 @@ func (m *responseMetricImpl) ObserveFailure() { } // Gather implements ResponseMetric. -func (m *responseMetricImpl) Gather() (map[float64]float64, int, error) { - reg := prometheus.NewRegistry() - reg.MustRegister(m.latencySeconds) +func (m *responseMetricImpl) Gather() ([]float64, map[float64]float64, int) { + latencies := m.dumpLatencies() + + return latencies, buildPercentileLatencies(latencies), int(atomic.LoadInt64(&m.failureCount)) +} - metricFamilies, err := reg.Gather() - if err != nil { - return nil, 0, fmt.Errorf("failed to gather from local registry: %w", err) +func (m *responseMetricImpl) dumpLatencies() []float64 { + m.mu.Lock() + defer m.mu.Unlock() + res := make([]float64, 0, m.latencies.Len()) + for e := m.latencies.Front(); e != nil; e = e.Next() { + res = append(res, e.Value.(float64)) } + return res +} - latencies := map[float64]float64{} - for _, q := range metricFamilies[0].GetMetric()[0].GetSummary().GetQuantile() { - latencies[q.GetQuantile()] = q.GetValue() +var percentiles = []float64{0, 50, 90, 95, 99, 100} + +func buildPercentileLatencies(latencies []float64) map[float64]float64 { + if len(latencies) == 0 { + return nil } - return latencies, int(atomic.LoadInt64(&m.failureCount)), nil + res := make(map[float64]float64, len(percentiles)) + + n := len(latencies) + sort.Float64s(latencies) + for _, p := range percentiles { + idx := int(math.Ceil(float64(n) * p / 100)) + if idx > 0 { + idx-- + } + res[p] = latencies[idx] + } + return res } diff --git a/metrics/request_test.go b/metrics/request_test.go new file mode 100644 index 0000000..2cd8e15 --- /dev/null +++ b/metrics/request_test.go @@ -0,0 +1,48 @@ +package metrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBuildPercentileLatencies(t *testing.T) { + ls := make([]float64, 100) + ls[0] = 50 + ls[1] = 49 + ls[2] = 1 + res := buildPercentileLatencies(ls) + assert.Equal(t, float64(0), res[0]) + assert.Equal(t, float64(0), res[50]) + assert.Equal(t, float64(0), res[90]) + assert.Equal(t, float64(0), res[95]) + assert.Equal(t, float64(49), res[99]) + assert.Equal(t, float64(50), res[100]) + + ls = make([]float64, 1000) + ls[0] = 50 + ls[1] = 49 + ls[2] = -1 + res = buildPercentileLatencies(ls) + assert.Equal(t, float64(-1), res[0]) + assert.Equal(t, float64(0), res[50]) + assert.Equal(t, float64(0), res[90]) + assert.Equal(t, float64(0), res[95]) + assert.Equal(t, float64(0), res[99]) + assert.Equal(t, float64(50), res[100]) +} + +func TestResponseMetric(t *testing.T) { + c := NewResponseMetric() + for i := 100; i > 0; i-- { + c.ObserveLatency(float64(i)) + } + + _, res, _ := c.Gather() + assert.Equal(t, float64(1), res[0]) + assert.Equal(t, float64(50), res[50]) + assert.Equal(t, float64(90), res[90]) + assert.Equal(t, float64(95), res[95]) + assert.Equal(t, float64(99), res[99]) + assert.Equal(t, float64(100), res[100]) +} diff --git a/request/schedule.go b/request/schedule.go index f2a216e..246fcc1 100644 --- a/request/schedule.go +++ b/request/schedule.go @@ -80,14 +80,11 @@ func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.I totalDuration := time.Since(start) - latencies, failures, err := respMetric.Gather() - if err != nil { - return nil, err - } + _, percentileLatencies, failures := respMetric.Gather() return &types.ResponseStats{ - Total: spec.Total, - Failures: failures, - Duration: totalDuration, - Latencies: latencies, + Total: spec.Total, + Failures: failures, + Duration: totalDuration, + PercentileLatencies: percentileLatencies, }, nil } From 520665ff9d5468a5afd57986d9f4ff46b3fa8db3 Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Thu, 21 Dec 2023 03:38:55 +0000 Subject: [PATCH 09/22] cmd: init virtualcluster subcommand Signed-off-by: Wei Fu --- cmd/kperf/commands/root.go | 2 + cmd/kperf/commands/virtualcluster/nodepool.go | 67 +++++++++++++++++++ cmd/kperf/commands/virtualcluster/vc.go | 15 +++++ 3 files changed, 84 insertions(+) create mode 100644 cmd/kperf/commands/virtualcluster/nodepool.go create mode 100644 cmd/kperf/commands/virtualcluster/vc.go diff --git a/cmd/kperf/commands/root.go b/cmd/kperf/commands/root.go index 6bac055..144f08d 100644 --- a/cmd/kperf/commands/root.go +++ b/cmd/kperf/commands/root.go @@ -3,6 +3,7 @@ package commands import ( "github.com/Azure/kperf/cmd/kperf/commands/multirunners" "github.com/Azure/kperf/cmd/kperf/commands/runner" + "github.com/Azure/kperf/cmd/kperf/commands/virtualcluster" "github.com/urfave/cli" ) @@ -15,6 +16,7 @@ func App() *cli.App { Commands: []cli.Command{ runner.Command, multirunners.Command, + virtualcluster.Command, }, } } diff --git a/cmd/kperf/commands/virtualcluster/nodepool.go b/cmd/kperf/commands/virtualcluster/nodepool.go new file mode 100644 index 0000000..2c64a90 --- /dev/null +++ b/cmd/kperf/commands/virtualcluster/nodepool.go @@ -0,0 +1,67 @@ +package virtualcluster + +import ( + "fmt" + + "github.com/urfave/cli" +) + +var nodepoolCommand = cli.Command{ + Name: "nodepool", + Usage: "Manage virtual node pools", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "kubeconfig", + Usage: "Path to the kubeconfig file", + }, + }, + Subcommands: []cli.Command{ + nodepoolAddCommand, + nodepoolDelCommand, + nodepoolListCommand, + }, +} + +var nodepoolAddCommand = cli.Command{ + Name: "add", + Usage: "Add a virtual node pool", + ArgsUsage: "NAME", + Flags: []cli.Flag{ + cli.IntFlag{ + Name: "nodes", + Usage: "The number of virtual nodes", + Value: 10, + }, + cli.IntFlag{ + Name: "cpu", + Usage: "The allocatable CPU resource per node", + Value: 8, + }, + cli.IntFlag{ + Name: "memory", + Usage: "The allocatable Memory resource per node (GiB)", + Value: 16, + }, + }, + Action: func(cliCtx *cli.Context) error { + return fmt.Errorf("nodepool add - not implemented") + }, +} + +var nodepoolDelCommand = cli.Command{ + Name: "delete", + ShortName: "del", + ArgsUsage: "NAME", + Usage: "Delete a virtual node pool", + Action: func(cliCtx *cli.Context) error { + return fmt.Errorf("nodepool delete - not implemented") + }, +} + +var nodepoolListCommand = cli.Command{ + Name: "list", + Usage: "List virtual node pools", + Action: func(cliCtx *cli.Context) error { + return fmt.Errorf("nodepool list - not implemented") + }, +} diff --git a/cmd/kperf/commands/virtualcluster/vc.go b/cmd/kperf/commands/virtualcluster/vc.go new file mode 100644 index 0000000..f869f82 --- /dev/null +++ b/cmd/kperf/commands/virtualcluster/vc.go @@ -0,0 +1,15 @@ +package virtualcluster + +import "github.com/urfave/cli" + +// const namespace = "kperf-virtualcluster" + +// Command represents virtualcluster subcommand. +var Command = cli.Command{ + Name: "virtualcluster", + ShortName: "vc", + Usage: "Setup virtual cluster and run workload on that", + Subcommands: []cli.Command{ + nodepoolCommand, + }, +} From c6080afcd90885908c90331a1cbdac1ab0b18f06 Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Thu, 21 Dec 2023 09:29:36 +0000 Subject: [PATCH 10/22] runner: use default value if flag is not set Signed-off-by: Wei Fu --- cmd/kperf/commands/runner/runner.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cmd/kperf/commands/runner/runner.go b/cmd/kperf/commands/runner/runner.go index c2ab3f9..0acea61 100644 --- a/cmd/kperf/commands/runner/runner.go +++ b/cmd/kperf/commands/runner/runner.go @@ -95,11 +95,15 @@ func loadConfig(cliCtx *cli.Context) (*types.LoadProfile, error) { } // override value by flags - // - // TODO(weifu): do not override if flag is not set - profileCfg.Spec.Rate = cliCtx.Int("rate") - profileCfg.Spec.Conns = cliCtx.Int("conns") - profileCfg.Spec.Total = cliCtx.Int("total") + if v := "rate"; cliCtx.IsSet(v) { + profileCfg.Spec.Rate = cliCtx.Int(v) + } + if v := "conns"; cliCtx.IsSet(v) || profileCfg.Spec.Conns == 0 { + profileCfg.Spec.Conns = cliCtx.Int(v) + } + if v := "total"; cliCtx.IsSet(v) || profileCfg.Spec.Total == 0 { + profileCfg.Spec.Total = cliCtx.Int(v) + } if err := profileCfg.Validate(); err != nil { return nil, err From 411bb359b6dac6a1dee3fc43e02e5f13a1fa73b9 Mon Sep 17 00:00:00 2001 From: manasachinta Date: Thu, 28 Dec 2023 15:34:20 -0500 Subject: [PATCH 11/22] Added failure list code, tested --- api/types/metric.go | 2 ++ cmd/kperf/commands/runner/runner.go | 5 +++++ metrics/request.go | 15 +++++++++------ metrics/request_test.go | 2 +- request/schedule.go | 5 +++-- tmp/xx.yaml | 17 +++++++++++++++++ 6 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 tmp/xx.yaml diff --git a/api/types/metric.go b/api/types/metric.go index d6eb2c1..a5f91be 100644 --- a/api/types/metric.go +++ b/api/types/metric.go @@ -8,6 +8,8 @@ type ResponseStats struct { Total int // Failures represents number of failure request. Failures int + // List of failures + FailureList []error // Duration means the time of benchmark. Duration time.Duration // PercentileLatencies represents the latency distribution in seconds. diff --git a/cmd/kperf/commands/runner/runner.go b/cmd/kperf/commands/runner/runner.go index 0acea61..67e1043 100644 --- a/cmd/kperf/commands/runner/runner.go +++ b/cmd/kperf/commands/runner/runner.go @@ -116,6 +116,11 @@ func printResponseStats(stats *types.ResponseStats) { fmt.Println("Response stat:") fmt.Printf(" Total: %v\n", stats.Total) fmt.Printf(" Failures: %v\n", stats.Failures) + fmt.Println("==========================") + for i, v := range stats.FailureList { + fmt.Printf(" Failure %d: %v\n", i+1, v) + } + fmt.Println("==========================") fmt.Printf(" Duration: %v\n", stats.Duration) fmt.Printf(" Requests/sec: %.2f\n", float64(stats.Total)/stats.Duration.Seconds()) diff --git a/metrics/request.go b/metrics/request.go index 374c929..5efd275 100644 --- a/metrics/request.go +++ b/metrics/request.go @@ -13,20 +13,22 @@ type ResponseMetric interface { // ObserveLatency observes latency. ObserveLatency(seconds float64) // ObserveFailure observes failure response. - ObserveFailure() + ObserveFailure(err error) // Gather returns the summary. - Gather() (latencies []float64, percentileLatencies map[float64]float64, failure int) + Gather() (latencies []float64, percentileLatencies map[float64]float64, failure int, failureList []error) } type responseMetricImpl struct { mu sync.Mutex failureCount int64 + failureList []error latencies *list.List } func NewResponseMetric() ResponseMetric { return &responseMetricImpl{ - latencies: list.New(), + latencies: list.New(), + failureList: []error{}, } } @@ -38,15 +40,16 @@ func (m *responseMetricImpl) ObserveLatency(seconds float64) { } // ObserveFailure implements ResponseMetric. -func (m *responseMetricImpl) ObserveFailure() { +func (m *responseMetricImpl) ObserveFailure(err error) { + m.failureList = append(m.failureList, err) atomic.AddInt64(&m.failureCount, 1) } // Gather implements ResponseMetric. -func (m *responseMetricImpl) Gather() ([]float64, map[float64]float64, int) { +func (m *responseMetricImpl) Gather() ([]float64, map[float64]float64, int, []error) { latencies := m.dumpLatencies() - return latencies, buildPercentileLatencies(latencies), int(atomic.LoadInt64(&m.failureCount)) + return latencies, buildPercentileLatencies(latencies), int(atomic.LoadInt64(&m.failureCount)), m.failureList } func (m *responseMetricImpl) dumpLatencies() []float64 { diff --git a/metrics/request_test.go b/metrics/request_test.go index 2cd8e15..eb4ad27 100644 --- a/metrics/request_test.go +++ b/metrics/request_test.go @@ -38,7 +38,7 @@ func TestResponseMetric(t *testing.T) { c.ObserveLatency(float64(i)) } - _, res, _ := c.Gather() + _, res, _, _ := c.Gather() assert.Equal(t, float64(1), res[0]) assert.Equal(t, float64(50), res[50]) assert.Equal(t, float64(90), res[90]) diff --git a/request/schedule.go b/request/schedule.go index 246fcc1..5f171bd 100644 --- a/request/schedule.go +++ b/request/schedule.go @@ -65,7 +65,7 @@ func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.I _, err = io.Copy(io.Discard, respBody) } if err != nil { - respMetric.ObserveFailure() + respMetric.ObserveFailure(err) } }() } @@ -80,10 +80,11 @@ func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.I totalDuration := time.Since(start) - _, percentileLatencies, failures := respMetric.Gather() + _, percentileLatencies, failures, failureList := respMetric.Gather() return &types.ResponseStats{ Total: spec.Total, Failures: failures, + FailureList: failureList, Duration: totalDuration, PercentileLatencies: percentileLatencies, }, nil diff --git a/tmp/xx.yaml b/tmp/xx.yaml new file mode 100644 index 0000000..23da4f1 --- /dev/null +++ b/tmp/xx.yaml @@ -0,0 +1,17 @@ +version: 1 +description: test +spec: + rate: 100 + total: 10 + conns: 10 + requests: + - staleList: + version: v1 + resource: pods + limit: 500 + shares: 100 + - quorumList: + version: v1 + resource: pods + limit: 1000 + shares: 150 From b53f59ca25666989c4a7985b7464cc88a98bf864 Mon Sep 17 00:00:00 2001 From: manasachinta Date: Mon, 1 Jan 2024 14:10:35 -0500 Subject: [PATCH 12/22] Removed failureCount field in ResponseMetric and ResponseStat, added mutex for ObserveFailure() --- api/types/metric.go | 2 -- cmd/kperf/commands/runner/runner.go | 2 +- metrics/request.go | 16 ++++++---------- metrics/request_test.go | 2 +- request/schedule.go | 8 ++++++-- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/api/types/metric.go b/api/types/metric.go index a5f91be..f5ad2ed 100644 --- a/api/types/metric.go +++ b/api/types/metric.go @@ -6,8 +6,6 @@ import "time" type ResponseStats struct { // Total represents total number of requests. Total int - // Failures represents number of failure request. - Failures int // List of failures FailureList []error // Duration means the time of benchmark. diff --git a/cmd/kperf/commands/runner/runner.go b/cmd/kperf/commands/runner/runner.go index 67e1043..1e659ca 100644 --- a/cmd/kperf/commands/runner/runner.go +++ b/cmd/kperf/commands/runner/runner.go @@ -115,7 +115,7 @@ func loadConfig(cliCtx *cli.Context) (*types.LoadProfile, error) { func printResponseStats(stats *types.ResponseStats) { fmt.Println("Response stat:") fmt.Printf(" Total: %v\n", stats.Total) - fmt.Printf(" Failures: %v\n", stats.Failures) + fmt.Printf(" Total Failures: %v\n", len(stats.FailureList)) fmt.Println("==========================") for i, v := range stats.FailureList { fmt.Printf(" Failure %d: %v\n", i+1, v) diff --git a/metrics/request.go b/metrics/request.go index 5efd275..eec8698 100644 --- a/metrics/request.go +++ b/metrics/request.go @@ -5,7 +5,6 @@ import ( "math" "sort" "sync" - "sync/atomic" ) // ResponseMetric is a measurement related to http response. @@ -15,14 +14,13 @@ type ResponseMetric interface { // ObserveFailure observes failure response. ObserveFailure(err error) // Gather returns the summary. - Gather() (latencies []float64, percentileLatencies map[float64]float64, failure int, failureList []error) + Gather() (latencies []float64, percentileLatencies map[float64]float64, failureList []error) } type responseMetricImpl struct { - mu sync.Mutex - failureCount int64 - failureList []error - latencies *list.List + mu sync.Mutex + failureList []error + latencies *list.List } func NewResponseMetric() ResponseMetric { @@ -42,14 +40,12 @@ func (m *responseMetricImpl) ObserveLatency(seconds float64) { // ObserveFailure implements ResponseMetric. func (m *responseMetricImpl) ObserveFailure(err error) { m.failureList = append(m.failureList, err) - atomic.AddInt64(&m.failureCount, 1) } // Gather implements ResponseMetric. -func (m *responseMetricImpl) Gather() ([]float64, map[float64]float64, int, []error) { +func (m *responseMetricImpl) Gather() ([]float64, map[float64]float64, []error) { latencies := m.dumpLatencies() - - return latencies, buildPercentileLatencies(latencies), int(atomic.LoadInt64(&m.failureCount)), m.failureList + return latencies, buildPercentileLatencies(latencies), m.failureList } func (m *responseMetricImpl) dumpLatencies() []float64 { diff --git a/metrics/request_test.go b/metrics/request_test.go index eb4ad27..2cd8e15 100644 --- a/metrics/request_test.go +++ b/metrics/request_test.go @@ -38,7 +38,7 @@ func TestResponseMetric(t *testing.T) { c.ObserveLatency(float64(i)) } - _, res, _, _ := c.Gather() + _, res, _ := c.Gather() assert.Equal(t, float64(1), res[0]) assert.Equal(t, float64(50), res[50]) assert.Equal(t, float64(90), res[90]) diff --git a/request/schedule.go b/request/schedule.go index 5f171bd..1e65249 100644 --- a/request/schedule.go +++ b/request/schedule.go @@ -16,6 +16,8 @@ import ( const defaultTimeout = 60 * time.Second +var m sync.Mutex + // Schedule files requests to apiserver based on LoadProfileSpec. func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.Interface) (*types.ResponseStats, error) { ctx, cancel := context.WithCancel(ctx) @@ -64,8 +66,11 @@ func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.I // we don't need that unmarshal object. _, err = io.Copy(io.Discard, respBody) } + if err != nil { + m.Lock() respMetric.ObserveFailure(err) + m.Unlock() } }() } @@ -80,10 +85,9 @@ func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.I totalDuration := time.Since(start) - _, percentileLatencies, failures, failureList := respMetric.Gather() + _, percentileLatencies, failureList := respMetric.Gather() return &types.ResponseStats{ Total: spec.Total, - Failures: failures, FailureList: failureList, Duration: totalDuration, PercentileLatencies: percentileLatencies, From cff5f226666674903ce03d0eeaea743da2c1c9ed Mon Sep 17 00:00:00 2001 From: manasachinta Date: Tue, 2 Jan 2024 00:31:38 -0500 Subject: [PATCH 13/22] Adding mutex inside ObserveFailure() instead of where it is called --- metrics/request.go | 2 ++ request/schedule.go | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/request.go b/metrics/request.go index eec8698..7f425ab 100644 --- a/metrics/request.go +++ b/metrics/request.go @@ -39,6 +39,8 @@ func (m *responseMetricImpl) ObserveLatency(seconds float64) { // ObserveFailure implements ResponseMetric. func (m *responseMetricImpl) ObserveFailure(err error) { + m.mu.Lock() + defer m.mu.Unlock() m.failureList = append(m.failureList, err) } diff --git a/request/schedule.go b/request/schedule.go index 1e65249..64b7553 100644 --- a/request/schedule.go +++ b/request/schedule.go @@ -68,9 +68,7 @@ func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.I } if err != nil { - m.Lock() respMetric.ObserveFailure(err) - m.Unlock() } }() } From f061cfbb502765ffd1828a7c483f298056e4f8f9 Mon Sep 17 00:00:00 2001 From: manasachinta Date: Tue, 2 Jan 2024 00:44:18 -0500 Subject: [PATCH 14/22] addressing more comments --- .gitignore | 3 +++ cmd/kperf/commands/runner/runner.go | 8 +++----- metrics/request.go | 3 ++- tmp/xx.yaml | 17 ----------------- 4 files changed, 8 insertions(+), 23 deletions(-) delete mode 100644 tmp/xx.yaml diff --git a/.gitignore b/.gitignore index 0bfa83c..1306df1 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ bin/ # Go workspace file go.work + +#tmp folder which contains .yaml files +tmp/ \ No newline at end of file diff --git a/cmd/kperf/commands/runner/runner.go b/cmd/kperf/commands/runner/runner.go index 1e659ca..0745433 100644 --- a/cmd/kperf/commands/runner/runner.go +++ b/cmd/kperf/commands/runner/runner.go @@ -115,12 +115,10 @@ func loadConfig(cliCtx *cli.Context) (*types.LoadProfile, error) { func printResponseStats(stats *types.ResponseStats) { fmt.Println("Response stat:") fmt.Printf(" Total: %v\n", stats.Total) - fmt.Printf(" Total Failures: %v\n", len(stats.FailureList)) - fmt.Println("==========================") - for i, v := range stats.FailureList { - fmt.Printf(" Failure %d: %v\n", i+1, v) + fmt.Printf(" Total Failures: %v\n", len(stats.FailureList)) + for _, v := range stats.FailureList { + fmt.Printf(" Failure: %v\n", v) } - fmt.Println("==========================") fmt.Printf(" Duration: %v\n", stats.Duration) fmt.Printf(" Requests/sec: %.2f\n", float64(stats.Total)/stats.Duration.Seconds()) diff --git a/metrics/request.go b/metrics/request.go index 7f425ab..80d7712 100644 --- a/metrics/request.go +++ b/metrics/request.go @@ -24,9 +24,10 @@ type responseMetricImpl struct { } func NewResponseMetric() ResponseMetric { + errList := make([]error, 0, 1024) return &responseMetricImpl{ latencies: list.New(), - failureList: []error{}, + failureList: errList, } } diff --git a/tmp/xx.yaml b/tmp/xx.yaml deleted file mode 100644 index 23da4f1..0000000 --- a/tmp/xx.yaml +++ /dev/null @@ -1,17 +0,0 @@ -version: 1 -description: test -spec: - rate: 100 - total: 10 - conns: 10 - requests: - - staleList: - version: v1 - resource: pods - limit: 500 - shares: 100 - - quorumList: - version: v1 - resource: pods - limit: 1000 - shares: 150 From 11208907bbd6bd69b87d576d37afd60e2c2468ed Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Wed, 3 Jan 2024 13:17:41 +0000 Subject: [PATCH 15/22] .github: set timeout=8m for lint Signed-off-by: Wei Fu --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 555b0cd..8af9d7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: - args: --config .golangci.yml + args: --config .golangci.yml --timeout=8m test: runs-on: ubuntu-latest From 414d95d52862ab41bf7ff82c4a0aa34286e56777 Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Tue, 2 Jan 2024 13:51:28 +0000 Subject: [PATCH 16/22] manifests: init embed FS for virtualnodes chart Signed-off-by: Wei Fu --- go.mod | 5 +- go.sum | 13 +- manifests/helm.go | 59 +++ manifests/mainfest.go | 8 + manifests/virtualcluster/nodes/Chart.yaml | 4 + .../nodes/templates/clusterrole.yaml | 59 +++ .../nodes/templates/clusterrolebinding.yaml | 12 + .../nodes/templates/config.yaml | 370 ++++++++++++++++++ .../virtualcluster/nodes/templates/nodes.tpl | 50 +++ .../nodes/templates/serviceaccount.yaml | 5 + .../nodes/templates/statefulsets.tpl | 70 ++++ manifests/virtualcluster/nodes/values.yaml | 6 + 12 files changed, 657 insertions(+), 4 deletions(-) create mode 100644 manifests/helm.go create mode 100644 manifests/mainfest.go create mode 100644 manifests/virtualcluster/nodes/Chart.yaml create mode 100644 manifests/virtualcluster/nodes/templates/clusterrole.yaml create mode 100644 manifests/virtualcluster/nodes/templates/clusterrolebinding.yaml create mode 100644 manifests/virtualcluster/nodes/templates/config.yaml create mode 100644 manifests/virtualcluster/nodes/templates/nodes.tpl create mode 100644 manifests/virtualcluster/nodes/templates/serviceaccount.yaml create mode 100644 manifests/virtualcluster/nodes/templates/statefulsets.tpl create mode 100644 manifests/virtualcluster/nodes/values.yaml diff --git a/go.mod b/go.mod index d414d58..20c423c 100644 --- a/go.mod +++ b/go.mod @@ -7,22 +7,25 @@ require ( github.com/urfave/cli v1.22.14 golang.org/x/time v0.3.0 gopkg.in/yaml.v2 v2.4.0 + helm.sh/helm/v3 v3.13.3 k8s.io/apimachinery v0.28.4 k8s.io/client-go v0.28.4 k8s.io/kubectl v0.28.4 ) require ( + github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/imdario/mergo v0.3.6 // indirect + github.com/imdario/mergo v0.3.13 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect diff --git a/go.sum b/go.sum index e21baab..21a98f2 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,12 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -24,8 +26,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -40,6 +42,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -112,8 +116,11 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +helm.sh/helm/v3 v3.13.3 h1:0zPEdGqHcubehJHP9emCtzRmu8oYsJFRrlVF3TFj8xY= +helm.sh/helm/v3 v3.13.3/go.mod h1:3OKO33yI3p4YEXtTITN2+4oScsHeQe71KuzhlZ+aPfg= k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= diff --git a/manifests/helm.go b/manifests/helm.go new file mode 100644 index 0000000..2829f9e --- /dev/null +++ b/manifests/helm.go @@ -0,0 +1,59 @@ +package manifests + +import ( + "fmt" + "io/fs" + "path/filepath" + "strings" + + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" +) + +// LoadChart returns chart from embed filesystem. +func LoadChart(componentName string) (*chart.Chart, error) { + files, err := getFilesFromFSRecursively(componentName) + if err != nil { + return nil, fmt.Errorf("failed to get chart files: %w", err) + } + + topDir := componentName + string(filepath.Separator) + bufFiles := make([]*loader.BufferedFile, 0, len(files)) + for _, f := range files { + data, err := fs.ReadFile(FS, f) + if err != nil { + return nil, fmt.Errorf("failed to read file (%s): %w", f, err) + } + + fname := filepath.ToSlash(strings.TrimPrefix(f, topDir)) + bufFiles = append(bufFiles, + &loader.BufferedFile{ + Name: fname, + Data: data, + }, + ) + } + return loader.LoadFiles(bufFiles) +} + +func getFilesFromFSRecursively(componentName string) ([]string, error) { + files := make([]string, 0) + + err := fs.WalkDir(FS, componentName, + func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + files = append(files, path) + return nil + }, + ) + if err != nil { + return nil, err + } + return files, nil +} diff --git a/manifests/mainfest.go b/manifests/mainfest.go new file mode 100644 index 0000000..0983de6 --- /dev/null +++ b/manifests/mainfest.go @@ -0,0 +1,8 @@ +package manifests + +import "embed" + +// FS embeds the manifests +// +//go:embed virtualcluster/* +var FS embed.FS diff --git a/manifests/virtualcluster/nodes/Chart.yaml b/manifests/virtualcluster/nodes/Chart.yaml new file mode 100644 index 0000000..567ab84 --- /dev/null +++ b/manifests/virtualcluster/nodes/Chart.yaml @@ -0,0 +1,4 @@ +{ + "name": "virtualnodes", + "version": "0.0.1" +} diff --git a/manifests/virtualcluster/nodes/templates/clusterrole.yaml b/manifests/virtualcluster/nodes/templates/clusterrole.yaml new file mode 100644 index 0000000..a0da8a7 --- /dev/null +++ b/manifests/virtualcluster/nodes/templates/clusterrole.yaml @@ -0,0 +1,59 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ .Values.name }} +rules: + - apiGroups: + - "" + resources: + - events + verbs: + - create + - update + - patch + - watch + - list + - get + - apiGroups: + - "" + resources: + - nodes + verbs: + - watch + - list + - get + - apiGroups: + - "" + resources: + - nodes/status + verbs: + - update + - patch + - apiGroups: + - "" + resources: + - pods + verbs: + - watch + - list + - delete + - update + - patch + - apiGroups: + - "" + resources: + - pods/status + verbs: + - update + - patch + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - update + - patch + - watch + - list + - get diff --git a/manifests/virtualcluster/nodes/templates/clusterrolebinding.yaml b/manifests/virtualcluster/nodes/templates/clusterrolebinding.yaml new file mode 100644 index 0000000..532f3d6 --- /dev/null +++ b/manifests/virtualcluster/nodes/templates/clusterrolebinding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Values.name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Values.name }} +subjects: + - kind: ServiceAccount + name: {{ .Values.name }} + namespace: {{ .Release.Namespace }} diff --git a/manifests/virtualcluster/nodes/templates/config.yaml b/manifests/virtualcluster/nodes/templates/config.yaml new file mode 100644 index 0000000..f04cb16 --- /dev/null +++ b/manifests/virtualcluster/nodes/templates/config.yaml @@ -0,0 +1,370 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app: {{ .Values.name }} + name: {{ .Values.name }} + namespace: {{ .Release.Namespace }} +data: + # NOTE: https://github.com/helm/helm/issues/2798#issuecomment-470435015 + kwok-config.yaml: |-{{` + kind: Stage + apiVersion: kwok.x-k8s.io/v1alpha1 + metadata: + name: pod-create + spec: + resourceRef: + apiGroup: v1 + kind: Pod + selector: + matchExpressions: + - key: '.metadata.deletionTimestamp' + operator: 'DoesNotExist' + - key: '.status.podIP' + operator: 'DoesNotExist' + weight: 1 + delay: + durationMilliseconds: 1000 + jitterDurationMilliseconds: 5000 + next: + event: + type: Normal + reason: Created + message: Created container + statusTemplate: | + {{ $now := Now }} + + conditions: + {{ if .spec.initContainers }} + - lastProbeTime: null + lastTransitionTime: '{{ $now }}' + message: 'containers with incomplete status: [{{ range .spec.initContainers }} {{ .name }} {{ end }}]' + reason: ContainersNotInitialized + status: "False" + type: Initialized + {{ else }} + - lastProbeTime: null + lastTransitionTime: '{{ $now }}' + status: "True" + type: Initialized + {{ end }} + - lastProbeTime: null + lastTransitionTime: '{{ $now }}' + message: 'containers with unready status: [{{ range .spec.containers }} {{ .name }} {{ end }}]' + reason: ContainersNotReady + status: "False" + type: Ready + - lastProbeTime: null + lastTransitionTime: '{{ $now }}' + message: 'containers with unready status: [{{ range .spec.containers }} {{ .name }} {{ end }}]' + reason: ContainersNotReady + status: "False" + type: ContainersReady + {{ range .spec.readinessGates }} + - lastTransitionTime: {{ $now }} + status: "True" + type: {{ .conditionType }} + {{ end }} + + {{ if .spec.initContainers }} + initContainerStatuses: + {{ range .spec.initContainers }} + - image: {{ .image }} + name: {{ .name }} + ready: false + restartCount: 0 + started: false + state: + waiting: + reason: PodInitializing + {{ end }} + containerStatuses: + {{ range .spec.containers }} + - image: {{ .image }} + name: {{ .name }} + ready: false + restartCount: 0 + started: false + state: + waiting: + reason: PodInitializing + {{ end }} + {{ else }} + containerStatuses: + {{ range .spec.containers }} + - image: {{ .image }} + name: {{ .name }} + ready: false + restartCount: 0 + started: false + state: + waiting: + reason: ContainerCreating + {{ end }} + {{ end }} + + hostIP: {{ NodeIPWith .spec.nodeName }} + podIP: {{ PodIPWith .spec.nodeName ( or .spec.hostNetwork false ) ( or .metadata.uid "" ) ( or .metadata.name "" ) ( or .metadata.namespace "" ) }} + phase: Pending + --- + kind: Stage + apiVersion: kwok.x-k8s.io/v1alpha1 + metadata: + name: pod-init-container-running + spec: + resourceRef: + apiGroup: v1 + kind: Pod + selector: + matchExpressions: + - key: '.metadata.deletionTimestamp' + operator: 'DoesNotExist' + - key: '.status.phase' + operator: 'In' + values: + - 'Pending' + - key: '.status.conditions.[] | select( .type == "Initialized" ) | .status' + operator: 'NotIn' + values: + - 'True' + - key: '.status.initContainerStatuses.[].state.waiting.reason' + operator: 'Exists' + weight: 1 + delay: + durationMilliseconds: 1000 + jitterDurationMilliseconds: 5000 + next: + statusTemplate: | + {{ $now := Now }} + {{ $root := . }} + initContainerStatuses: + {{ range $index, $item := .spec.initContainers }} + {{ $origin := index $root.status.initContainerStatuses $index }} + - image: {{ $item.image }} + name: {{ $item.name }} + ready: true + restartCount: 0 + started: true + state: + running: + startedAt: '{{ $now }}' + {{ end }} + --- + kind: Stage + apiVersion: kwok.x-k8s.io/v1alpha1 + metadata: + name: pod-init-container-completed + spec: + resourceRef: + apiGroup: v1 + kind: Pod + selector: + matchExpressions: + - key: '.metadata.deletionTimestamp' + operator: 'DoesNotExist' + - key: '.status.phase' + operator: 'In' + values: + - 'Pending' + - key: '.status.conditions.[] | select( .type == "Initialized" ) | .status' + operator: 'NotIn' + values: + - 'True' + - key: '.status.initContainerStatuses.[].state.running.startedAt' + operator: 'Exists' + weight: 1 + delay: + durationMilliseconds: 1000 + jitterDurationMilliseconds: 5000 + next: + statusTemplate: | + {{ $now := Now }} + {{ $root := . }} + conditions: + - lastProbeTime: null + lastTransitionTime: '{{ $now }}' + status: "True" + type: Initialized + initContainerStatuses: + {{ range $index, $item := .spec.initContainers }} + {{ $origin := index $root.status.initContainerStatuses $index }} + - image: {{ $item.image }} + name: {{ $item.name }} + ready: true + restartCount: 0 + started: false + state: + terminated: + exitCode: 0 + finishedAt: '{{ $now }}' + reason: Completed + startedAt: '{{ $now }}' + {{ end }} + containerStatuses: + {{ range .spec.containers }} + - image: {{ .image }} + name: {{ .name }} + ready: false + restartCount: 0 + started: false + state: + waiting: + reason: ContainerCreating + {{ end }} + --- + kind: Stage + apiVersion: kwok.x-k8s.io/v1alpha1 + metadata: + name: pod-ready + spec: + resourceRef: + apiGroup: v1 + kind: Pod + selector: + matchExpressions: + - key: '.metadata.deletionTimestamp' + operator: 'DoesNotExist' + - key: '.status.phase' + operator: 'In' + values: + - 'Pending' + - key: '.status.conditions.[] | select( .type == "Initialized" ) | .status' + operator: 'In' + values: + - 'True' + - key: '.status.conditions.[] | select( .type == "ContainersReady" ) | .status' + operator: 'NotIn' + values: + - 'True' + weight: 1 + delay: + durationMilliseconds: 1000 + jitterDurationMilliseconds: 5000 + next: + delete: false + statusTemplate: | + {{ $now := Now }} + {{ $root := . }} + conditions: + - lastProbeTime: null + lastTransitionTime: '{{ $now }}' + message: '' + reason: '' + status: "True" + type: Ready + - lastProbeTime: null + lastTransitionTime: '{{ $now }}' + message: '' + reason: '' + status: "True" + type: ContainersReady + containerStatuses: + {{ range $index, $item := .spec.containers }} + {{ $origin := index $root.status.containerStatuses $index }} + - image: {{ $item.image }} + name: {{ $item.name }} + ready: true + restartCount: 0 + started: true + state: + running: + startedAt: '{{ $now }}' + {{ end }} + phase: Running + --- + kind: Stage + apiVersion: kwok.x-k8s.io/v1alpha1 + metadata: + name: pod-complete + spec: + resourceRef: + apiGroup: v1 + kind: Pod + selector: + matchExpressions: + - key: '.metadata.deletionTimestamp' + operator: 'DoesNotExist' + - key: '.status.phase' + operator: 'In' + values: + - 'Running' + - key: '.status.conditions.[] | select( .type == "Ready" ) | .status' + operator: 'In' + values: + - 'True' + weight: 1 + delay: + durationMilliseconds: 1000 + jitterDurationMilliseconds: 5000 + next: + delete: false + statusTemplate: | + {{ $now := Now }} + {{ $root := . }} + containerStatuses: + {{ range $index, $item := .spec.containers }} + {{ $origin := index $root.status.containerStatuses $index }} + - image: {{ $item.image }} + name: {{ $item.name }} + ready: true + restartCount: 0 + started: false + state: + terminated: + exitCode: 0 + finishedAt: '{{ $now }}' + reason: Completed + startedAt: '{{ $now }}' + {{ end }} + phase: Succeeded + --- + kind: Stage + apiVersion: kwok.x-k8s.io/v1alpha1 + metadata: + name: pod-remove-finalizer + spec: + resourceRef: + apiGroup: v1 + kind: Pod + selector: + matchExpressions: + - key: '.metadata.deletionTimestamp' + operator: 'Exists' + - key: '.metadata.finalizers.[]' + operator: 'In' + values: + - 'kwok.x-k8s.io/fake' + weight: 1 + delay: + durationMilliseconds: 1000 + jitterDurationMilliseconds: 5000 + next: + finalizers: + remove: + - value: 'kwok.x-k8s.io/fake' + event: + type: Normal + reason: Killing + message: Stopping container + --- + kind: Stage + apiVersion: kwok.x-k8s.io/v1alpha1 + metadata: + name: pod-delete + spec: + resourceRef: + apiGroup: v1 + kind: Pod + selector: + matchExpressions: + - key: '.metadata.deletionTimestamp' + operator: 'Exists' + - key: '.metadata.finalizers' + operator: 'DoesNotExist' + weight: 1 + delay: + durationMilliseconds: 1000 + jitterDurationFrom: + expressionFrom: '.metadata.deletionTimestamp' + next: + delete: true`}} diff --git a/manifests/virtualcluster/nodes/templates/nodes.tpl b/manifests/virtualcluster/nodes/templates/nodes.tpl new file mode 100644 index 0000000..c8e290d --- /dev/null +++ b/manifests/virtualcluster/nodes/templates/nodes.tpl @@ -0,0 +1,50 @@ +{{- $name := .Values.name }} +{{- $cpu := .Values.cpu }} +{{- $memory := .Values.memory }} +{{- $labels := .Values.nodeLabels }} +{{- range $index := (untilStep 0 (int .Values.replicas) 1) }} +apiVersion: v1 +kind: Node +metadata: + annotations: + node.alpha.kubernetes.io/ttl: "0" + kwok.x-k8s.io/node: fake + kwok.x-k8s.io/manage: {{ $name }}-{{ $index }} + labels: + beta.kubernetes.io/arch: amd64 + beta.kubernetes.io/os: linux + kubernetes.io/arch: amd64 + kubernetes.io/hostname: {{ $name }}-{{ $index }} + kubernetes.io/os: linux + kubernetes.io/role: agent + node-role.kubernetes.io/agent: "" + node.kubernetes.io/exclude-from-external-load-balancers: "true" + kubernetes.azure.com/managed: "false" + type: kperf-virtualnodes + alpha.kperf.io/nodepool: {{ $name }} +{{- range $key, $value := $labels }} + {{ $key }}: {{ $value }} +{{- end }} + name: {{ $name }}-{{ $index }} +spec: + taints: # Avoid scheduling actual running pods to fake Node + - effect: NoSchedule + key: kperf.io/nodepool + value: fake +status: + allocatable: + cpu: {{ $cpu }} + memory: {{ $memory }}Gi + pods: 110 + capacity: + cpu: {{ $cpu }} + memory: {{ $memory }}Gi + pods: 110 + nodeInfo: + architecture: amd64 + containerRuntimeVersion: "kwok" + kubeProxyVersion: fake + kubeletVersion: fake + operatingSystem: linux +--- +{{- end}} diff --git a/manifests/virtualcluster/nodes/templates/serviceaccount.yaml b/manifests/virtualcluster/nodes/templates/serviceaccount.yaml new file mode 100644 index 0000000..5c20ddc --- /dev/null +++ b/manifests/virtualcluster/nodes/templates/serviceaccount.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.name }} + namespace: {{ .Release.Namespace }} diff --git a/manifests/virtualcluster/nodes/templates/statefulsets.tpl b/manifests/virtualcluster/nodes/templates/statefulsets.tpl new file mode 100644 index 0000000..882e80f --- /dev/null +++ b/manifests/virtualcluster/nodes/templates/statefulsets.tpl @@ -0,0 +1,70 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ .Values.name }} + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + app: {{ .Values.name }} + replicas: {{ .Values.replicas }} + podManagementPolicy: Parallel + template: + metadata: + labels: + app: {{ .Values.name }} + spec: +{{- if .Values.controllerNodeSelectors }} + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + {{- range $key, $values := .Values.controllerNodeSelectors }} + - key: "{{ $key }}" + operator: In + values: + {{- range $values }} + - {{ . }} + {{- end }} + {{- end }} +{{- end }} + terminationGracePeriodSeconds: 1 + containers: + - args: + - --config=/data/kwok-config.yaml + - --manage-all-nodes=false + - --manage-single-node=$(POD_NAME) # act as virtualnode + - --disregard-status-with-annotation-selector=kwok.x-k8s.io/status=custom + - --disregard-status-with-label-selector= + - --node-ip=$(POD_IP) + - --node-port=10247 + - --cidr=10.0.0.1/24 + - --node-lease-duration-seconds=40 + env: + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + image: registry.k8s.io/kwok/kwok:v0.4.0 + imagePullPolicy: IfNotPresent + name: kwok-controller + volumeMounts: + - name: kwok-config + mountPath: /data/ + resources: + limits: + cpu: "500m" + requests: + cpu: "200m" + restartPolicy: Always + serviceAccount: {{ .Values.name }} + serviceAccountName: {{ .Values.name }} + volumes: + - name: kwok-config + configMap: + name: {{ .Values.name }} diff --git a/manifests/virtualcluster/nodes/values.yaml b/manifests/virtualcluster/nodes/values.yaml new file mode 100644 index 0000000..fa21288 --- /dev/null +++ b/manifests/virtualcluster/nodes/values.yaml @@ -0,0 +1,6 @@ +name: "" +controllerNodeSelectors: {} +replicas: 0 +nodeLabels: {} +cpu: 0 +memory: 0 From 3e22d941768ad0374ea2c671245d16284635d307 Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Wed, 3 Jan 2024 08:22:05 +0000 Subject: [PATCH 17/22] helmcli: init helm package with release client Signed-off-by: Wei Fu --- go.mod | 96 ++++++++++++ go.sum | 384 +++++++++++++++++++++++++++++++++++++++++++++ helmcli/release.go | 182 +++++++++++++++++++++ 3 files changed, 662 insertions(+) create mode 100644 helmcli/release.go diff --git a/go.mod b/go.mod index 20c423c..004e0f9 100644 --- a/go.mod +++ b/go.mod @@ -9,39 +9,135 @@ require ( gopkg.in/yaml.v2 v2.4.0 helm.sh/helm/v3 v3.13.3 k8s.io/apimachinery v0.28.4 + k8s.io/cli-runtime v0.28.4 k8s.io/client-go v0.28.4 k8s.io/kubectl v0.28.4 ) require ( + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Masterminds/squirrel v1.5.4 // indirect + github.com/Microsoft/hcsshim v0.11.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/containerd/containerd v1.7.6 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/docker/cli v24.0.6+incompatible // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-metrics v0.0.1 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/emicklei/go-restful/v3 v3.10.1 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gosuri/uitable v0.0.4 // indirect + github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.13 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmoiron/sqlx v1.3.5 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.16.0 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect + 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.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/rubenv/sql-migrate v1.5.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xlab/treeprint v1.2.0 // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect + go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect + golang.org/x/crypto v0.14.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect + google.golang.org/grpc v1.56.3 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.28.4 // indirect + k8s.io/apiextensions-apiserver v0.28.4 // indirect + k8s.io/apiserver v0.28.4 // indirect + k8s.io/component-base v0.28.4 // indirect k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + oras.land/oras-go v1.2.4 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect + sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 21a98f2..3e6de0d 100644 --- a/go.sum +++ b/go.sum @@ -1,141 +1,525 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= +github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/hcsshim v0.11.0 h1:7EFNIY4igHEXUdj1zXgAyU3fLc7QfOKHbkldRVTBdiM= +github.com/Microsoft/hcsshim v0.11.0/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +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/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= +github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= +github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= +github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= +github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= +github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= +github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= +github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= +github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= +github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= +github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +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/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +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/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rubenv/sql-migrate v1.5.2 h1:bMDqOnrJVV/6JQgQ/MxOpU+AdO8uzYYA/TxFUBzFtS0= +github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +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-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= +google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= helm.sh/helm/v3 v3.13.3 h1:0zPEdGqHcubehJHP9emCtzRmu8oYsJFRrlVF3TFj8xY= helm.sh/helm/v3 v3.13.3/go.mod h1:3OKO33yI3p4YEXtTITN2+4oScsHeQe71KuzhlZ+aPfg= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= +k8s.io/apiextensions-apiserver v0.28.4 h1:AZpKY/7wQ8n+ZYDtNHbAJBb+N4AXXJvyZx6ww6yAJvU= +k8s.io/apiextensions-apiserver v0.28.4/go.mod h1:pgQIZ1U8eJSMQcENew/0ShUTlePcSGFq6dxSxf2mwPM= k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= +k8s.io/apiserver v0.28.4 h1:BJXlaQbAU/RXYX2lRz+E1oPe3G3TKlozMMCZWu5GMgg= +k8s.io/apiserver v0.28.4/go.mod h1:Idq71oXugKZoVGUUL2wgBCTHbUR+FYTWa4rq9j4n23w= +k8s.io/cli-runtime v0.28.4 h1:IW3aqSNFXiGDllJF4KVYM90YX4cXPGxuCxCVqCD8X+Q= +k8s.io/cli-runtime v0.28.4/go.mod h1:MLGRB7LWTIYyYR3d/DOgtUC8ihsAPA3P8K8FDNIqJ0k= k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= +k8s.io/component-base v0.28.4 h1:c/iQLWPdUgI90O+T9TeECg8o7N3YJTiuz2sKxILYcYo= +k8s.io/component-base v0.28.4/go.mod h1:m9hR0uvqXDybiGL2nf/3Lf0MerAfQXzkfWhUY58JUbU= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= k8s.io/kubectl v0.28.4 h1:gWpUXW/T7aFne+rchYeHkyB8eVDl5UZce8G4X//kjUQ= k8s.io/kubectl v0.28.4/go.mod h1:CKOccVx3l+3MmDbkXtIUtibq93nN2hkDR99XDCn7c/c= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +oras.land/oras-go v1.2.4 h1:djpBY2/2Cs1PV87GSJlxv4voajVOMZxqqtq9AB8YNvY= +oras.land/oras-go v1.2.4/go.mod h1:DYcGfb3YF1nKjcezfX2SNlDAeQFKSXmf+qrFmrh4324= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/helmcli/release.go b/helmcli/release.go new file mode 100644 index 0000000..78b5f5a --- /dev/null +++ b/helmcli/release.go @@ -0,0 +1,182 @@ +package helmcli + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/storage/driver" + "helm.sh/helm/v3/pkg/strvals" + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +var noopLog action.DebugLog = func(_ string, _ ...interface{}) {} + +// ValuesApplier is to apply new key/values to existing chart's values. +type ValuesApplier func(values map[string]interface{}) error + +// StringPathValuesApplier applies key/values by string path. +// +// For instance, x.y.z=1 is the same to that YAML value: +// +// ```yaml +// +// x: +// y: +// z: 1 +// +// ``` +func StringPathValuesApplier(values ...string) ValuesApplier { + return func(to map[string]interface{}) error { + for _, v := range values { + if err := strvals.ParseInto(v, to); err != nil { + return fmt.Errorf("failed to parse (%s) into values: %w", v, err) + } + } + return nil + } +} + +// ReleaseCli is a client to deploy helm chart with secret storage. +type ReleaseCli struct { + namespace string + name string + + cfg *action.Configuration + ch *chart.Chart + values map[string]interface{} + labels map[string]string +} + +// NewReleaseCli returns new ReleaseCli instance. +// +// TODO: +// 1. show debug log. +// 2. add flag to disable Wait +func NewReleaseCli( + kubeconfigPath string, + namespace string, + name string, + ch *chart.Chart, + labels map[string]string, + valuesAppliers ...ValuesApplier, +) (*ReleaseCli, error) { + // build default values + values, err := copyValues(ch.Values) + if err != nil { + return nil, err + } + + for _, applier := range valuesAppliers { + if err := applier(values); err != nil { + return nil, fmt.Errorf("failed to apply: %w", err) + } + } + + actionCfg := new(action.Configuration) + if err := actionCfg.Init( + &genericclioptions.ConfigFlags{ + KubeConfig: &kubeconfigPath, + }, + namespace, + "secret", + noopLog, + ); err != nil { + return nil, fmt.Errorf("failed to init action config: %w", err) + } + + return &ReleaseCli{ + namespace: namespace, + name: name, + cfg: actionCfg, + ch: ch, + values: values, + labels: labels, + }, nil +} + +// Deploy will install or upgrade that release. +func (cli *ReleaseCli) Deploy(ctx context.Context, timeout time.Duration, valuesAppliers ...ValuesApplier) error { + values, err := cli.initValues(valuesAppliers...) + if err != nil { + return err + } + + // NOTE: Maintain only one history record just in case that there are + // too many secret records which causes ETCD OutOfSpace. + histCli := action.NewHistory(cli.cfg) + histCli.Max = 1 + if _, err = histCli.Run(cli.name); err == driver.ErrReleaseNotFound { + installCli := action.NewInstall(cli.cfg) + installCli.CreateNamespace = true + installCli.Atomic = true + installCli.Namespace = cli.namespace + installCli.ReleaseName = cli.name + installCli.IsUpgrade = true + installCli.Timeout = timeout + installCli.Labels = cli.labels + installCli.Wait = true + + release, err := installCli.RunWithContext(ctx, cli.ch, values) + if err != nil { + return fmt.Errorf("failed to install that release %s: %w", cli.name, err) + } + cli.values = release.Config + return nil + } + + upgradeCli := action.NewUpgrade(cli.cfg) + upgradeCli.Namespace = cli.namespace + upgradeCli.Atomic = true + upgradeCli.Timeout = timeout + upgradeCli.MaxHistory = 1 + upgradeCli.Wait = true + upgradeCli.Labels = cli.labels + + release, err := upgradeCli.RunWithContext(ctx, cli.name, cli.ch, values) + if err != nil { + return fmt.Errorf("failed to upgrade that release %s: %w", cli.name, err) + } + + cli.values = release.Config + return nil +} + +// Uninstall deletes that release. +func (cli *ReleaseCli) Uninstall() error { + uninstallCli := action.NewUninstall(cli.cfg) + _, err := uninstallCli.Run(cli.name) + return err +} + +// initValues is to apply valuesAppliers into copied values. Just in case that +// we can rollback if valuesApplier returns error. +func (cli *ReleaseCli) initValues(valuesAppliers ...ValuesApplier) (map[string]interface{}, error) { + values, err := copyValues(cli.values) + if err != nil { + return nil, fmt.Errorf("failed to copy values: %w", err) + } + + for _, applier := range valuesAppliers { + if err := applier(values); err != nil { + return nil, fmt.Errorf("failed to apply: %w", err) + } + } + return values, nil +} + +func copyValues(src map[string]interface{}) (map[string]interface{}, error) { + data, err := json.Marshal(src) + if err != nil { + return nil, fmt.Errorf("failed to json.Marshal original values: %w", err) + } + + newValues := make(map[string]interface{}) + if err := json.Unmarshal(data, &newValues); err != nil { + return nil, fmt.Errorf("failed to use json.Unmarshal to copy values: %w", err) + } + return newValues, nil +} From 3fa3436c69f2d28d304609c5613875561a11abf9 Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Wed, 3 Jan 2024 08:41:15 +0000 Subject: [PATCH 18/22] helmcli: add GetCli client Signed-off-by: Wei Fu --- helmcli/get.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 helmcli/get.go diff --git a/helmcli/get.go b/helmcli/get.go new file mode 100644 index 0000000..0d63f51 --- /dev/null +++ b/helmcli/get.go @@ -0,0 +1,41 @@ +package helmcli + +import ( + "fmt" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/release" + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +// GetCli is a client to get helm chart from secret storage. +type GetCli struct { + namespace string + + cfg *action.Configuration +} + +// NewGetCli returns new GetCli instance. +func NewGetCli(kubeconfigPath string, namespace string) (*GetCli, error) { + actionCfg := new(action.Configuration) + if err := actionCfg.Init( + &genericclioptions.ConfigFlags{ + KubeConfig: &kubeconfigPath, + }, + namespace, + "secret", + noopLog, + ); err != nil { + return nil, fmt.Errorf("failed to init action config: %w", err) + } + return &GetCli{ + namespace: namespace, + cfg: actionCfg, + }, nil +} + +// Get returns all the information about that given release. +func (cli *GetCli) Get(releaseName string) (*release.Release, error) { + getCli := action.NewGet(cli.cfg) + return getCli.Run(releaseName) +} From ef4908e27ef8a19caa6966297dcb0c96c572e865 Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Wed, 3 Jan 2024 09:28:31 +0000 Subject: [PATCH 19/22] *: init kperf-vc-nodepool add subcommand The users can use that following command to create a new virtual node pool. ```bash $ bin/kperf vc nodepool add vc-kperf-1 $ bin/kperf vc nodepool add vc-kperf-2 --nodes 1 --cpu 128 --memory 180 $ kubectl get nodes | grep vc-kperf- vc-kperf-1-0 Ready agent 4m40s fake vc-kperf-1-1 Ready agent 4m40s fake vc-kperf-1-2 Ready agent 4m40s fake vc-kperf-1-3 Ready agent 4m40s fake vc-kperf-1-4 Ready agent 4m40s fake vc-kperf-1-5 Ready agent 4m40s fake vc-kperf-1-6 Ready agent 4m40s fake vc-kperf-1-7 Ready agent 4m40s fake vc-kperf-1-8 Ready agent 4m40s fake vc-kperf-1-9 Ready agent 4m40s fake vc-kperf-2-0 Ready agent 3m20s fake ``` That nodepool is managed by helm charts. The users can also use `helm` command to check. ```bash $ helm -n virtualnodes-kperf-io get values vc-kperf-1 USER-SUPPLIED VALUES: controllerNodeSelectors: {} cpu: 8 memory: 16 name: vc-kperf-1 nodeLabels: {} replicas: 10 $ helm -n virtualnodes-kperf-io get values vc-kperf-2 USER-SUPPLIED VALUES: controllerNodeSelectors: {} cpu: 128 memory: 180 name: vc-kperf-2 nodeLabels: {} replicas: 1 ``` Since we don't have kperf-vc-nodepool del, we should use `helm` to cleanup nodepools. ```bash $ helm -n virtualnodes-kperf-io uninstall vc-kperf-1 $ helm -n virtualnodes-kperf-io uninstall vc-kperf-2 ``` Signed-off-by: Wei Fu --- cmd/kperf/commands/virtualcluster/nodepool.go | 22 +++- virtualcluster/nodes_common.go | 109 ++++++++++++++++++ virtualcluster/nodes_create.go | 54 +++++++++ 3 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 virtualcluster/nodes_common.go create mode 100644 virtualcluster/nodes_create.go diff --git a/cmd/kperf/commands/virtualcluster/nodepool.go b/cmd/kperf/commands/virtualcluster/nodepool.go index 2c64a90..13beefe 100644 --- a/cmd/kperf/commands/virtualcluster/nodepool.go +++ b/cmd/kperf/commands/virtualcluster/nodepool.go @@ -1,7 +1,11 @@ package virtualcluster import ( + "context" "fmt" + "strings" + + "github.com/Azure/kperf/virtualcluster" "github.com/urfave/cli" ) @@ -44,7 +48,23 @@ var nodepoolAddCommand = cli.Command{ }, }, Action: func(cliCtx *cli.Context) error { - return fmt.Errorf("nodepool add - not implemented") + if cliCtx.NArg() != 1 { + return fmt.Errorf("required only one argument as nodepool name") + } + nodepoolName := strings.TrimSpace(cliCtx.Args().Get(0)) + if len(nodepoolName) == 0 { + return fmt.Errorf("required non-empty nodepool name") + } + + kubeCfgPath := cliCtx.String("kubeconfig") + + return virtualcluster.CreateNodepool(context.Background(), + kubeCfgPath, + nodepoolName, + virtualcluster.WithNodepoolCPUOpt(cliCtx.Int("cpu")), + virtualcluster.WithNodepoolMemoryOpt(cliCtx.Int("memory")), + virtualcluster.WithNodepoolCountOpt(cliCtx.Int("nodes")), + ) }, } diff --git a/virtualcluster/nodes_common.go b/virtualcluster/nodes_common.go new file mode 100644 index 0000000..63d8a2c --- /dev/null +++ b/virtualcluster/nodes_common.go @@ -0,0 +1,109 @@ +package virtualcluster + +import ( + "fmt" + + "github.com/Azure/kperf/helmcli" +) + +var ( + defaultNodepoolCfg = nodepoolConfig{ + count: 10, + cpu: 8, + memory: 16, // GiB + } + + // virtualnodeReleaseLabels is used to mark that helm chart release + // is managed by kperf. + virtualnodeReleaseLabels = map[string]string{ + "virtualnodes.kperf.io/managed": "true", + } +) + +const ( + // virtualnodeChartName should be aligned with ../manifests/virtualcluster/nodes. + virtualnodeChartName = "virtualcluster/nodes" + + // virtualnodeReleaseNamespace is used to host virtual nodes. + // + // NOTE: The Node resource is cluster-scope. Just in case that new node + // name is conflict with existing one, we should use fixed namespace + // to store all the resources related to virtual nodes. + virtualnodeReleaseNamespace = "virtualnodes-kperf-io" +) + +type nodepoolConfig struct { + // count represents the desired number of node. + count int + // cpu represents a logical CPU resource provided by virtual node. + cpu int + // memory represents a logical memory resource provided by virtual node. + // The unit is GiB. + memory int + // labels is to be applied to each virtual node. + labels []string + // nodeSelectors forces virtual node's controller to nodes with that specific labels. + nodeSelectors map[string][]string +} + +func (cfg *nodepoolConfig) validate() error { + if cfg.count <= 0 || cfg.cpu <= 0 || cfg.memory <= 0 { + return fmt.Errorf("invalid count=%d or cpu=%d or memory=%d", + cfg.count, cfg.cpu, cfg.memory) + } + return nil +} + +// NodepoolOpt is used to update default node pool's setting. +type NodepoolOpt func(*nodepoolConfig) + +// WithNodepoolCountOpt updates node count. +func WithNodepoolCountOpt(count int) NodepoolOpt { + return func(cfg *nodepoolConfig) { + cfg.count = count + } +} + +// WithNodepoolCPUOpt updates CPU resource. +func WithNodepoolCPUOpt(cpu int) NodepoolOpt { + return func(cfg *nodepoolConfig) { + cfg.cpu = cpu + } +} + +// WithNodepoolMemoryOpt updates Memory resource. +func WithNodepoolMemoryOpt(memory int) NodepoolOpt { + return func(cfg *nodepoolConfig) { + cfg.memory = memory + } +} + +// WithNodepoolLabelsOpt updates node's labels. +func WithNodepoolLabelsOpt(labels []string) NodepoolOpt { + return func(cfg *nodepoolConfig) { + cfg.labels = labels + } +} + +// WithNodepoolNodeControllerAffinity forces virtual node's controller to +// nodes with that specific labels. +func WithNodepoolNodeControllerAffinity(nodeSelectors map[string][]string) NodepoolOpt { + return func(cfg *nodepoolConfig) { + cfg.nodeSelectors = nodeSelectors + } +} + +// toHelmValuesAppliers creates ValuesAppliers. +// +// NOTE: Please align with ../manifests/virtualcluster/nodes/values.yaml +// +// TODO: Add YAML ValuesAppliers to support array type. +func (cfg *nodepoolConfig) toHelmValuesAppliers(nodepoolName string) []helmcli.ValuesApplier { + res := make([]string, 0, 4) + + res = append(res, fmt.Sprintf("name=%s", nodepoolName)) + res = append(res, fmt.Sprintf("replicas=%d", cfg.count)) + res = append(res, fmt.Sprintf("cpu=%d", cfg.cpu)) + res = append(res, fmt.Sprintf("memory=%d", cfg.memory)) + return []helmcli.ValuesApplier{helmcli.StringPathValuesApplier(res...)} +} diff --git a/virtualcluster/nodes_create.go b/virtualcluster/nodes_create.go new file mode 100644 index 0000000..57e0e1b --- /dev/null +++ b/virtualcluster/nodes_create.go @@ -0,0 +1,54 @@ +package virtualcluster + +import ( + "context" + "fmt" + "time" + + "github.com/Azure/kperf/helmcli" + "github.com/Azure/kperf/manifests" +) + +// CreateNodepool creates a new node pool. +// +// TODO: +// 1. create a new package to define ErrNotFound, ErrAlreadyExists, ... errors. +// 2. support configurable timeout. +func CreateNodepool(ctx context.Context, kubeconfigPath string, nodepoolName string, opts ...NodepoolOpt) error { + cfg := defaultNodepoolCfg + for _, opt := range opts { + opt(&cfg) + } + + if err := cfg.validate(); err != nil { + return err + } + + getCli, err := helmcli.NewGetCli(kubeconfigPath, virtualnodeReleaseNamespace) + if err != nil { + return fmt.Errorf("failed to create helm get client: %w", err) + } + + _, err = getCli.Get(nodepoolName) + if err == nil { + return fmt.Errorf("nodepool %s already exists", nodepoolName) + } + + ch, err := manifests.LoadChart(virtualnodeChartName) + if err != nil { + return fmt.Errorf("failed to load virtual node chart: %w", err) + } + + releaseCli, err := helmcli.NewReleaseCli( + kubeconfigPath, + virtualnodeReleaseNamespace, + nodepoolName, + ch, + virtualnodeReleaseLabels, + cfg.toHelmValuesAppliers(nodepoolName)..., + ) + if err != nil { + return fmt.Errorf("failed to create helm release client: %w", err) + } + return releaseCli.Deploy(ctx, 120*time.Second) +} From 668d307e9149b0d81014be62dc75f43353010942 Mon Sep 17 00:00:00 2001 From: manasachinta Date: Wed, 3 Jan 2024 09:02:54 -0500 Subject: [PATCH 20/22] removing unused mutex variable in schedule.go --- request/schedule.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/request/schedule.go b/request/schedule.go index 64b7553..7a2a83d 100644 --- a/request/schedule.go +++ b/request/schedule.go @@ -16,8 +16,6 @@ import ( const defaultTimeout = 60 * time.Second -var m sync.Mutex - // Schedule files requests to apiserver based on LoadProfileSpec. func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.Interface) (*types.ResponseStats, error) { ctx, cancel := context.WithCancel(ctx) From f58a57f2f674b82e8c266fe60e2da4204828e55f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 14:30:35 +0000 Subject: [PATCH 21/22] build(deps): bump github.com/containerd/containerd from 1.7.6 to 1.7.11 Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.7.6 to 1.7.11. - [Release notes](https://github.com/containerd/containerd/releases) - [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md) - [Commits](https://github.com/containerd/containerd/compare/v1.7.6...v1.7.11) --- updated-dependencies: - dependency-name: github.com/containerd/containerd dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 18 +++++++++++------- go.sum | 39 +++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/go.mod b/go.mod index 004e0f9..fe0ceec 100644 --- a/go.mod +++ b/go.mod @@ -23,12 +23,13 @@ require ( github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect - github.com/Microsoft/hcsshim v0.11.0 // indirect + github.com/Microsoft/hcsshim v0.11.4 // indirect github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/containerd/containerd v1.7.6 // indirect + github.com/containerd/containerd v1.7.11 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -43,6 +44,7 @@ require ( github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/color v1.13.0 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logr/logr v1.2.4 // indirect @@ -111,19 +113,21 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.2.0 // indirect - go.opentelemetry.io/otel v1.14.0 // indirect - go.opentelemetry.io/otel/trace v1.14.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect + go.opentelemetry.io/otel v1.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/net v0.17.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect - google.golang.org/grpc v1.56.3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect + google.golang.org/grpc v1.58.3 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 3e6de0d..4c3e8f2 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/hcsshim v0.11.0 h1:7EFNIY4igHEXUdj1zXgAyU3fLc7QfOKHbkldRVTBdiM= -github.com/Microsoft/hcsshim v0.11.0/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= +github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= +github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -45,9 +45,11 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= -github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= -github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= +github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw= +github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -85,6 +87,7 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZM github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -344,10 +347,14 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTN github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= -go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= -go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= -go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -365,7 +372,7 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -383,8 +390,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -438,7 +445,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= +golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -450,13 +457,13 @@ google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +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/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From 40132ba3eae45eb85f310e922302295e1cacd84e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 15:45:44 +0000 Subject: [PATCH 22/22] build(deps): bump golang.org/x/crypto from 0.14.0 to 0.17.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.17.0. - [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index fe0ceec..ee4cb79 100644 --- a/go.mod +++ b/go.mod @@ -118,13 +118,13 @@ require ( go.opentelemetry.io/otel/metric v1.19.0 // indirect go.opentelemetry.io/otel/trace v1.19.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect - golang.org/x/crypto v0.14.0 // indirect + golang.org/x/crypto v0.17.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/grpc v1.58.3 // indirect diff --git a/go.sum b/go.sum index 4c3e8f2..fdd7f8e 100644 --- a/go.sum +++ b/go.sum @@ -363,8 +363,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -419,21 +419,21 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=